You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
ldo-compact-fork/packages/solid/src/SolidLdoTransactionDataset.ts

186 lines
6.0 KiB

import { LdoTransactionDataset } from "@ldo/ldo";
import type { ISolidLdoDataset } from "./types";
import type { ResourceGetterOptions } from "./ResourceStore";
import type { Container } from "./resource/Container";
import type { Leaf } from "./resource/Leaf";
import {
isContainerUri,
type ContainerUri,
type LeafUri,
} from "./util/uriTypes";
import type { SolidLdoDatasetContext } from "./SolidLdoDatasetContext";
import type { DatasetFactory, Quad } from "@rdfjs/types";
import {
updateDatasetInBulk,
type ITransactionDatasetFactory,
} from "@ldo/subscribable-dataset";
import type { AggregateSuccess } from "./requester/results/success/SuccessResult";
import type { ResourceResult } from "./resource/resourceResult/ResourceResult";
import type {
IgnoredInvalidUpdateSuccess,
UpdateDefaultGraphSuccess,
UpdateSuccess,
} from "./requester/results/success/UpdateSuccess";
import { AggregateError } from "./requester/results/error/ErrorResult";
import type {
UpdateResult,
UpdateResultError,
} from "./requester/requests/updateDataResource";
import type { DatasetChanges, GraphNode } from "@ldo/rdf-utils";
import { splitChangesByGraph } from "./util/splitChangesByGraph";
/**
* A SolidLdoTransactionDataset has all the functionality of a SolidLdoDataset
* and represents a transaction to the parent SolidLdoDataset.
*
* It is recommended to use the `startTransaction` method on a SolidLdoDataset
* to initialize this class
*
* @example
* ```typescript
* import { createSolidLdoDataset } from "@ldo/solid";
* import { ProfileShapeType } from "./.ldo/profile.shapeTypes.ts"
*
* // ...
*
* const solidLdoDataset = createSolidLdoDataset();
*
* const profileDocument = solidLdoDataset
* .getResource("https://example.com/profile");
* await profileDocument.read();
*
* const transaction = solidLdoDataset.startTransaction();
*
* const profile = transaction
* .using(ProfileShapeType)
* .fromSubject("https://example.com/profile#me");
* profile.name = "Some Name";
* await transaction.commitToPod();
* ```
*/
export class SolidLdoTransactionDataset
extends LdoTransactionDataset
implements ISolidLdoDataset
{
/**
* @internal
*/
public context: SolidLdoDatasetContext;
/**
* @param context - SolidLdoDatasetContext
* @param datasetFactory - An optional dataset factory
* @param transactionDatasetFactory - A factory for creating transaction datasets
* @param initialDataset - A set of triples to initialize this dataset
*/
constructor(
parentDataset: ISolidLdoDataset,
context: SolidLdoDatasetContext,
datasetFactory: DatasetFactory,
transactionDatasetFactory: ITransactionDatasetFactory<Quad>,
) {
super(parentDataset, datasetFactory, transactionDatasetFactory);
this.context = context;
}
/**
* Retireves a representation (either a LeafResource or a ContainerResource)
* of a Solid Resource at the given URI. This resource represents the
* current state of the resource: whether it is currently fetched or in the
* process of fetching as well as some information about it.
*
* @param uri - the URI of the resource
* @param options - Special options for getting the resource
*
* @returns a Leaf or Container Resource
*
* @example
* ```typescript
* const profileDocument = solidLdoDataset
* .getResource("https://example.com/profile");
* ```
*/
getResource(uri: ContainerUri, options?: ResourceGetterOptions): Container;
getResource(uri: LeafUri, options?: ResourceGetterOptions): Leaf;
getResource(uri: string, options?: ResourceGetterOptions): Leaf | Container;
getResource(uri: string, options?: ResourceGetterOptions): Leaf | Container {
return this.context.resourceStore.get(uri, options);
}
public startTransaction(): SolidLdoTransactionDataset {
return new SolidLdoTransactionDataset(
this,
this.context,
this.datasetFactory,
this.transactionDatasetFactory,
);
}
async commitToPod(): Promise<
| AggregateSuccess<
ResourceResult<UpdateSuccess | UpdateDefaultGraphSuccess, Leaf>
>
| AggregateError<UpdateResultError>
> {
const changes = this.getChanges();
const changesByGraph = splitChangesByGraph(changes);
// Iterate through all changes by graph in
const results: [
GraphNode,
DatasetChanges<Quad>,
UpdateResult | IgnoredInvalidUpdateSuccess | UpdateDefaultGraphSuccess,
][] = await Promise.all(
Array.from(changesByGraph.entries()).map(
async ([graph, datasetChanges]) => {
if (graph.termType === "DefaultGraph") {
// Undefined means that this is the default graph
updateDatasetInBulk(this.parentDataset, datasetChanges);
return [
graph,
datasetChanges,
{
type: "updateDefaultGraphSuccess",
isError: false,
} as UpdateDefaultGraphSuccess,
];
}
if (isContainerUri(graph.value)) {
return [
graph,
datasetChanges,
{
type: "ignoredInvalidUpdateSuccess",
isError: false,
} as IgnoredInvalidUpdateSuccess,
];
}
const resource = this.getResource(graph.value as LeafUri);
const updateResult = await resource.update(datasetChanges);
return [graph, datasetChanges, updateResult];
},
),
);
// If one has errored, return error
const errors = results.filter((result) => result[2].isError);
if (errors.length > 0) {
return new AggregateError(
errors.map((result) => result[2] as UpdateResultError),
);
}
return {
isError: false,
type: "aggregateSuccess",
results: results
.map((result) => result[2])
.filter(
(result): result is ResourceResult<UpdateSuccess, Leaf> =>
result.type === "updateSuccess" ||
result.type === "updateDefaultGraphSuccess" ||
result.type === "ignoredInvalidUpdateSuccess",
),
};
}
}