Skip to content

Commit

Permalink
Implement experimental support for ActivityPods
Browse files Browse the repository at this point in the history
A report with more details about this will be published in https://github.com/noeldemartin/ramen
  • Loading branch information
NoelDeMartin committed Dec 20, 2024
1 parent f7d78a2 commit c8e5162
Show file tree
Hide file tree
Showing 12 changed files with 235 additions and 35 deletions.
6 changes: 6 additions & 0 deletions src/engines/SolidEngine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe('SolidEngine', () => {
containerUrl,
personUrl,
expect.anything(),
{},
);

const properties = SolidClientMock.createDocumentSpy.mock.calls[0]?.[2];
Expand Down Expand Up @@ -86,6 +87,7 @@ describe('SolidEngine', () => {
parentUrl,
documentUrl,
expect.anything(),
{},
);

const properties = SolidClientMock.createDocumentSpy.mock.calls[0]?.[2];
Expand Down Expand Up @@ -449,6 +451,7 @@ describe('SolidEngine', () => {
new UpdatePropertyOperation(RDFResourceProperty.literal(documentUrl, IRI('foaf:name'), name)),
new UpdatePropertyOperation(RDFResourceProperty.literal(documentUrl, IRI('purl:modified'), date)),
],
{},
);
});

Expand All @@ -474,6 +477,7 @@ describe('SolidEngine', () => {
expect(SolidClientMock.updateDocument).toHaveBeenCalledWith(
documentUrl,
[new RemovePropertyOperation(documentUrl, IRI('foaf:name'))],
{},
);
});

Expand Down Expand Up @@ -507,6 +511,7 @@ describe('SolidEngine', () => {
new RemovePropertyOperation(firstResourceUrl),
new RemovePropertyOperation(secondResourceUrl),
],
{},
);
});

Expand Down Expand Up @@ -556,6 +561,7 @@ describe('SolidEngine', () => {
RDFResourceProperty.reference(secondResourceUrl, 'reference', newFirstResourceUrl),
),
],
{},
);
});

Expand Down
24 changes: 20 additions & 4 deletions src/engines/SolidEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ import RDFResourceProperty from '@/solid/RDFResourceProperty';
import RemovePropertyOperation from '@/solid/operations/RemovePropertyOperation';
import SolidClient from '@/solid/SolidClient';
import UpdatePropertyOperation from '@/solid/operations/UpdatePropertyOperation';
import IRI from '@/solid/utils/IRI';
import { usingExperimentalActivityPods } from '@/experimental';
import type { Fetch } from '@/solid/SolidClient';
import type { LiteralValue } from '@/solid/RDFResourceProperty';
import type { RDFDocumentMetadata } from '@/solid/RDFDocument';
import type { UpdateOperation } from '@/solid/operations/Operation';

import IRI from '@/solid/utils/IRI';

