fix: Support empty PATCH requests

This commit is contained in:
Joachim Van Herwegen 2021-06-10 16:09:53 +02:00
parent a6371b0735
commit ded263a81d
6 changed files with 139 additions and 10 deletions

109
package-lock.json generated
View File

@ -1432,6 +1432,29 @@
"asynciterator": "^3.1.0",
"immutable": "^3.8.2",
"sparqlalgebrajs": "^2.4.0"
},
"dependencies": {
"sparqlalgebrajs": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/sparqlalgebrajs/-/sparqlalgebrajs-2.5.5.tgz",
"integrity": "sha512-sG9XI5311mS+JPDaeZUwtwYaYDRiTZDzxtHVS1GSrnfcZ2aiK1fa1PX9z16l7dtS35X3z1j1qyHEElzZO5OM3A==",
"requires": {
"fast-deep-equal": "^3.1.3",
"minimist": "^1.2.5",
"rdf-data-factory": "^1.0.4",
"rdf-isomorphic": "^1.2.0",
"rdf-string": "^1.5.0",
"sparqljs": "^3.3.0"
}
},
"sparqljs": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/sparqljs/-/sparqljs-3.4.2.tgz",
"integrity": "sha512-MmmZ6cMuvhf4Eh2FXX21dalgADUiZ9WN8XKMedwhTFg0r7W09/o8wvoZ8C4yA6FptnjjAjm+mGnxAEpkSRY3QQ==",
"requires": {
"rdf-data-factory": "^1.0.4"
}
}
}
},
"@dabh/diagnostics": {
@ -2368,6 +2391,14 @@
"resolved": "https://registry.npmjs.org/@rdfjs/to-ntriples/-/to-ntriples-1.0.2.tgz",
"integrity": "sha512-ngw5XAaGHjgGiwWWBPGlfdCclHftonmbje5lMys4G2j4NvfExraPIuRZgjSnd5lg4dnulRVUll8tRbgKO+7EDA=="
},
"@rdfjs/types": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.0.1.tgz",
"integrity": "sha512-YxVkH0XrCNG3MWeZxfg596GFe+oorTVusmNxRP6ZHTsGczZ8AGvG3UchRNkg3Fy4MyysI7vBAA5YZbESL+VmHQ==",
"requires": {
"@types/node": "*"
}
},
"@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@ -6569,6 +6600,15 @@
"integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==",
"dev": true
},
"hash.js": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
"requires": {
"inherits": "^2.0.3",
"minimalistic-assert": "^1.0.1"
}
},
"hosted-git-info": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
@ -9628,6 +9668,11 @@
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
"dev": true
},
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@ -10769,6 +10814,14 @@
"rdf-terms": "^1.6.2"
}
},
"rdf-js": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/rdf-js/-/rdf-js-4.0.2.tgz",
"integrity": "sha512-ApvlFa/WsQh8LpPK/6hctQwG06Z9ztQQGWVtrcrf9L6+sejHNXLPOqL+w7q3hF+iL0C4sv3AX1PUtGkLNzyZ0Q==",
"requires": {
"@rdfjs/types": "*"
}
},
"rdf-literal": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/rdf-literal/-/rdf-literal-1.2.0.tgz",
@ -11505,22 +11558,62 @@
}
},
"sparqlalgebrajs": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/sparqlalgebrajs/-/sparqlalgebrajs-2.5.1.tgz",
"integrity": "sha512-J682nUANGeaaJexN5gcjw7fS507Vxoiz78wIRgFZ2Zv0nu4ObPZQDPgzYK01V0/FXd7sboXkWiHyG8BUeXnimQ==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/sparqlalgebrajs/-/sparqlalgebrajs-3.0.0.tgz",
"integrity": "sha512-3QbDE78l170TiyzzTNkW4ug0p9rCRpNHK5vTN67O9LO2yrwnqI3v3Hp+8RW7ZQZAswZCPQ3T3o8TFDR6s3AZYA==",
"requires": {
"@types/minimist": "^1.2.1",
"@types/node": "^15.12.2",
"@types/rdf-js": "^4.0.2",
"@types/sparqljs": "^3.1.2",
"fast-deep-equal": "^3.1.3",
"minimist": "^1.2.5",
"rdf-data-factory": "^1.0.4",
"rdf-isomorphic": "^1.2.0",
"rdf-isomorphic": "^1.2.1",
"rdf-string": "^1.5.0",
"sparqljs": "^3.3.0"
"sparqljs": "^3.4.2"
},
"dependencies": {
"@types/minimist": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz",
"integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg=="
},
"@types/node": {
"version": "15.12.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz",
"integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww=="
},
"@types/rdf-js": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/rdf-js/-/rdf-js-4.0.2.tgz",
"integrity": "sha512-soR/+RMogGiDU1lrpuQl5ZL55/L1eq/JlR2dWx052Uh/RYs9okh3XZHFlIJXHZqjqyjEn4WdbOMfBj7vvc2WVQ==",
"requires": {
"rdf-js": "*"
}
},
"@types/sparqljs": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@types/sparqljs/-/sparqljs-3.1.2.tgz",
"integrity": "sha512-tLfrnBuK37P2Bn8Fo7Qik95sBXYHw5D+gq3MMq1HVyoTpCWivwPnP0Mmd7Apamdc9eH3mLJwIZIETHCQ6HxMUw==",
"requires": {
"rdf-js": "^4.0.2"
}
},
"rdf-isomorphic": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/rdf-isomorphic/-/rdf-isomorphic-1.2.1.tgz",
"integrity": "sha512-kIKlQYoizNqp8zhbca1zV3mYngisoD/KNt/xBRjagp7R3F8niI3b1vxvqcWlSkNXgPD6MsXpP2E/uXZ8oGTIcA==",
"requires": {
"hash.js": "^1.1.7",
"rdf-string": "^1.5.0",
"rdf-terms": "^1.6.2"
}
},
"sparqljs": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/sparqljs/-/sparqljs-3.3.0.tgz",
"integrity": "sha512-2WnOX1b72pmf8yHyhoaPKnqqCBMn22qa59H/aiIruQ4lkv9ngaSYGOos5ZqW3o6AcUvCj/ObLQw4RnfQKXRdww==",
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/sparqljs/-/sparqljs-3.4.2.tgz",
"integrity": "sha512-MmmZ6cMuvhf4Eh2FXX21dalgADUiZ9WN8XKMedwhTFg0r7W09/o8wvoZ8C4yA6FptnjjAjm+mGnxAEpkSRY3QQ==",
"requires": {
"rdf-data-factory": "^1.0.4"
}

