Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identity Metadata #3

Merged
merged 75 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
2ddd377
chore: remove unneeded atTypes
jkoenig134 Jan 29, 2024
a05d520
feat(confumption): implement identity metadata
jkoenig134 Jan 29, 2024
1587bbc
test(consumption): add tests for identity metadata
jkoenig134 Jan 29, 2024
ac50cb1
Merge branch 'main' into feature/identity-metadata
jkoenig134 Jan 29, 2024
a481c57
Merge branch 'main' into feature/identity-metadata
jkoenig134 Feb 2, 2024
cda2537
Merge branch 'main' into feature/identity-metadata
jkoenig134 Feb 6, 2024
e72e99f
Merge main into feature/identity-metadata
github-actions[bot] Feb 7, 2024
bfb94e4
Merge main into feature/identity-metadata
github-actions[bot] Feb 7, 2024
ceb5670
Merge main into feature/identity-metadata
github-actions[bot] Feb 9, 2024
fdca024
Merge branch 'main' into feature/identity-metadata
jkoenig134 Feb 9, 2024
e88de9b
Merge branch 'main' into feature/identity-metadata
jkoenig134 Feb 9, 2024
20a7f64
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 9, 2024
d49255e
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 12, 2024
4da50eb
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 12, 2024
c229fb7
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 13, 2024
63c9b83
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 14, 2024
7c86de9
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 15, 2024
6378691
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 16, 2024
9458e7b
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 19, 2024
bfa9e10
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 19, 2024
21f97dc
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 20, 2024
ecdcf9a
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 20, 2024
efc1b86
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 22, 2024
8ee874b
Merge branch 'main' into feature/identity-metadata
mergify[bot] Feb 23, 2024
ac7d0a5
Merge branch 'main' into feature/identity-metadata
mergify[bot] Mar 11, 2024
f04f84a
Merge branch 'main' into feature/identity-metadata
mergify[bot] Mar 11, 2024
a3e9347
Merge branch 'main' into feature/identity-metadata
mergify[bot] Mar 12, 2024
834794f
Merge branch 'main' into feature/identity-metadata
mergify[bot] Mar 12, 2024
a9be732
Merge branch 'main' into feature/identity-metadata
mergify[bot] Mar 18, 2024
79f99a5
Merge branch 'main' into feature/identity-metadata
mergify[bot] Mar 18, 2024
0d3bec1
Merge branch 'main' into feature/identity-metadata
mergify[bot] Mar 19, 2024
26b4028
Merge branch 'main' into feature/identity-metadata
mergify[bot] Apr 2, 2024
38ef8b2
Merge branch 'main' into feature/identity-metadata
mergify[bot] Apr 5, 2024
df67022
Merge branch 'main' into feature/identity-metadata
jkoenig134 Apr 16, 2024
2ec1200
Merge branch 'main' into feature/identity-metadata
mergify[bot] Apr 16, 2024
9f5d7e5
Merge branch 'main' into feature/identity-metadata
mergify[bot] Apr 17, 2024
fc46978
Merge branch 'main' into feature/identity-metadata
mergify[bot] Apr 22, 2024
dd84115
Merge branch 'main' into feature/identity-metadata
mergify[bot] Apr 23, 2024
6f280de
Merge branch 'main' into feature/identity-metadata
mergify[bot] Apr 23, 2024
e4d338b
Merge branch 'main' into feature/identity-metadata
mergify[bot] Apr 23, 2024
8a56d4e
Merge branch 'main' into feature/identity-metadata
mergify[bot] Apr 24, 2024
65c49c9
Merge branch 'main' into feature/identity-metadata
mergify[bot] Apr 25, 2024
b6c756e
Merge branch 'main' into feature/identity-metadata
mergify[bot] Apr 29, 2024
b2b77ca
Merge branch 'main' into feature/identity-metadata
jkoenig134 Aug 12, 2024
01e97ea
Merge branch 'main' into feature/identity-metadata
mergify[bot] Aug 19, 2024
03cdc7a
Merge branch 'main' into feature/identity-metadata
jkoenig134 Aug 28, 2024
00dc8f9
fix: adapt coding to runtime changes
jkoenig134 Aug 30, 2024
c6859f0
Merge branch 'main' into feature/identity-metadata
jkoenig134 Sep 12, 2024
415c571
chore: undo refactorings
jkoenig134 Sep 12, 2024
8794836
refactor: massively simplify the IdentityMetadataController
jkoenig134 Sep 12, 2024
eb68fcc
feat: add runtime usecases
jkoenig134 Sep 12, 2024
8eb52cf
feat: add IdentityMetadataFacade
jkoenig134 Sep 12, 2024
64cb2de
fix: register IdentityMetadataController
jkoenig134 Sep 12, 2024
596d108
fix: make sure undefined as key is treated as the key does not exist
jkoenig134 Sep 12, 2024
00cb68a
feat: deleteIdentityMetadataExchangedWithPeer
jkoenig134 Sep 12, 2024
27757aa
chore: bump serval
jkoenig134 Sep 12, 2024
c8eef87
refactor: make identity metadata content a JSONWrapper
jkoenig134 Sep 12, 2024
4a0f98a
fix: remove old imports
jkoenig134 Sep 12, 2024
e7c7cc3
test: first runtime tests
jkoenig134 Sep 12, 2024
3111716
chore: update test
jkoenig134 Sep 12, 2024
6906f23
Merge branch 'main' into feature/identity-metadata
jkoenig134 Sep 18, 2024
b214ba1
chore: add test
jkoenig134 Sep 18, 2024
4f3fadd
fix: typo
jkoenig134 Sep 18, 2024
48ed0c9
chore: simplify tests
jkoenig134 Sep 18, 2024
5a8d9f4
rename stuff
jkoenig134 Sep 18, 2024
b4438c3
chore: PR comments
jkoenig134 Sep 18, 2024
d592267
chore: naming
jkoenig134 Sep 18, 2024
73a988a
chore: naming
jkoenig134 Sep 18, 2024
4875e74
chore: just remove that strange test
jkoenig134 Sep 18, 2024
e333c6b
chore: add datawallet sync
jkoenig134 Sep 18, 2024
8973e1a
chore: PR comment
jkoenig134 Sep 18, 2024
20985e9
chore: wording
jkoenig134 Sep 18, 2024
045bcbe
chore: test naming
jkoenig134 Sep 18, 2024
428a7fd
chore: naming
jkoenig134 Sep 18, 2024
760f719
chore: aaaarghggh
jkoenig134 Sep 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions packages/consumption/src/consumption/ConsumptionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
DraftsController,
FreeTextRequestItemProcessor,
GenericRequestItemProcessor,
IdentityMetadataController,
IncomingRequestsController,
NotificationItemConstructor,
NotificationItemProcessorConstructor,
Expand Down Expand Up @@ -86,6 +87,11 @@ export class ConsumptionController {
return this._notifications;
}