export interface SolidEngineConfig {
useGlobbing: boolean;
globbingBatchSize: number | null;
Expand Down Expand Up @@ -82,7 +82,17 @@ export class SolidEngine implements Engine {
throw new DocumentAlreadyExists(id);

const properties = await this.getJsonLDGraphProperties(document);
const url = await this.client.createDocument(collection, id, properties);
const url = await this.client.createDocument(
collection,
id,
properties,
usingExperimentalActivityPods()
? {
method: 'post',
format: 'application/ld+json',
}
: {},
);

return url;
}
Expand Down Expand Up @@ -126,7 +136,13 @@ export class SolidEngine implements Engine {

const operations = await this.extractJsonLDGraphUpdate(updates);

await this.client.updateDocument(id, operations);
await this.client.updateDocument(
id,
operations,
usingExperimentalActivityPods()
? { format: 'application/ld+json' }
: {},
);
}

public async delete(collection: string, id: string): Promise<void> {
Expand Down
24 changes: 24 additions & 0 deletions src/experimental/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const experimental: ExperimentalFlags = {
activityPods: false,
};

export interface ExperimentalFlags {
activityPods: boolean;
}

export function getExperimentalFlag(flag: keyof ExperimentalFlags): boolean {
return experimental[flag];
}

/**
* Experimental features can be enabled or disabled using this function,
* but they are unstable and they could be removed or modified without conforming
* with Semantic Versioning.
*/
export function setExperimentalFlags(flags: Partial<ExperimentalFlags>): void {
Object.assign(experimental, flags);
}

export function usingExperimentalActivityPods(): boolean {
return getExperimentalFlag('activityPods');
}
1 change: 1 addition & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './engines/index';
export * from './experimental/index';
export * from './models/index';

export { SoukaiSolid } from './SoukaiSolid';
Expand Down
52 changes: 37 additions & 15 deletions src/models/SolidModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ import type {
import type { Fetch, JsonLD, JsonLDGraph, SubjectParts } from '@noeldemartin/solid-utils';
import type { Quad } from 'rdf-js';

import { SolidEngine } from '@/engines/SolidEngine';

import IRI from '@/solid/utils/IRI';
import RDFDocument from '@/solid/RDFDocument';
import { SolidEngine } from '@/engines/SolidEngine';
import { usingExperimentalActivityPods } from '@/experimental';
import type RDFResource from '@/solid/RDFResource';

import {
Expand Down Expand Up @@ -777,7 +777,7 @@ export class SolidModel extends SolidModelBase {
Object
.values(this._relations)
.filter(isSolidDocumentRelation)
.filter(relation => relation.enabled && relation.useSameDocument)
.filter(relation => relation.enabled && relation.useSameDocument && !usingExperimentalActivityPods())
.map(relation => relation.getLoadedModels())
.flat()
.forEach(relatedModel => relatedModel.setDocumentExists(documentExists));
Expand Down Expand Up @@ -836,7 +836,7 @@ export class SolidModel extends SolidModelBase {
Object
.values(this._relations)
.filter(isSolidMultiModelDocumentRelation)
.filter(relation => relation.enabled && relation.useSameDocument)
.filter(relation => relation.enabled && relation.useSameDocument && !usingExperimentalActivityPods())
.forEach(relation => {
relation.__modelsInSameDocument = relation.__modelsInSameDocument || [];
relation.__modelsInSameDocument.push(...relation.__newModels);
Expand All @@ -847,7 +847,13 @@ export class SolidModel extends SolidModelBase {
Object
.values(this._relations)
.filter(isSolidSingleModelDocumentRelation)
.filter(relation => relation.enabled && relation.useSameDocument && !!relation.__newModel)
.filter(
relation =>
relation.useSameDocument &&
relation.enabled &&
!!relation.__newModel &&
!usingExperimentalActivityPods(),
)
.forEach(relation => {
relation.__modelInSameDocument = relation.__newModel;

Expand Down Expand Up @@ -1039,7 +1045,7 @@ export class SolidModel extends SolidModelBase {
!relation.enabled ||
!relation.loaded ||
(!isDocumentContainsManyRelation && !isSolidDocumentRelation(relation)) ||
(isSolidDocumentRelation(relation) && !relation.useSameDocument)
(isSolidDocumentRelation(relation) && (!relation.useSameDocument || usingExperimentalActivityPods()))
) {
continue;
}
Expand Down Expand Up @@ -1154,13 +1160,13 @@ export class SolidModel extends SolidModelBase {

return this
.hasOne(metadataModelClass, 'resourceUrl')
.usingSameDocument(true)
.usingSameDocument(!usingExperimentalActivityPods())
.onDelete('cascade');
}

public operationsRelationship(): Relation {
return (new OperationsRelation(this))
.usingSameDocument(true)
.usingSameDocument(!usingExperimentalActivityPods())
.onDelete('cascade');
}

Expand Down Expand Up @@ -1265,8 +1271,9 @@ export class SolidModel extends SolidModelBase {

await super.beforeSave();

if (!this.url && this.static('mintsUrls'))
if (!this.url && this.static('mintsUrls') && !usingExperimentalActivityPods()) {
this.mintUrl();
}

if (ignoreRelations)
return;
Expand All @@ -1288,8 +1295,9 @@ export class SolidModel extends SolidModelBase {
};

while (hasUnprocessedModels()) {
if (this.static('mintsUrls'))
if (this.static('mintsUrls') && !usingExperimentalActivityPods()) {
this.mintDocumentModelsKeys(unprocessedModels);
}

await Promise.all(unprocessedModels.map(async model => {
if (model !== this && model.isDirty())
Expand All @@ -1309,7 +1317,12 @@ export class SolidModel extends SolidModelBase {
relation.__beforeParentCreate();
}

if (this.metadata && !this.metadata.resourceUrl && this.static('defaultResourceHash')) {
if (
this.metadata &&
!this.metadata.resourceUrl &&
this.static('defaultResourceHash') &&
!usingExperimentalActivityPods()
) {
this.metadata.resourceUrl = this.url ?? `#${this.static('defaultResourceHash')}`;
this.metadata.mintUrl(this.getDocumentUrl() || undefined, this._documentExists);
}
Expand Down Expand Up @@ -1370,7 +1383,10 @@ export class SolidModel extends SolidModelBase {

if (this.metadata && this.metadata.resourceUrl !== this.url) {
this.metadata.resourceUrl = this.url;
this.metadata.mintUrl(this.getDocumentUrl() || undefined, this._documentExists);

if (!usingExperimentalActivityPods()) {
this.metadata.mintUrl(this.getDocumentUrl() || undefined, this._documentExists);
}
}

await Promise.all(this.getDirtyDocumentModels().map(model => model.afterSave(true)));
Expand Down Expand Up @@ -1399,8 +1415,10 @@ export class SolidModel extends SolidModelBase {
});
}

this.metadata?.mintUrl(documentUrl, documentExists);
this.operations?.map(operation => operation.mintUrl(documentUrl, documentExists));
if (!usingExperimentalActivityPods()) {
this.metadata?.mintUrl(documentUrl, documentExists);
this.operations?.map(operation => operation.mintUrl(documentUrl, documentExists));
}
}

protected async syncDirty(): Promise<string> {
Expand Down Expand Up @@ -1429,7 +1447,11 @@ export class SolidModel extends SolidModelBase {
const documentUrl = await createDocument();

return this.getSerializedPrimaryKey()
?? (defaultResourceHash ? `${documentUrl}#${defaultResourceHash}` : documentUrl);
?? (
(defaultResourceHash && !usingExperimentalActivityPods())
? `${documentUrl}#${defaultResourceHash}`
: documentUrl
);
}

if (!this._exists) {
Expand Down
11 changes: 11 additions & 0 deletions src/models/SolidTypeIndex.schema.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { FieldType } from 'soukai';

import { defineSolidModelSchema } from '@/models/schema';

export default defineSolidModelSchema({
rdfContext: 'http://www.w3.org/ns/solid/terms#',
rdfsClass: 'TypeIndex',
timestamps: false,
fields: {
registrationUrls: {
type: FieldType.Array,
items: FieldType.Key,

// Note: This term is actually missing from the vocab, but it is used in ActivityPods.
rdfProperty: 'hasTypeRegistration',
},
},
});
7 changes: 7 additions & 0 deletions src/models/SolidTypeIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import DocumentContainsManyRelation from './relations/DocumentContainsManyRelati
import Model from './SolidTypeIndex.schema';
import SolidContainer from './SolidContainer';
import SolidTypeRegistration from './SolidTypeRegistration';
import { usingExperimentalActivityPods } from '@/experimental';

export default class SolidTypeIndex extends Model {

Expand Down Expand Up @@ -40,13 +41,19 @@ export default class SolidTypeIndex extends Model {
>;

public registrationsRelationship(): Relation {
if (usingExperimentalActivityPods()) {
return this.belongsToMany(SolidTypeRegistration, 'registrationUrls');
}

return new DocumentContainsManyRelation(this, SolidTypeRegistration);
}

public async findContainer<T extends SolidContainer = SolidContainer>(
modelClass: SolidModelConstructor,
containerClass?: SolidModelConstructor<T>,
): Promise<T | null> {
await this.loadRelationIfUnloaded('registrations');

const containerRegistrations = this.registrations.filter(registration => {
return registration.instanceContainer && arrayEquals(registration.forClass, modelClass.rdfsClasses);
});
Expand Down
3 changes: 2 additions & 1 deletion src/models/internals/JsonLDModelSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { JsonLD } from '@noeldemartin/solid-utils';

import { inferFieldDefinition } from '@/models/fields';
import { isSolidDocumentRelation, isSolidHasRelation } from '@/models/relations/guards';
import { usingExperimentalActivityPods } from '@/experimental';
import type { SolidBootedFieldDefinition, SolidBootedFieldsDefinition } from '@/models/fields';
import type { SolidModel } from '@/models/SolidModel';
import type { SolidRelation } from '@/models/relations/inference';
Expand Down Expand Up @@ -151,7 +152,7 @@ export default class JsonLDModelSerializer {
if (!(model.static('primaryKey') in attributes) && options.includeAnonymousHashes) {
const defaultResourceHash = model.static('defaultResourceHash');

if (defaultResourceHash) {
if (defaultResourceHash && !usingExperimentalActivityPods()) {
jsonld['@id'] = `#${defaultResourceHash}`;
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/models/relations/SolidContainsRelation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Attributes } from 'soukai';

import { SolidEngine } from '@/engines/SolidEngine';

import { usingExperimentalActivityPods } from '@/experimental';
import type SolidContainer from '@/models/SolidContainer';
import type { SolidModel } from '@/models/SolidModel';
import type { SolidModelConstructor } from '@/models/inference';
Expand Down Expand Up @@ -52,7 +53,7 @@ export default class SolidContainsRelation<

const related = this.relatedClass.newInstance(attributes);

if (!related.url && related.static('mintsUrls')) {
if (!related.url && related.static('mintsUrls') && !usingExperimentalActivityPods()) {
this.relatedClass.withCollection(this.parent.url, () => related.mintUrl());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Attributes, Key } from 'soukai';
import type { ClosureArgs } from '@noeldemartin/utils';

import { isSolidBelongsToRelation, isSolidHasRelation } from '@/models/relations/guards';
import { usingExperimentalActivityPods } from '@/experimental';
import type { SolidModel } from '@/models/SolidModel';
import type { SolidModelConstructor } from '@/models/inference';

Expand Down Expand Up @@ -92,10 +93,13 @@ export default class SolidMultiModelDocumentRelation<
this.protectedSolidMulti.assertLoaded('save');
this.attach(model);

if (!this.useSameDocument)
if (!this.useSameDocument || usingExperimentalActivityPods()) {
await model.save();
else if (this.parent.exists())

this.setForeignAttributes(model);
} else if (this.parent.exists()) {
await this.parent.save();
}

return model;
}
Expand Down
Loading

0 comments on commit c8e5162

Please sign in to comment.