mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
fix: Allow overwriting and deleting root container in SparqlDataAccessor
This commit is contained in:
parent
13061e35b9
commit
fc8540f553
@ -113,7 +113,7 @@ export class SparqlDataAccessor implements DataAccessor {
|
|||||||
*/
|
*/
|
||||||
public async writeContainer(identifier: ResourceIdentifier, metadata: RepresentationMetadata): Promise<void> {
|
public async writeContainer(identifier: ResourceIdentifier, metadata: RepresentationMetadata): Promise<void> {
|
||||||
const { name, parent } = this.getRelatedNames(identifier);
|
const { name, parent } = this.getRelatedNames(identifier);
|
||||||
return this.sendSparqlUpdate(this.sparqlInsert(name, parent, metadata));
|
return this.sendSparqlUpdate(this.sparqlInsert(name, metadata, parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,7 +135,7 @@ export class SparqlDataAccessor implements DataAccessor {
|
|||||||
// Not relevant since all content is triples
|
// Not relevant since all content is triples
|
||||||
metadata.removeAll(CONTENT_TYPE);
|
metadata.removeAll(CONTENT_TYPE);
|
||||||
|
|
||||||
return this.sendSparqlUpdate(this.sparqlInsert(name, parent, metadata, triples));
|
return this.sendSparqlUpdate(this.sparqlInsert(name, metadata, parent, triples));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,10 +148,17 @@ export class SparqlDataAccessor implements DataAccessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to get named nodes corresponding to the identifier and its parent container.
|
* Helper function to get named nodes corresponding to the identifier and its parent container.
|
||||||
|
* In case of a root container only the name will be returned.
|
||||||
*/
|
*/
|
||||||
private getRelatedNames(identifier: ResourceIdentifier): { name: NamedNode; parent: NamedNode } {
|
private getRelatedNames(identifier: ResourceIdentifier): { name: NamedNode; parent?: NamedNode } {
|
||||||
const parentIdentifier = this.identifierStrategy.getParentContainer(identifier);
|
|
||||||
const name = namedNode(identifier.path);
|
const name = namedNode(identifier.path);
|
||||||
|
|
||||||
|
// Root containers don't have a parent
|
||||||
|
if (this.identifierStrategy.isRootContainer(identifier)) {
|
||||||
|
return { name };
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentIdentifier = this.identifierStrategy.getParentContainer(identifier);
|
||||||
const parent = namedNode(parentIdentifier.path);
|
const parent = namedNode(parentIdentifier.path);
|
||||||
return { name, parent };
|
return { name, parent };
|
||||||
}
|
}
|
||||||
@ -219,14 +226,15 @@ export class SparqlDataAccessor implements DataAccessor {
|
|||||||
* @param metadata - New metadata of the resource.
|
* @param metadata - New metadata of the resource.
|
||||||
* @param triples - New data of the resource.
|
* @param triples - New data of the resource.
|
||||||
*/
|
*/
|
||||||
private sparqlInsert(name: NamedNode, parent: NamedNode, metadata: RepresentationMetadata, triples?: Quad[]): Update {
|
private sparqlInsert(name: NamedNode, metadata: RepresentationMetadata, parent?: NamedNode, triples?: Quad[]):
|
||||||
|
Update {
|
||||||
const metaName = this.getMetadataNode(name);
|
const metaName = this.getMetadataNode(name);
|
||||||
|
|
||||||
// Insert new metadata and containment triple
|
// Insert new metadata and containment triple
|
||||||
const insert: GraphQuads[] = [
|
const insert: GraphQuads[] = [ this.sparqlUpdateGraph(metaName, metadata.quads()) ];
|
||||||
this.sparqlUpdateGraph(metaName, metadata.quads()),
|
if (parent) {
|
||||||
this.sparqlUpdateGraph(parent, [ quad(parent, toNamedNode(LDP.contains), name) ]),
|
insert.push(this.sparqlUpdateGraph(parent, [ quad(parent, toNamedNode(LDP.contains), name) ]));
|
||||||
];
|
}
|
||||||
|
|
||||||
// Necessary updates: delete metadata and insert new data
|
// Necessary updates: delete metadata and insert new data
|
||||||
const updates: UpdateOperation[] = [
|
const updates: UpdateOperation[] = [
|
||||||
@ -256,21 +264,26 @@ export class SparqlDataAccessor implements DataAccessor {
|
|||||||
/**
|
/**
|
||||||
* Creates a query that deletes everything related to the given name.
|
* Creates a query that deletes everything related to the given name.
|
||||||
* @param name - Name of resource to delete.
|
* @param name - Name of resource to delete.
|
||||||
* @param parent - Parent of the resource to delete so containment triple can be removed.
|
* @param parent - Parent of the resource to delete so the containment triple can be removed (unless root).
|
||||||
*/
|
*/
|
||||||
private sparqlDelete(name: NamedNode, parent: NamedNode): Update {
|
private sparqlDelete(name: NamedNode, parent?: NamedNode): Update {
|
||||||
return {
|
const update: Update = {
|
||||||
updates: [
|
updates: [
|
||||||
this.sparqlUpdateDeleteAll(name),
|
this.sparqlUpdateDeleteAll(name),
|
||||||
this.sparqlUpdateDeleteAll(this.getMetadataNode(name)),
|
this.sparqlUpdateDeleteAll(this.getMetadataNode(name)),
|
||||||
{
|
|
||||||
updateType: 'delete',
|
|
||||||
delete: [ this.sparqlUpdateGraph(parent, [ quad(parent, toNamedNode(LDP.contains), name) ]) ],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
type: 'update',
|
type: 'update',
|
||||||
prefixes: {},
|
prefixes: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
update.updates.push({
|
||||||
|
updateType: 'delete',
|
||||||
|
delete: [ this.sparqlUpdateGraph(parent, [ quad(parent, toNamedNode(LDP.contains), name) ]) ],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return update;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -172,6 +172,24 @@ describe('A SparqlDataAccessor', (): void => {
|
|||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not write containment triples when writing to a root container.', async(): Promise<void> => {
|
||||||
|
metadata = new RepresentationMetadata({ path: 'http://test.com/' },
|
||||||
|
{ [RDF.type]: [ toNamedNode(LDP.Resource), toNamedNode(LDP.Container) ]});
|
||||||
|
await expect(accessor.writeContainer({ path: 'http://test.com/' }, metadata)).resolves.toBeUndefined();
|
||||||
|
|
||||||
|
expect(fetchUpdate).toHaveBeenCalledTimes(1);
|
||||||
|
expect(fetchUpdate.mock.calls[0][0]).toBe(endpoint);
|
||||||
|
expect(simplifyQuery(fetchUpdate.mock.calls[0][1])).toBe(simplifyQuery([
|
||||||
|
'DELETE WHERE { GRAPH <meta:http://test.com/> { ?s ?p ?o. } };',
|
||||||
|
'INSERT DATA {',
|
||||||
|
' GRAPH <meta:http://test.com/> {',
|
||||||
|
' <http://test.com/> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/ldp#Resource>.',
|
||||||
|
' <http://test.com/> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/ldp#Container>.',
|
||||||
|
' }',
|
||||||
|
'}',
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
|
||||||
it('overwrites the data and metadata when writing a resource and updates parent.', async(): Promise<void> => {
|
it('overwrites the data and metadata when writing a resource and updates parent.', async(): Promise<void> => {
|
||||||
metadata = new RepresentationMetadata({ path: 'http://test.com/container/resource' },
|
metadata = new RepresentationMetadata({ path: 'http://test.com/container/resource' },
|
||||||
{ [RDF.type]: [ toNamedNode(LDP.Resource) ]});
|
{ [RDF.type]: [ toNamedNode(LDP.Resource) ]});
|
||||||
@ -224,6 +242,19 @@ describe('A SparqlDataAccessor', (): void => {
|
|||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not try to remove containment triples when deleting a root container.', async(): Promise<void> => {
|
||||||
|
metadata = new RepresentationMetadata({ path: 'http://test.com/' },
|
||||||
|
{ [RDF.type]: [ toNamedNode(LDP.Resource), toNamedNode(LDP.Container) ]});
|
||||||
|
await expect(accessor.deleteResource({ path: 'http://test.com/' })).resolves.toBeUndefined();
|
||||||
|
|
||||||
|
expect(fetchUpdate).toHaveBeenCalledTimes(1);
|
||||||
|
expect(fetchUpdate.mock.calls[0][0]).toBe(endpoint);
|
||||||
|
expect(simplifyQuery(fetchUpdate.mock.calls[0][1])).toBe(simplifyQuery([
|
||||||
|
'DELETE WHERE { GRAPH <http://test.com/> { ?s ?p ?o. } };',
|
||||||
|
'DELETE WHERE { GRAPH <meta:http://test.com/> { ?s ?p ?o. } }',
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
|
||||||
it('errors when trying to write to a metadata document.', async(): Promise<void> => {
|
it('errors when trying to write to a metadata document.', async(): Promise<void> => {
|
||||||
await expect(accessor.writeDocument({ path: 'meta:http://test.com/container/resource' }, data, metadata))
|
await expect(accessor.writeDocument({ path: 'meta:http://test.com/container/resource' }, data, metadata))
|
||||||
.rejects.toThrow(new ConflictHttpError('Not allowed to create NamedNodes with the metadata extension.'));
|
.rejects.toThrow(new ConflictHttpError('Not allowed to create NamedNodes with the metadata extension.'));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user