Skip to content

Commit

Permalink
feat: allow client to be extended with custom controllers (#240)
Browse files Browse the repository at this point in the history
  • Loading branch information
fdionisi authored Jun 10, 2024
1 parent fc7cbb9 commit e923554
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 74 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
.idea/
connector/.gradle
connector/.settings
connector/.classpath
connector/.project
connector/build
coverage/
dist/
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"license": "MIT",
"dependencies": {
"@think-it-labs/typed-error": "^0.3.0",
"jsonld": "^8.2.1"
"jsonld": "^8.2.1",
"type-fest": "^4.20.0"
},
"devDependencies": {
"@commitlint/config-conventional": "^19.0.3",
Expand Down
81 changes: 60 additions & 21 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,42 @@
import { Class } from "type-fest";
import { version } from "../package.json";
import { EdcConnectorClientContext } from "./context";
import { ObservabilityController, PublicController } from "./controllers";
import { EdcController } from "./edc-controller";
import { Addresses } from "./entities";
import { ManagementController } from "./facades/management";
import { Inner } from "./inner";

import { version } from "../package.json";
import { ManagementController } from "./facades/management";
export type EdcConnectorClientType<
T extends Record<string, EdcController>,
> = EdcConnectorClient & T;

export class EdcConnectorClient {

#apiToken: string | undefined;
#addresses: Addresses = {};
#inner = new Inner();

constructor() {

}

get management() {
const context = new EdcConnectorClientContext(this.#apiToken, this.#addresses);
const context = new EdcConnectorClientContext(
this.#apiToken,
this.#addresses,
);
return new ManagementController(this.#inner, context);
}

get observability() {
const context = new EdcConnectorClientContext(this.#apiToken, this.#addresses);
const context = new EdcConnectorClientContext(
this.#apiToken,
this.#addresses,
);
return new ObservabilityController(this.#inner, context);
}

get public() {
const context = new EdcConnectorClientContext(this.#apiToken, this.#addresses);
const context = new EdcConnectorClientContext(
this.#apiToken,
this.#addresses,
);
return new PublicController(this.#inner, context);
}

Expand All @@ -46,42 +55,72 @@ export class EdcConnectorClient {
return version;
}

static Builder = class Builder {
static Builder = class Builder<
Controllers extends Record<
string,
EdcController
> = {},
> {
#instance = new EdcConnectorClient();

apiToken(apiToken: string): Builder {
this.#instance.#apiToken = apiToken
apiToken(apiToken: string): this {
this.#instance.#apiToken = apiToken;
return this;
}

managementUrl(managementUrl: string): Builder {
managementUrl(managementUrl: string): this {
this.#instance.#addresses.management = managementUrl;
return this;
}

defaultUrl(defaultUrl: string): Builder {
defaultUrl(defaultUrl: string): this {
this.#instance.#addresses.default = defaultUrl;
return this;
}

protocolUrl(protocolUrl: string): Builder {
protocolUrl(protocolUrl: string): this {
this.#instance.#addresses.protocol = protocolUrl;
return this;
}

publicUrl(publicUrl: string): Builder {
publicUrl(publicUrl: string): this {
this.#instance.#addresses.public = publicUrl;
return this;
}

controlUrl(controlUrl: string): Builder {
controlUrl(controlUrl: string): this {
this.#instance.#addresses.control = controlUrl;
return this;
}

build(): EdcConnectorClient {
return this.#instance;
use<K extends string, C extends EdcController>(
key: K,
Controller: Class<C>,
): Builder<Controllers & Record<K, C>> {
Object.defineProperty(this.#instance, key, {
get() {
return new Controller(
this.#inner,
this.createContext(
this.#apiToken!,
this.#addresses,
),
);
},
enumerable: true,
configurable: false,
});

// SAFETY: we use `Object.defineProperty` above to extend the `EdcConnectorClient` instance.
return this as any;
}
}

build(): EdcConnectorClientType<
Controllers
> {
return this.#instance as
& EdcConnectorClient
& Controllers;
}
};
}
34 changes: 14 additions & 20 deletions src/controllers/observability-controller.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import { EdcConnectorClientContext } from "../context";
import { HealthStatus } from "../entities";
import { Inner } from "../inner";
import { EdcController } from "../edc-controller";

export class ObservabilityController {
#inner: Inner;
#context?: EdcConnectorClientContext;

constructor(inner: Inner, context?: EdcConnectorClientContext) {
this.#inner = inner;
this.#context = context;
}

async checkHealth(context?: EdcConnectorClientContext): Promise<HealthStatus> {
const actualContext = context || this.#context!;
export class ObservabilityController extends EdcController {
async checkHealth(
context?: EdcConnectorClientContext,
): Promise<HealthStatus> {
const actualContext = context || this.context!;

return this.#inner.request(actualContext.default, {
return this.inner.request(actualContext.default, {
path: "/check/health",
method: "GET",
apiToken: actualContext.apiToken,
Expand All @@ -24,9 +18,9 @@ export class ObservabilityController {
async checkLiveness(
context?: EdcConnectorClientContext,
): Promise<HealthStatus> {
const actualContext = context || this.#context!;
const actualContext = context || this.context!;

return this.#inner.request(actualContext.default, {
return this.inner.request(actualContext.default, {
path: "/check/liveness",
method: "GET",
apiToken: actualContext.apiToken,
Expand All @@ -36,9 +30,9 @@ export class ObservabilityController {
async checkReadiness(
context?: EdcConnectorClientContext,
): Promise<HealthStatus> {
const actualContext = context || this.#context!;
const actualContext = context || this.context!;

return this.#inner.request(actualContext.default, {
return this.inner.request(actualContext.default, {
path: "/check/readiness",
method: "GET",
apiToken: actualContext.apiToken,
Expand All @@ -48,9 +42,9 @@ export class ObservabilityController {
async checkStartup(
context?: EdcConnectorClientContext,
): Promise<HealthStatus> {
const actualContext = context || this.#context!;
return this.#inner.request(actualContext.default, {
const actualContext = context || this.context!;

return this.inner.request(actualContext.default, {
path: "/check/startup",
method: "GET",
apiToken: actualContext.apiToken,
Expand Down
16 changes: 4 additions & 12 deletions src/controllers/public-controller.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import { EdcConnectorClientContext } from "../context";
import { Inner } from "../inner";

export class PublicController {
#inner: Inner;
#context?: EdcConnectorClientContext;

constructor(inner: Inner, context?: EdcConnectorClientContext) {
this.#inner = inner;
this.#context = context;
}
import { EdcController } from "../edc-controller";

export class PublicController extends EdcController {
async getTransferredData(
headers: Record<string, string | undefined>,
context?: EdcConnectorClientContext,
): Promise<Response> {
const actualContext = context || this.#context!;
const actualContext = context || this.context!;

return this.#inner.stream(actualContext.public, {
return this.inner.stream(actualContext.public, {
path: "/",
method: "GET",
headers,
Expand Down
12 changes: 12 additions & 0 deletions src/edc-controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { EdcConnectorClientContext } from "./context";
import { Inner } from "./inner";

export class EdcController {
protected inner: Inner;
protected context?: EdcConnectorClientContext;

constructor(inner: Inner, context?: EdcConnectorClientContext) {
this.inner = inner;
this.context = context;
}
}
29 changes: 10 additions & 19 deletions src/facades/management.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Inner } from "../inner";
import { EdcConnectorClientContext } from "../context";
import {
AssetController,
CatalogController,
Expand All @@ -12,33 +10,26 @@ import {
SecretController,
TransferProcessController,
} from "../controllers/management-controllers";
import { EdcController } from "../edc-controller";

export class ManagementController {
#inner: Inner;
#context: EdcConnectorClientContext | undefined;

constructor(inner: Inner, context?: EdcConnectorClientContext) {
this.#inner = inner;
this.#context = context;
}

export class ManagementController extends EdcController {
get assets() {
return new AssetController(this.#inner, this.#context);
return new AssetController(this.inner, this.context);
}
get catalog() {
return new CatalogController(this.#inner, this.#context);
return new CatalogController(this.inner, this.context);
}
get contractAgreements() {
return new ContractAgreementController(this.#inner, this.#context);
return new ContractAgreementController(this.inner, this.context);
}
get contractDefinitions() {
return new ContractDefinitionController(this.#inner, this.#context);
return new ContractDefinitionController(this.inner, this.context);
}
get contractNegotiations() {
return new ContractNegotiationController(this.#inner, this.#context);
return new ContractNegotiationController(this.inner, this.context);
}
get dataplanes() {
return new DataplaneController(this.#inner, this.#context);
return new DataplaneController(this.inner, this.context);
}
get edrs(): EdrController {
return new EdrController(this.#inner, this.#context);
Expand All @@ -47,9 +38,9 @@ export class ManagementController {
return new SecretController(this.#inner, this.#context);
}
get policyDefinitions() {
return new PolicyDefinitionController(this.#inner, this.#context);
return new PolicyDefinitionController(this.inner, this.context);
}
get transferProcesses() {
return new TransferProcessController(this.#inner, this.#context);
return new TransferProcessController(this.inner, this.context);
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./context";
export * from "./client";
export * from "./entities";
export * from "./error";
export * from "./edc-controller";
43 changes: 42 additions & 1 deletion tests/edc-client.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { EdcConnectorClient, EdcConnectorClientContext } from "../src";
import {
EdcConnectorClient,
EdcConnectorClientContext,
EdcController,
} from "../src";
import { Addresses } from "../src";

describe("EdcConnectorClient", () => {
Expand Down Expand Up @@ -39,4 +43,41 @@ describe("EdcConnectorClient", () => {
expect(context.control).toBe(addresses.control);
});
});

describe("edcClient.Builder.use", () => {
interface ActiveResponse {
active: boolean;
}

class FooController extends EdcController {
async testFoo(): Promise<ActiveResponse> {
return {
active: true,
};
}
}

class BarController extends EdcController {
async testBar(): Promise<ActiveResponse> {
return {
active: false,
};
}
}

it("allows to extend the clients through middlewares", async () => {
const client = new EdcConnectorClient.Builder()
.use("foo", FooController)
.use("bar", BarController)
.build();

await expect(client.foo.testFoo()).resolves.toEqual({
active: true,
});

await expect(client.bar.testBar()).resolves.toEqual({
active: false,
});
});
});
});
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3669,6 +3669,11 @@ type-fest@^0.21.3:
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==

type-fest@^4.20.0:
version "4.20.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.20.0.tgz#019becf5a97cd58eee93f592f0961859a74482a7"
integrity sha512-MBh+PHUHHisjXf4tlx0CFWoMdjx8zCMLJHOjnV1prABYZFHqtFOyauCIK2/7w4oIfwkF8iNhLtnJEfVY2vn3iw==

typescript@^5.1.6:
version "5.4.5"
resolved "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz"
Expand Down

0 comments on commit e923554

Please sign in to comment.