View File

@ -122,7 +122,7 @@
"rdf-terms": "^1.5.1",
"redis": "^3.0.2",
"redlock": "^4.2.0",
"sparqlalgebrajs": "^2.5.1",
"sparqlalgebrajs": "^3.0.0",
"sparqljs": "^3.1.2",
"streamify-array": "^1.0.1",
"url-join": "^4.0.1",

View File

@ -44,7 +44,7 @@ export class SparqlPatchPermissionsExtractor extends PermissionsExtractor {
}
private isSupported(op: Algebra.Operation): boolean {
if (op.type === Algebra.types.DELETE_INSERT) {
if (this.isDeleteInsert(op) || this.isNop(op)) {
return true;
}
if (op.type === Algebra.types.COMPOSITE_UPDATE) {
@ -57,7 +57,14 @@ export class SparqlPatchPermissionsExtractor extends PermissionsExtractor {
return op.type === Algebra.types.DELETE_INSERT;
}
private isNop(op: Algebra.Operation): op is Algebra.Nop {
return op.type === Algebra.types.NOP;
}
private needsAppend(update: Algebra.Operation): boolean {
if (this.isNop(update)) {
return false;
}
if (this.isDeleteInsert(update)) {
return Boolean(update.insert && update.insert.length > 0);
}
@ -66,6 +73,9 @@ export class SparqlPatchPermissionsExtractor extends PermissionsExtractor {
}
private needsWrite(update: Algebra.Operation): boolean {
if (this.isNop(update)) {
return false;
}
if (this.isDeleteInsert(update)) {
return Boolean(update.delete && update.delete.length > 0);
}

View File

@ -47,6 +47,12 @@ export class SparqlUpdatePatchHandler extends PatchHandler {
// Verify the patch
const { source, identifier, patch } = input;
const op = (patch as SparqlUpdatePatch).algebra;
// In case of a NOP we can skip everything
if (op.type === Algebra.types.NOP) {
return [];
}
this.validateUpdate(op);
return this.applyPatch(source, identifier, op);

View File

@ -32,6 +32,19 @@ describe('A SparqlPatchPermissionsExtractor', (): void => {
await expect(result).rejects.toThrow('Can only determine permissions of a PATCH with DELETE/INSERT operations.');
});
it('requires nothing for NOP operations.', async(): Promise<void> => {
const operation = {
method: 'PATCH',
body: { algebra: factory.createNop() },
} as unknown as Operation;
await expect(extractor.handle(operation)).resolves.toEqual({
read: false,
append: false,
write: false,
control: false,
});
});
it('requires append for INSERT operations.', async(): Promise<void> => {
const operation = {
method: 'PATCH',

View File

@ -93,6 +93,13 @@ describe('A SparqlUpdatePatchHandler', (): void => {
await expect(handler.canHandle(input)).rejects.toThrow(NotImplementedHttpError);
});
it('handles NOP operations by not doing anything.', async(): Promise<void> => {
await handle('');
expect(source.getRepresentation).toHaveBeenCalledTimes(0);
expect(converter.handleSafe).toHaveBeenCalledTimes(0);
expect(source.setRepresentation).toHaveBeenCalledTimes(0);
});
it('handles INSERT DATA updates.', async(): Promise<void> => {
await handle(fullfilledDataInsert);
expect(await basicChecks(startQuads.concat(