diff --git a/packages/runtime/src/extensibility/facades/consumption/SettingsFacade.ts b/packages/runtime/src/extensibility/facades/consumption/SettingsFacade.ts index 95208151a..cae195c2b 100644 --- a/packages/runtime/src/extensibility/facades/consumption/SettingsFacade.ts +++ b/packages/runtime/src/extensibility/facades/consumption/SettingsFacade.ts @@ -13,13 +13,16 @@ import { GetSettingsRequest, GetSettingsUseCase, UpdateSettingRequest, - UpdateSettingUseCase + UpdateSettingUseCase, + UpsertSettingByKeyRequest, + UpsertSettingByKeyUseCase } from "../../../useCases"; export class SettingsFacade { public constructor( @Inject private readonly createSettingUseCase: CreateSettingUseCase, @Inject private readonly updateSettingUseCase: UpdateSettingUseCase, + @Inject private readonly upsertSettingByKeyUseCase: UpsertSettingByKeyUseCase, @Inject private readonly deleteSettingUseCase: DeleteSettingUseCase, @Inject private readonly getSettingsUseCase: GetSettingsUseCase, @Inject private readonly getSettingUseCase: GetSettingUseCase, @@ -49,4 +52,8 @@ export class SettingsFacade { public async updateSetting(request: UpdateSettingRequest): Promise> { return await this.updateSettingUseCase.execute(request); } + + public async upsertSettingByKey(request: UpsertSettingByKeyRequest): Promise> { + return await this.upsertSettingByKeyUseCase.execute(request); + } } diff --git a/packages/runtime/src/useCases/common/Schemas.ts b/packages/runtime/src/useCases/common/Schemas.ts index 10903345c..8068bc662 100644 --- a/packages/runtime/src/useCases/common/Schemas.ts +++ b/packages/runtime/src/useCases/common/Schemas.ts @@ -20357,6 +20357,27 @@ export const UpdateSettingRequest: any = { } } +export const UpsertSettingByKeyRequest: any = { + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/UpsertSettingByKeyRequest", + "definitions": { + "UpsertSettingByKeyRequest": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": {} + }, + "required": [ + "key", + "value" + ], + "additionalProperties": false + } + } +} + export const DownloadFileRequest: any = { "$schema": "http://json-schema.org/draft-07/schema#", "$ref": "#/definitions/DownloadFileRequest", diff --git a/packages/runtime/src/useCases/consumption/settings/UpsertSettingByKey.ts b/packages/runtime/src/useCases/consumption/settings/UpsertSettingByKey.ts new file mode 100644 index 000000000..839b8c619 --- /dev/null +++ b/packages/runtime/src/useCases/consumption/settings/UpsertSettingByKey.ts @@ -0,0 +1,49 @@ +import { Serializable } from "@js-soft/ts-serval"; +import { Result } from "@js-soft/ts-utils"; +import { SettingsController } from "@nmshd/consumption"; +import { AccountController } from "@nmshd/transport"; +import { Inject } from "typescript-ioc"; +import { SettingDTO } from "../../../types"; +import { SchemaRepository, SchemaValidator, UseCase } from "../../common"; +import { SettingMapper } from "./SettingMapper"; + +export interface UpsertSettingByKeyRequest { + key: string; + value: any; +} + +class Validator extends SchemaValidator { + public constructor(@Inject schemaRepository: SchemaRepository) { + super(schemaRepository.getSchema("UpsertSettingByKeyRequest")); + } +} + +export class UpsertSettingByKeyUseCase extends UseCase { + public constructor( + @Inject private readonly settingController: SettingsController, + @Inject private readonly accountController: AccountController, + @Inject validator: Validator + ) { + super(validator); + } + + protected async executeInternal(request: UpsertSettingByKeyRequest): Promise> { + const settings = await this.settingController.getSettings({ key: request.key }); + + const newValue = Serializable.fromUnknown(request.value); + + if (settings.length === 0) { + const setting = await this.settingController.createSetting({ key: request.key, value: newValue }); + await this.accountController.syncDatawallet(); + return Result.ok(SettingMapper.toSettingDTO(setting)); + } + + const latestSetting = settings.reduce((prev, current) => (prev.createdAt > current.createdAt ? prev : current)); + + latestSetting.value = newValue; + await this.settingController.updateSetting(latestSetting); + await this.accountController.syncDatawallet(); + + return Result.ok(SettingMapper.toSettingDTO(latestSetting)); + } +} diff --git a/packages/runtime/src/useCases/consumption/settings/index.ts b/packages/runtime/src/useCases/consumption/settings/index.ts index 89932f968..bd1d3786e 100644 --- a/packages/runtime/src/useCases/consumption/settings/index.ts +++ b/packages/runtime/src/useCases/consumption/settings/index.ts @@ -5,3 +5,4 @@ export * from "./GetSettingByKey"; export * from "./GetSettings"; export * from "./SettingMapper"; export * from "./UpdateSetting"; +export * from "./UpsertSettingByKey"; diff --git a/packages/runtime/test/consumption/settings.test.ts b/packages/runtime/test/consumption/settings.test.ts index 921e00e0e..2b719825a 100644 --- a/packages/runtime/test/consumption/settings.test.ts +++ b/packages/runtime/test/consumption/settings.test.ts @@ -11,34 +11,39 @@ beforeAll(async () => { const runtimeServices = await runtimeServiceProvider.launch(1); consumptionServices = runtimeServices[0].consumption; }, 30000); + afterAll(async () => await runtimeServiceProvider.stop()); -describe("Settings", () => { - const value = { aKey: "a-value" }; - let settingId: string; +afterEach(async () => { + const settings = await consumptionServices.settings.getSettings({}); + for (const setting of settings.value) { + await consumptionServices.settings.deleteSetting({ id: setting.id }); + } +}); +describe("Settings", () => { test("should create a setting", async () => { - const result = await consumptionServices.settings.createSetting({ - key: "a-key", - value: value - }); + const value = { aKey: "a-value" }; + const result = await consumptionServices.settings.createSetting({ key: "a-key", value: value }); expect(result).toBeSuccessful(); - - const setting = result.value; - settingId = setting.id; }); test("should get the setting", async () => { - const result = await consumptionServices.settings.getSetting({ id: settingId }); + const value = { aKey: "a-value" }; + const createSettingResult = await consumptionServices.settings.createSetting({ key: "a-key", value: value }); + + const result = await consumptionServices.settings.getSetting({ id: createSettingResult.value.id }); expect(result).toBeSuccessful(); const setting = result.value; - settingId = setting.id; expect(setting.value).toStrictEqual(value); }); test("should contain the setting in the list of settings", async () => { + const value = { aKey: "a-value" }; + const settingId = (await consumptionServices.settings.createSetting({ key: "a-key", value: value })).value.id; + const result = await consumptionServices.settings.getSettings({}); expect(result).toBeSuccessful(); @@ -50,6 +55,9 @@ describe("Settings", () => { }); test("should edit the setting", async () => { + const value = { aKey: "a-value" }; + const settingId = (await consumptionServices.settings.createSetting({ key: "a-key", value: value })).value.id; + const newValue = { aKey: "another-Value" }; const updateResult = await consumptionServices.settings.updateSetting({ id: settingId, @@ -65,6 +73,9 @@ describe("Settings", () => { }); test("should delete the setting", async () => { + const value = { aKey: "a-value" }; + const settingId = (await consumptionServices.settings.createSetting({ key: "a-key", value: value })).value.id; + const deleteResult = await consumptionServices.settings.deleteSetting({ id: settingId }); expect(deleteResult).toBeSuccessful(); @@ -76,10 +87,8 @@ describe("Settings", () => { }); test("should get the setting by key", async () => { - const toBeSucceeded = await consumptionServices.settings.createSetting({ - key: "a-key", - value: { key: ["value"] } - }); + const value = { aKey: "a-value" }; + const toBeSucceeded = await consumptionServices.settings.createSetting({ key: "a-key", value }); await consumptionServices.settings.createSetting({ key: "a-key", @@ -94,6 +103,37 @@ describe("Settings", () => { const setting = result.value; expect(setting.value).toStrictEqual({ key: ["newValue"] }); }); + + test("should upsert a setting by key when it does not exist yet", async () => { + await consumptionServices.settings.upsertSettingByKey({ + key: "a-key", + value: { aKey: "a-value" } + }); + + const result = await consumptionServices.settings.getSettings({}); + expect(result).toBeSuccessful(); + expect(result.value).toHaveLength(1); + + const setting = await consumptionServices.settings.getSettingByKey({ key: "a-key" }); + expect(setting.value.value).toStrictEqual({ aKey: "a-value" }); + }); + + test("should upsert a setting by key", async () => { + const value = { aKey: "a-value" }; + await consumptionServices.settings.createSetting({ key: "a-key", value }); + + await consumptionServices.settings.upsertSettingByKey({ + key: "a-key", + value: { aKey: "aNewValue" } + }); + + const result = await consumptionServices.settings.getSettings({}); + expect(result).toBeSuccessful(); + expect(result.value).toHaveLength(1); + + const setting = await consumptionServices.settings.getSettingByKey({ key: "a-key" }); + expect(setting.value.value).toStrictEqual({ aKey: "aNewValue" }); + }); }); describe("Settings query", () => {