Skip to content

Commit

Permalink
Provide backbone-defined valid tags for Attributes (#307)
Browse files Browse the repository at this point in the history
* feat: add usecase for fetching tokens

* chore: fix circular dependencies

* chore: remove unwanted changes

* feat: update backbone to support tags

* chore: remove circular dependency

* chore: add tag mapping and rename interfaces

* chore: use TagListDTO

* chore: pr comments

* chore: pr comments

* Update TagsController.test.ts

* chore: add TagList Serializable

* Aktualisieren von TagsController.ts

* chore: add rest client mocking

* chore: add missing file

* Update packages/transport/src/core/TransportController.ts

Co-authored-by: Julian König <33655937+jkoenig134@users.noreply.github.com>

* chore: add PR comments

* chore: fix tests

* chore: PR comments

* chore: fix tests

* chore: move tags to consumptiong

* chore: fix linting

* chore: rename backbone attrubte tag

* chore: rename backbone attrubte tag

* chore: naming

* chore: improve code

* chore: renaming

* chore: renaming

* chore: add missing file

* chore: PR comments

* chore: remove backbone from client method name

* chore: remove toStrictEqualExcluding

* chore: rename attrbitetag to tag in transport

* chore: remove unuse controller name

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Julian König <33655937+jkoenig134@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 5, 2024
1 parent 745223c commit c285509
Show file tree
Hide file tree
Showing 16 changed files with 2,107 additions and 1,346 deletions.
3,153 changes: 1,816 additions & 1,337 deletions package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from "@nmshd/content";
import { CoreAddress, CoreDate, CoreId, ICoreDate, ICoreId } from "@nmshd/core-types";
import * as iql from "@nmshd/iql";
import { SynchronizedCollection, TransportCoreErrors } from "@nmshd/transport";
import { SynchronizedCollection, TagClient, TransportCoreErrors } from "@nmshd/transport";
import _ from "lodash";
import { nameof } from "ts-simple-nameof";
import { ConsumptionBaseController } from "../../consumption/ConsumptionBaseController";
Expand All @@ -35,6 +35,7 @@ import {
ThirdPartyRelationshipAttributeSucceededEvent
} from "./events";
import { AttributeSuccessorParams, AttributeSuccessorParamsJSON, IAttributeSuccessorParams } from "./local/AttributeSuccessorParams";
import { AttributeTagCollection } from "./local/AttributeTagCollection";
import { CreateRepositoryAttributeParams, ICreateRepositoryAttributeParams } from "./local/CreateRepositoryAttributeParams";
import { CreateSharedLocalAttributeCopyParams, ICreateSharedLocalAttributeCopyParams } from "./local/CreateSharedLocalAttributeCopyParams";
import { ICreateSharedLocalAttributeParams } from "./local/CreateSharedLocalAttributeParams";
Expand All @@ -45,6 +46,7 @@ import { IdentityAttributeQueryTranslator, RelationshipAttributeQueryTranslator,

export class AttributesController extends ConsumptionBaseController {
private attributes: SynchronizedCollection;
private attributeTagClient: TagClient;

public constructor(
parent: ConsumptionController,
Expand All @@ -59,6 +61,7 @@ export class AttributesController extends ConsumptionBaseController {
await super.init();

this.attributes = await this.parent.accountController.getSynchronizedCollection("Attributes");
this.attributeTagClient = new TagClient(this.parent.transport.config, this.parent.accountController.authenticator, this.parent.transport.correlator);

return this;
}
Expand Down Expand Up @@ -1290,4 +1293,9 @@ export class AttributesController extends ConsumptionBaseController {

return ownSharedAttributeSuccessors;
}

public async getAttributeTagCollection(): Promise<AttributeTagCollection> {
const backboneTagCollection = (await this.attributeTagClient.getTagCollection()).value;
return AttributeTagCollection.from(backboneTagCollection);
}
}
1 change: 1 addition & 0 deletions packages/consumption/src/modules/attributes/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./AttributesController";
export * from "./events";
export * from "./local/AttributeSuccessorParams";
export * from "./local/AttributeTagCollection";
export * from "./local/CreateRepositoryAttributeParams";
export * from "./local/CreateSharedLocalAttributeCopyParams";
export * from "./local/CreateSharedLocalAttributeParams";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Serializable, serialize, type, validate } from "@js-soft/ts-serval";

export interface IAttributeTagCollection {
supportedLanguages: string[];
tagsForAttributeValueTypes: Record<string, Record<string, IAttributeTag>>;
}

export interface IAttributeTag {
displayNames: Record<string, string>;
children?: Record<string, IAttributeTag>;
}

@type("AttributeTagCollection")
export class AttributeTagCollection extends Serializable implements IAttributeTagCollection {
@serialize({ type: String })
@validate()
public supportedLanguages: string[];

@serialize()
@validate()
public tagsForAttributeValueTypes: Record<string, Record<string, AttributeTag>>;

public static from(value: IAttributeTagCollection): AttributeTagCollection {
return this.fromAny(value);
}
}

@type("AttributeTag")
export class AttributeTag extends Serializable implements IAttributeTag {
@serialize()
@validate()
public displayNames: Record<string, string>;

@serialize()
@validate({ nullable: true })
public children?: Record<string, AttributeTag>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { IDatabaseConnection } from "@js-soft/docdb-access-abstractions";
import { AccountController, ClientResult, TagClient, Transport } from "@nmshd/transport";
import { spy, when } from "ts-mockito";
import { AttributeTagCollection, ConsumptionController } from "../../../src";
import { TestUtil } from "../../core/TestUtil";

describe("AttributeTagCollection", function () {
let connection: IDatabaseConnection;

let transport: Transport;

let consumptionController: ConsumptionController;
let accountController: AccountController;

let mockedClient: TagClient;

/* eslint-disable @typescript-eslint/naming-convention */
const mockTags = {
supportedLanguages: ["de", "en"],
tagsForAttributeValueTypes: {
PhoneNumber: {
emergency: {
displayNames: {
de: "Notfallkontakt",
en: "Emergency Contact"
},
children: {
first: {
displayNames: {
de: "Erster Notfallkontakt",
en: "First Emergency Contact"
}
},
second: {
displayNames: {
de: "Zweiter Notfallkontakt",
en: "Second Emergency Contact"
}
}
}
},
private: {
displayNames: {
de: "Privat",
en: "Private"
}
}
}
}
};
/* eslint-enable @typescript-eslint/naming-convention */

beforeAll(async function () {
connection = await TestUtil.createConnection();
transport = TestUtil.createTransport(connection);

await transport.init();

({ consumptionController, accountController } = (await TestUtil.provideAccounts(transport, 1))[0]);

const client = consumptionController.attributes["attributeTagClient"];
mockedClient = spy(client);
});

afterAll(async function () {
await accountController.close();

await connection.close();
});

test("should receive the legal tags from the Backbone", async function () {
when(mockedClient.getTagCollection()).thenResolve(ClientResult.ok(mockTags));
const tags = await consumptionController.attributes.getAttributeTagCollection();

expect(tags).toStrictEqual(AttributeTagCollection.from(mockTags));
});
});
12 changes: 6 additions & 6 deletions packages/runtime/src/extensibility/TransportServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ import {

export class TransportServices {
public constructor(
@Inject public readonly account: AccountFacade,
@Inject public readonly challenges: ChallengesFacade,
@Inject public readonly devices: DevicesFacade,
@Inject public readonly files: FilesFacade,
@Inject public readonly identityDeletionProcesses: IdentityDeletionProcessesFacade,
@Inject public readonly messages: MessagesFacade,
@Inject public readonly publicRelationshipTemplateReferences: PublicRelationshipTemplateReferencesFacade,
@Inject public readonly relationships: RelationshipsFacade,
@Inject public readonly relationshipTemplates: RelationshipTemplatesFacade,
@Inject public readonly tokens: TokensFacade,
@Inject public readonly account: AccountFacade,
@Inject public readonly devices: DevicesFacade,
@Inject public readonly challenges: ChallengesFacade,
@Inject public readonly identityDeletionProcesses: IdentityDeletionProcessesFacade,
@Inject public readonly publicRelationshipTemplateReferences: PublicRelationshipTemplateReferencesFacade
@Inject public readonly tokens: TokensFacade
) {}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Result } from "@js-soft/ts-utils";
import { Inject } from "@nmshd/typescript-ioc";
import { LocalAttributeDTO, LocalRequestDTO } from "../../../types";
import { AttributeTagCollectionDTO, LocalAttributeDTO, LocalRequestDTO } from "../../../types";
import {
ChangeDefaultRepositoryAttributeRequest,
ChangeDefaultRepositoryAttributeUseCase,
Expand Down Expand Up @@ -30,6 +30,7 @@ import {
ExecuteThirdPartyRelationshipAttributeQueryRequest,
ExecuteThirdPartyRelationshipAttributeQueryUseCase,
GetAttributeRequest,
GetAttributeTagCollectionUseCase,
GetAttributeUseCase,
GetAttributesRequest,
GetAttributesUseCase,
Expand Down Expand Up @@ -84,7 +85,8 @@ export class AttributesFacade {
@Inject private readonly deletePeerSharedAttributeAndNotifyOwnerUseCase: DeletePeerSharedAttributeAndNotifyOwnerUseCase,
@Inject private readonly deleteThirdPartyRelationshipAttributeAndNotifyPeerUseCase: DeleteThirdPartyRelationshipAttributeAndNotifyPeerUseCase,
@Inject private readonly deleteRepositoryAttributeUseCase: DeleteRepositoryAttributeUseCase,
@Inject private readonly deleteSharedAttributesForRejectedOrRevokedRelationshipUseCase: DeleteSharedAttributesForRejectedOrRevokedRelationshipUseCase
@Inject private readonly deleteSharedAttributesForRejectedOrRevokedRelationshipUseCase: DeleteSharedAttributesForRejectedOrRevokedRelationshipUseCase,
@Inject private readonly getAttributeTagCollectionUseCase: GetAttributeTagCollectionUseCase
) {}

public async createRepositoryAttribute(request: CreateRepositoryAttributeRequest): Promise<Result<LocalAttributeDTO>> {
Expand Down Expand Up @@ -199,4 +201,8 @@ export class AttributesFacade {
public async deleteSharedAttributesForRejectedOrRevokedRelationship(request: DeleteSharedAttributesForRejectedOrRevokedRelationshipRequest): Promise<Result<void>> {
return await this.deleteSharedAttributesForRejectedOrRevokedRelationshipUseCase.execute(request);
}

public async getAttributeTagCollection(): Promise<Result<AttributeTagCollectionDTO>> {
return await this.getAttributeTagCollectionUseCase.execute();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface AttributeTagCollectionDTO {
supportedLanguages: string[];
tagsForAttributeValueTypes: Record<string, Record<string, AttributeTagDTO>>;
}

export interface AttributeTagDTO {
displayNames: Record<string, string>;
children?: Record<string, AttributeTagDTO>;
}
1 change: 1 addition & 0 deletions packages/runtime/src/types/consumption/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./AttributeTagCollectionDTO";
export * from "./DraftDTO";
export * from "./IdentityMetadataDTO";
export * from "./LocalAttributeDTO";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { AttributeTag, AttributeTagCollection } from "@nmshd/consumption";
import { AttributeTagCollectionDTO, AttributeTagDTO } from "../../../types";

export class AttributeTagCollectionMapper {
public static toAttributeTagCollectionDTO(tagList: AttributeTagCollection): AttributeTagCollectionDTO {
return {
supportedLanguages: tagList.supportedLanguages,
tagsForAttributeValueTypes: Object.entries(tagList.tagsForAttributeValueTypes).reduce(
(acc, [key, value]) => {
acc[key] = Object.entries(value).reduce(
(acc2, [key2, value2]) => {
acc2[key2] = AttributeTagCollectionMapper.toAttributeTagDTO(value2);
return acc2;
},
{} as Record<string, AttributeTagDTO>
);
return acc;
},
{} as Record<string, Record<string, AttributeTagDTO>>
)
};
}

public static toAttributeTagDTO(tagListDTO: AttributeTag): AttributeTagDTO {
const tagDTO: AttributeTagDTO = {
displayNames: tagListDTO.displayNames
};
if (tagListDTO.children) {
tagDTO.children = Object.entries(tagListDTO.children).reduce(
(acc, [key, value]) => {
acc[key] = AttributeTagCollectionMapper.toAttributeTagDTO(value);
return acc;
},
{} as Record<string, AttributeTagDTO>
);
}
return tagDTO;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Result } from "@js-soft/ts-utils";
import { AttributesController } from "@nmshd/consumption";
import { Inject } from "@nmshd/typescript-ioc";
import { AttributeTagCollectionDTO } from "../../../types";
import { UseCase } from "../../common";
import { AttributeTagCollectionMapper } from "./AttributeTagCollectionMapper";

export class GetAttributeTagCollectionUseCase extends UseCase<void, AttributeTagCollectionDTO> {
public constructor(@Inject private readonly attributesController: AttributesController) {
super();
}

protected async executeInternal(): Promise<Result<AttributeTagCollectionDTO>> {
const attributeTagCollection = await this.attributesController.getAttributeTagCollection();
return Result.ok(AttributeTagCollectionMapper.toAttributeTagCollectionDTO(attributeTagCollection));
}
}
2 changes: 2 additions & 0 deletions packages/runtime/src/useCases/consumption/attributes/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./AttributeMapper";
export * from "./AttributeTagCollectionMapper";
export * from "./ChangeDefaultRepositoryAttribute";
export * from "./CreateAndShareRelationshipAttribute";
export * from "./CreateRepositoryAttribute";
Expand All @@ -13,6 +14,7 @@ export * from "./ExecuteRelationshipAttributeQuery";
export * from "./ExecuteThirdPartyRelationshipAttributeQuery";
export * from "./GetAttribute";
export * from "./GetAttributes";
export * from "./GetAttributeTagCollection";
export * from "./GetOwnSharedAttributes";
export * from "./GetPeerSharedAttributes";
export * from "./GetRepositoryAttributes";
Expand Down
65 changes: 65 additions & 0 deletions packages/runtime/test/consumption/attributeTagCollection.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { ClientResult, TagClient } from "@nmshd/transport";
import { reset, spy, when } from "ts-mockito";
import { RuntimeServiceProvider, TestRuntimeServices } from "../lib";

const serviceProvider = new RuntimeServiceProvider();
let runtimeService: TestRuntimeServices;

let mockedRestClient: TagClient;

beforeAll(async () => {
runtimeService = (await serviceProvider.launch(1))[0];
const client = runtimeService.consumption.attributes["getAttributeTagCollectionUseCase"]["attributesController"]["attributeTagClient"] as TagClient;
mockedRestClient = spy(client);
}, 30000);

afterAll(() => serviceProvider.stop());

afterEach(() => {
reset(mockedRestClient);
});

describe("get attributeTagCollection", function () {
/* eslint-disable @typescript-eslint/naming-convention */
const mockTags = {
supportedLanguages: ["de", "en"],
tagsForAttributeValueTypes: {
PhoneNumber: {
emergency: {
displayNames: {
de: "Notfallkontakt",
en: "Emergency Contact"
},
children: {
first: {
displayNames: {
de: "Erster Notfallkontakt",
en: "First Emergency Contact"
}
},
second: {
displayNames: {
de: "Zweiter Notfallkontakt",
en: "Second Emergency Contact"
}
}
}
},
private: {
displayNames: {
de: "Privat",
en: "Private"
}
}
}
}
};
/* eslint-enable @typescript-eslint/naming-convention */

test("should receive the legal tags from the Backbone", async function () {
when(mockedRestClient.getTagCollection()).thenResolve(ClientResult.ok(mockTags));
const tags = await runtimeService.consumption.attributes.getAttributeTagCollection();

expect(tags.value).toStrictEqual(mockTags);
});
});
2 changes: 2 additions & 0 deletions packages/transport/src/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ export * from "./sync/DatawalletModificationsProcessor";
export * from "./sync/local/DatawalletModification";
export * from "./sync/SyncController";
export * from "./sync/SynchronizedCollection";
export * from "./tags/backbone/BackboneGetTagCollection";
export * from "./tags/backbone/TagClient";
export * from "./tokens/AnonymousTokenController";
export * from "./tokens/backbone/BackboneGetTokens";
export * from "./tokens/backbone/BackbonePostTokens";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface BackboneGetTagCollection {
supportedLanguages: string[];
tagsForAttributeValueTypes: Record<string, Record<string, BackboneGetTag>>;
}

interface BackboneGetTag {
displayNames: Record<string, string>;
children?: Record<string, BackboneGetTag>;
}
Loading

0 comments on commit c285509

Please sign in to comment.