private _identityMetadata: IdentityMetadataController;
public get identityMetadata(): IdentityMetadataController {
return this._identityMetadata;
}

public async init(
requestItemProcessorOverrides = new Map<RequestItemConstructor, RequestItemProcessorConstructor>(),
notificationItemProcessorOverrides = new Map<NotificationItemConstructor, NotificationItemProcessorConstructor>()
Expand Down Expand Up @@ -135,6 +141,8 @@ export class ConsumptionController {
this.accountController.activeDevice
).init();

this._identityMetadata = await new IdentityMetadataController(this).init();

this._settings = await new SettingsController(this).init();
this._attributeListeners = await new AttributeListenersController(this, this.transport.eventBus, this.accountController.identity).init();
return this;
Expand Down Expand Up @@ -170,5 +178,6 @@ export class ConsumptionController {
await this.settings.deleteSettingsForRelationship(relationshipId);
await this.attributeListeners.deletePeerAttributeListeners(peer);
await this.notifications.deleteNotificationsExchangedWithPeer(peer);
await this.identityMetadata.deleteIdentityMetadataReferencedWithPeer(peer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export enum ConsumptionControllerName {
DraftsController = "DraftsController",
RequestsController = "RequestsController",
SettingsController = "SettingsController",
NotificationsController = "NotificationsController"
NotificationsController = "NotificationsController",
IdentityMetadataController = "IdentityMetadataController"
}
1 change: 1 addition & 0 deletions packages/consumption/src/consumption/ConsumptionIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export class ConsumptionIds {
public static readonly request = new CoreIdHelper("REQ");
public static readonly attributeListener = new CoreIdHelper("ATL");
public static readonly notification = new CoreIdHelper("NOT");
public static readonly identityMetadata = new CoreIdHelper("IDM");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { CoreAddress } from "@nmshd/core-types";
import { SynchronizedCollection } from "@nmshd/transport";
import { ConsumptionBaseController } from "../../consumption/ConsumptionBaseController";
import { ConsumptionController } from "../../consumption/ConsumptionController";
import { ConsumptionControllerName } from "../../consumption/ConsumptionControllerName";
import { ConsumptionIds } from "../../consumption/ConsumptionIds";
import { IdentityMetadata } from "./local/IdentityMetadata";
import { IUpsertIdentityMetadataParams, UpsertIdentityMetadataParams } from "./local/UpsertIdentityMetadataParams";

export class IdentityMetadataController extends ConsumptionBaseController {
private identityMetadata: SynchronizedCollection;

public constructor(parent: ConsumptionController) {
super(ConsumptionControllerName.IdentityMetadataController, parent);
}

public override async init(): Promise<this> {
await super.init();

this.identityMetadata = await this.parent.accountController.getSynchronizedCollection("IdentityMetadata");
return this;
}

public async getIdentityMetadata(reference: CoreAddress, key?: string): Promise<IdentityMetadata | undefined> {
const result = await this.identityMetadata.findOne({
britsta marked this conversation as resolved.
Show resolved Hide resolved
reference: reference.toString(),
key: key ?? { $exists: false }
});

return result ? IdentityMetadata.from(result) : undefined;
}

public async upsertIdentityMetadata(params: IUpsertIdentityMetadataParams): Promise<IdentityMetadata> {
britsta marked this conversation as resolved.
Show resolved Hide resolved
const parsedParams = UpsertIdentityMetadataParams.from(params);

const oldDoc = await this.identityMetadata.findOne({
reference: parsedParams.reference.toString(),
key: parsedParams.key ?? { $exists: false }
});

if (!oldDoc) {
const identityMetadata = IdentityMetadata.from({
id: await ConsumptionIds.identityMetadata.generate(),
key: parsedParams.key,
reference: parsedParams.reference,
value: parsedParams.value
});

await this.identityMetadata.create(identityMetadata);

return identityMetadata;
}

const identityMetadata = IdentityMetadata.from(oldDoc);
identityMetadata.value = parsedParams.value;

await this.identityMetadata.update(oldDoc, identityMetadata);

return identityMetadata;
}

public async deleteIdentityMetadata(identityMetadata: IdentityMetadata): Promise<void> {
await this.identityMetadata.delete(identityMetadata);
}

public async deleteIdentityMetadataReferencedWithPeer(peerAddress: CoreAddress): Promise<void> {
const docs = await this.identityMetadata.find({ reference: peerAddress.toString() });

for (const doc of docs) {
const identityMetadata = IdentityMetadata.from(doc);
await this.deleteIdentityMetadata(identityMetadata);
}
}
}
3 changes: 3 additions & 0 deletions packages/consumption/src/modules/identityMetadata/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./IdentityMetadataController";
export * from "./local/IdentityMetadata";
export * from "./local/UpsertIdentityMetadataParams";
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ISerializable, JSONWrapper, serialize, type, validate } from "@js-soft/ts-serval";
import { CoreAddress, ICoreAddress } from "@nmshd/core-types";
import { CoreSynchronizable, ICoreSynchronizable } from "@nmshd/transport";
import { nameof } from "ts-simple-nameof";

export interface IdentityMetadataJSON {
key?: string;
reference: string;
value: any;
}

export interface IIdentityMetadata extends ICoreSynchronizable {
key?: string;
reference: ICoreAddress;
value: ISerializable;
}

@type("IdentityMetadata")
export class IdentityMetadata extends CoreSynchronizable implements IIdentityMetadata {
public override readonly technicalProperties = ["@type", "@context", nameof<IdentityMetadata>((r) => r.key), nameof<IdentityMetadata>((r) => r.reference)];
public override readonly userdataProperties = [nameof<IdentityMetadata>((r) => r.value)];

@validate({ nullable: true })
@serialize()
public key?: string;

@validate()
@serialize()
public reference: CoreAddress;

@validate()
@serialize()
public value: JSONWrapper;

public static from(value: IIdentityMetadata | IdentityMetadataJSON): IdentityMetadata {
return this.fromAny(value);
}
}
jkoenig134 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ISerializable, JSONWrapper, Serializable, serialize, validate } from "@js-soft/ts-serval";
import { CoreAddress, ICoreAddress } from "@nmshd/core-types";

export interface IUpsertIdentityMetadataParams extends ISerializable {
key?: string;
reference: ICoreAddress;
value: ISerializable;
}

export class UpsertIdentityMetadataParams extends Serializable implements IUpsertIdentityMetadataParams {
@validate({ nullable: true })
@serialize()
public key?: string;

@validate()
@serialize()
public reference: CoreAddress;

@validate()
@serialize()
public value: JSONWrapper;

public static from(value: IUpsertIdentityMetadataParams): UpsertIdentityMetadataParams {
return this.fromAny(value);
}
}
1 change: 1 addition & 0 deletions packages/consumption/src/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from "./attributeListeners";
export * from "./attributes";
export * from "./common";
export * from "./drafts";
export * from "./identityMetadata";
export * from "./notifications";
export * from "./requests";
export * from "./settings";
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { IDatabaseConnection } from "@js-soft/docdb-access-abstractions";
import { JSONWrapper } from "@js-soft/ts-serval";
import { CoreAddress } from "@nmshd/core-types";
import { AccountController, Transport } from "@nmshd/transport";
import { ConsumptionController } from "../../../src";
import { TestUtil } from "../../core/TestUtil";
import { MockEventBus } from "../MockEventBus";

const mockEventBus = new MockEventBus();

describe("IdentityMetadataController", function () {
let connection: IDatabaseConnection;
let transport: Transport;

let consumptionController: ConsumptionController;
let testAccount: AccountController;

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

await transport.init();

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

afterAll(async function () {
await testAccount.close();
await connection.close();
});

afterEach(async function () {
await consumptionController.identityMetadata["identityMetadata"]["parent"].delete({});
const count = await consumptionController.identityMetadata["identityMetadata"].count();
// eslint-disable-next-line jest/no-standalone-expect
expect(count).toBe(0);
});

test("should create an identity metadata", async function () {
const identityMetadata = await consumptionController.identityMetadata.upsertIdentityMetadata({
value: { a: "json" },
reference: CoreAddress.from("did:e:a-domain:dids:anidentity")
});

expect(identityMetadata.reference).toBeInstanceOf(CoreAddress);
expect(identityMetadata.reference.toString()).toBe("did:e:a-domain:dids:anidentity");

expect(identityMetadata.value).toBeInstanceOf(JSONWrapper);
expect(identityMetadata.value.toJSON()).toStrictEqual({ a: "json" });
});

test("should create an identity metadata with a key", async function () {
const identityMetadata = await consumptionController.identityMetadata.upsertIdentityMetadata({
value: { a: "json" },
reference: CoreAddress.from("did:e:a-domain:dids:anidentity"),
key: "key"
});

expect(identityMetadata.reference).toBeInstanceOf(CoreAddress);
expect(identityMetadata.reference.toString()).toBe("did:e:a-domain:dids:anidentity");

expect(identityMetadata.value).toBeInstanceOf(JSONWrapper);
expect(identityMetadata.value.toJSON()).toStrictEqual({ a: "json" });

expect(identityMetadata.key).toBe("key");
});

test("should update an identity metadata", async function () {
const query = { reference: CoreAddress.from("did:e:a-domain:dids:anidentity") };

await consumptionController.identityMetadata.upsertIdentityMetadata({
...query,
value: { a: "json" }
});

const updated = await consumptionController.identityMetadata.upsertIdentityMetadata({
...query,
value: { another: "json" }
});
expect(updated.value.toJSON()).toStrictEqual({ another: "json" });

const queried = await consumptionController.identityMetadata.getIdentityMetadata(CoreAddress.from("did:e:a-domain:dids:anidentity"));
expect(queried).toBeDefined();
expect(queried!.value.toJSON()).toStrictEqual({ another: "json" });
});

test("should update an identity metadata with a key", async function () {
const query = { reference: CoreAddress.from("did:e:a-domain:dids:anidentity"), key: "key" };

await consumptionController.identityMetadata.upsertIdentityMetadata({
...query,
value: { a: "json" }
});

const updated = await consumptionController.identityMetadata.upsertIdentityMetadata({
...query,
value: { another: "json" }
});
expect(updated.value.toJSON()).toStrictEqual({ another: "json" });

const queried = await consumptionController.identityMetadata.getIdentityMetadata(CoreAddress.from("did:e:a-domain:dids:anidentity"), "key");
expect(queried).toBeDefined();
expect(queried!.value.toJSON()).toStrictEqual({ another: "json" });
});

test("should delete an identity metadata", async function () {
const identityMetadata = await consumptionController.identityMetadata.upsertIdentityMetadata({
reference: CoreAddress.from("did:e:a-domain:dids:anidentity"),
value: { a: "json" }
});

expect(await consumptionController.identityMetadata["identityMetadata"].count()).toBe(1);

await consumptionController.identityMetadata.deleteIdentityMetadata(identityMetadata);
expect(await consumptionController.identityMetadata["identityMetadata"].count()).toBe(0);
});

test("should delete an identity metadata with a key", async function () {
const identityMetadata = await consumptionController.identityMetadata.upsertIdentityMetadata({
reference: CoreAddress.from("did:e:a-domain:dids:anidentity"),
key: "key",
value: { a: "json" }
});

expect(await consumptionController.identityMetadata["identityMetadata"].count()).toBe(1);

await consumptionController.identityMetadata.deleteIdentityMetadata(identityMetadata);
expect(await consumptionController.identityMetadata["identityMetadata"].count()).toBe(0);
});

test("should delete all identity metadata referenced with peer", async function () {
await consumptionController.identityMetadata.upsertIdentityMetadata({ reference: CoreAddress.from("did:e:a-domain:dids:anidentity"), value: { a: "json" } });
await consumptionController.identityMetadata.upsertIdentityMetadata({ reference: CoreAddress.from("did:e:a-domain:dids:anidentity"), value: { a: "json" }, key: "key" });
await consumptionController.identityMetadata.upsertIdentityMetadata({
reference: CoreAddress.from("did:e:a-domain:dids:anotheridentity"),
value: { a: "json" },
key: "key"
});
expect(await consumptionController.identityMetadata["identityMetadata"].count()).toBe(3);

await consumptionController.identityMetadata.deleteIdentityMetadataReferencedWithPeer(CoreAddress.from("did:e:a-domain:dids:anidentity"));
expect(await consumptionController.identityMetadata["identityMetadata"].count()).toBe(1);
});
});
5 changes: 5 additions & 0 deletions packages/runtime/src/Runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
AttributesController,
ConsumptionController,
DraftsController,
IdentityMetadataController,
IncomingRequestsController,
NotificationsController,
OutgoingRequestsController,
Expand Down Expand Up @@ -281,6 +282,10 @@ export abstract class Runtime<TConfig extends RuntimeConfig = RuntimeConfig> {
.factory(() => this.getConsumptionController().settings)
.scope(Scope.Request);

Container.bind(IdentityMetadataController)
.factory(() => this.getConsumptionController().identityMetadata)
.scope(Scope.Request);

Container.bind(NotificationsController)
.factory(() => this.getConsumptionController().notifications)
.scope(Scope.Request);
Expand Down
4 changes: 3 additions & 1 deletion packages/runtime/src/extensibility/ConsumptionServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
AttributeListenersFacade,
AttributesFacade,
DraftsFacade,
IdentityMetadataFacade,
IncomingRequestsFacade,
NotificationsFacade,
OutgoingRequestsFacade,
Expand All @@ -17,6 +18,7 @@ export class ConsumptionServices {
@Inject public readonly incomingRequests: IncomingRequestsFacade,
@Inject public readonly outgoingRequests: OutgoingRequestsFacade,
@Inject public readonly attributeListeners: AttributeListenersFacade,
@Inject public readonly notifications: NotificationsFacade
@Inject public readonly notifications: NotificationsFacade,
@Inject public readonly identityMetadata: IdentityMetadataFacade
) {}
}
Loading