diff --git a/packages/connected/src/ConnectedContext.ts b/packages/connected/src/ConnectedContext.ts new file mode 100644 index 0000000..9f097cc --- /dev/null +++ b/packages/connected/src/ConnectedContext.ts @@ -0,0 +1,8 @@ +import type { ConnectedLdoDataset } from "./ConnectedLdoDataset"; +import type { ConnectedPlugin } from "./ConnectedPlugin"; + +export type ConnectedContext = { + dataset: ConnectedLdoDataset; +} & { + [P in Plugins[number] as P["name"]]: P["types"]["context"]; +}; diff --git a/packages/connected/src/ConnectedLdoDataset.ts b/packages/connected/src/ConnectedLdoDataset.ts index 88e4154..987db77 100644 --- a/packages/connected/src/ConnectedLdoDataset.ts +++ b/packages/connected/src/ConnectedLdoDataset.ts @@ -33,6 +33,8 @@ export class ConnectedLdoDataset super(datasetFactory, transactionDatasetFactory, initialDataset); this.plugins = plugins; this.resourceMap = new Map(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore this is a builder. It will eventually match this.context = { dataset: this, }; @@ -91,6 +93,7 @@ export class ConnectedLdoDataset resource = plugin.getResource(uri, this.context); this.resourceMap.set(normalizedUri, resource); } + // HACK: cast to any return resource as any; } @@ -99,10 +102,14 @@ export class ConnectedLdoDataset Plugin extends Extract, >(name: Name): Promise> { const validPlugin = this.plugins.find((plugin) => name === plugin.name)!; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore I'm not sure why this doesn't work const newResourceResult = await validPlugin.createResource(this.context); - if (newResourceResult.isError) return newResourceResult; + // HACK: cast to any + if (newResourceResult.isError) return newResourceResult as any; this.resourceMap.set(newResourceResult.uri, newResourceResult); - return newResourceResult; + // HACK: cast to any + return newResourceResult as any; } setContext< diff --git a/packages/connected/src/ConnectedLdoTransactionDataset.ts b/packages/connected/src/ConnectedLdoTransactionDataset.ts index e6dfc10..94d380c 100644 --- a/packages/connected/src/ConnectedLdoTransactionDataset.ts +++ b/packages/connected/src/ConnectedLdoTransactionDataset.ts @@ -6,19 +6,19 @@ import { type ITransactionDatasetFactory, } from "@ldo/subscribable-dataset"; import type { DatasetChanges, GraphNode } from "@ldo/rdf-utils"; -import type { ConnectedLdoDataset } from "./ConnectedLdoDataset"; import type { ConnectedPlugin } from "./ConnectedPlugin"; import type { ConnectedContext } from "./ConnectedContext"; import type { InvalidIdentifierResource } from "./InvalidIdentifierResource"; import type { IConnectedLdoDataset } from "./IConnectedLdoDataset"; import { splitChangesByGraph } from "./util/splitChangesByGraph"; -import type { - IgnoredInvalidUpdateSuccess, - UpdateSuccess, -} from "./results/success/UpdateSuccess"; +import type { IgnoredInvalidUpdateSuccess } from "./results/success/UpdateSuccess"; import { UpdateDefaultGraphSuccess } from "./results/success/UpdateSuccess"; +import type { ErrorResult } from "./results/error/ErrorResult"; import { AggregateError } from "./results/error/ErrorResult"; -import type { AggregateSuccess } from "./results/success/SuccessResult"; +import type { + AggregateSuccess, + SuccessResult, +} from "./results/success/SuccessResult"; /** * A SolidLdoTransactionDataset has all the functionality of a SolidLdoDataset @@ -138,12 +138,16 @@ export class ConnectedLdoTransactionDataset async commitChanges(): Promise< | AggregateSuccess< - | UpdateSuccess + | Extract< + Awaited>, + { isError: false } + > | UpdateDefaultGraphSuccess + | IgnoredInvalidUpdateSuccess > | AggregateError< Extract< - ReturnType, + Awaited>, { isError: true } > > @@ -156,7 +160,7 @@ export class ConnectedLdoTransactionDataset GraphNode, DatasetChanges, ( - | ReturnType + | Awaited> | IgnoredInvalidUpdateSuccess | UpdateDefaultGraphSuccess ), @@ -168,32 +172,33 @@ export class ConnectedLdoTransactionDataset updateDatasetInBulk(this.parentDataset, datasetChanges); return [graph, datasetChanges, new UpdateDefaultGraphSuccess()]; } - const resource = this.getResource(graph.value); - const updateResult = await resource.update(datasetChanges); + const resource = this.getResource( + graph.value, + ) as Plugins[number]["types"]["resource"]; + const updateResult = (await resource.update( + datasetChanges, + )) as Awaited< + ReturnType + >; return [graph, datasetChanges, updateResult]; }, ), ); // If one has errored, return error - const errors = results.filter((result) => result[2].isError); + const errors = ( + results.map((result) => result[2]) as (SuccessResult | ErrorResult)[] + ).filter((result): result is ErrorResult => result.isError); if (errors.length > 0) { - return new AggregateError( - errors.map((result) => result[2] as UpdateResultError), - ); + // HACK: Cast to Any + return new AggregateError(errors) as any; } return { isError: false, type: "aggregateSuccess", - results: results - .map((result) => result[2]) - .filter( - (result): result is ResourceResult => - result.type === "updateSuccess" || - result.type === "updateDefaultGraphSuccess" || - result.type === "ignoredInvalidUpdateSuccess", - ), + // HACK: Cast to Any + results: results.map((result) => result[2]) as any, }; } } diff --git a/packages/connected/src/ConnectedPlugin.ts b/packages/connected/src/ConnectedPlugin.ts index 7dda68b..ca427b3 100644 --- a/packages/connected/src/ConnectedPlugin.ts +++ b/packages/connected/src/ConnectedPlugin.ts @@ -10,9 +10,12 @@ export interface ConnectedPlugin< ContextType = any, > { name: Name; - getResource(uri: UriType, context: ConnectedContext): ResourceType; + getResource( + uri: UriType, + context: ConnectedContext, + ): ResourceType; createResource( - context: ConnectedContext, + context: ConnectedContext, ): Promise; isUriValid(uri: UriType): uri is UriType; normalizeUri?: (uri: UriType) => UriType; diff --git a/packages/connected/src/util/splitChangesByGraph.d.ts b/packages/connected/src/util/splitChangesByGraph.d.ts new file mode 100644 index 0000000..ece158f --- /dev/null +++ b/packages/connected/src/util/splitChangesByGraph.d.ts @@ -0,0 +1,5 @@ +import type { GraphNode, DatasetChanges } from "@ldo/rdf-utils"; +import type { Quad } from "@rdfjs/types"; +export declare function graphNodeToString(graphNode: GraphNode): string; +export declare function stringToGraphNode(input: string): GraphNode; +export declare function splitChangesByGraph(changes: DatasetChanges): Map>; diff --git a/packages/connected/src/util/splitChangesByGraph.js b/packages/connected/src/util/splitChangesByGraph.js new file mode 100644 index 0000000..0a63728 --- /dev/null +++ b/packages/connected/src/util/splitChangesByGraph.js @@ -0,0 +1,45 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.splitChangesByGraph = exports.stringToGraphNode = exports.graphNodeToString = void 0; +const dataset_1 = require("@ldo/dataset"); +const data_model_1 = require("@rdfjs/data-model"); +function graphNodeToString(graphNode) { + return graphNode.termType === "DefaultGraph" + ? "defaultGraph()" + : graphNode.value; +} +exports.graphNodeToString = graphNodeToString; +function stringToGraphNode(input) { + return input === "defaultGraph()" ? (0, data_model_1.defaultGraph)() : (0, data_model_1.namedNode)(input); +} +exports.stringToGraphNode = stringToGraphNode; +function splitChangesByGraph(changes) { + const changesMap = {}; + changes.added?.forEach((quad) => { + const graphHash = graphNodeToString(quad.graph); + if (!changesMap[graphHash]) { + changesMap[graphHash] = {}; + } + if (!changesMap[graphHash].added) { + changesMap[graphHash].added = (0, dataset_1.createDataset)(); + } + changesMap[graphHash].added?.add((0, data_model_1.quad)(quad.subject, quad.predicate, quad.object, quad.graph)); + }); + changes.removed?.forEach((quad) => { + const graphHash = graphNodeToString(quad.graph); + if (!changesMap[graphHash]) { + changesMap[graphHash] = {}; + } + if (!changesMap[graphHash].removed) { + changesMap[graphHash].removed = (0, dataset_1.createDataset)(); + } + changesMap[graphHash].removed?.add((0, data_model_1.quad)(quad.subject, quad.predicate, quad.object, quad.graph)); + }); + const finalMap = new Map(); + Object.entries(changesMap).forEach(([key, value]) => { + finalMap.set(stringToGraphNode(key), value); + }); + return finalMap; +} +exports.splitChangesByGraph = splitChangesByGraph; +//# sourceMappingURL=splitChangesByGraph.js.map \ No newline at end of file diff --git a/packages/connected/src/util/splitChangesByGraph.js.map b/packages/connected/src/util/splitChangesByGraph.js.map new file mode 100644 index 0000000..a31a8b2 --- /dev/null +++ b/packages/connected/src/util/splitChangesByGraph.js.map @@ -0,0 +1 @@ +{"version":3,"file":"splitChangesByGraph.js","sourceRoot":"","sources":["splitChangesByGraph.ts"],"names":[],"mappings":";;;AAAA,0CAA6C;AAG7C,kDAAgF;AAQhF,SAAgB,iBAAiB,CAAC,SAAoB;IACpD,OAAO,SAAS,CAAC,QAAQ,KAAK,cAAc;QAC1C,CAAC,CAAC,gBAAgB;QAClB,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC;AACtB,CAAC;AAJD,8CAIC;AAQD,SAAgB,iBAAiB,CAAC,KAAa;IAC7C,OAAO,KAAK,KAAK,gBAAgB,CAAC,CAAC,CAAC,IAAA,yBAAY,GAAE,CAAC,CAAC,CAAC,IAAA,sBAAS,EAAC,KAAK,CAAC,CAAC;AACxE,CAAC;AAFD,8CAEC;AASD,SAAgB,mBAAmB,CACjC,OAA6B;IAE7B,MAAM,UAAU,GAAyC,EAAE,CAAC;IAC5D,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC9B,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,KAAkB,CAAC,CAAC;QAC7D,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;YAC1B,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;SAC5B;QACD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE;YAChC,UAAU,CAAC,SAAS,CAAC,CAAC,KAAK,GAAG,IAAA,uBAAa,GAAE,CAAC;SAC/C;QACD,UAAU,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,GAAG,CAC9B,IAAA,iBAAU,EAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAClE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAChC,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,KAAkB,CAAC,CAAC;QAC7D,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;YAC1B,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;SAC5B;QACD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;YAClC,UAAU,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,IAAA,uBAAa,GAAE,CAAC;SACjD;QACD,UAAU,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,CAChC,IAAA,iBAAU,EAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAClE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAmC,CAAC;IAC5D,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QAClD,QAAQ,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AAClB,CAAC;AAnCD,kDAmCC"} \ No newline at end of file diff --git a/packages/connected/test/MockResource.ts b/packages/connected/test/MockResource.ts index 3261ee3..468c32a 100644 --- a/packages/connected/test/MockResource.ts +++ b/packages/connected/test/MockResource.ts @@ -1,12 +1,15 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import EventEmitter from "events"; +import type { ResourceError } from "../src"; import { Unfetched, type ConnectedResult, type Resource, type ResourceEventEmitter, - type ResourceResult, } from "../src"; +import type { DatasetChanges } from "@ldo/rdf-utils"; +import type { ReadSuccess } from "../src/results/success/ReadSuccess"; +import type { UpdateSuccess } from "../src/results/success/UpdateSuccess"; export class MockResouce extends (EventEmitter as new () => ResourceEventEmitter) @@ -44,10 +47,15 @@ export class MockResouce isSubscribedToNotifications(): boolean { throw new Error("Method not implemented."); } - read(): Promise> { + read(): Promise | ResourceError> { throw new Error("Method not implemented."); } - readIfAbsent(): Promise> { + readIfAbsent(): Promise | ResourceError> { + throw new Error("Method not implemented."); + } + update( + _datasetChanges: DatasetChanges, + ): Promise | ResourceError> { throw new Error("Method not implemented."); } subscribeToNotifications(_callbacks?: { diff --git a/packages/connected/tsconfig.build.json b/packages/connected/tsconfig.build.json index 4bd5a5e..8083f35 100644 --- a/packages/connected/tsconfig.build.json +++ b/packages/connected/tsconfig.build.json @@ -1,7 +1,8 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "./dist", + "outDir": "./dist" }, - "include": ["./src"] + "include": ["./src"], + "exclude": ["./dist", "./coverage"] } \ No newline at end of file