diff --git a/src/Operation.js b/src/Operation.js new file mode 100644 index 0000000..30619f0 --- /dev/null +++ b/src/Operation.js @@ -0,0 +1,31 @@ +// @flow + +type OperationOptions = { + method?: string, + returns?: string, + types?: Array, +} + +/** + * @property {string} name - The name of this operation + */ +export default class Operation { + name: string; + + /** + * @param {string} name + * @param {?OperationOptions} options + */ + constructor(name: string, options: OperationOptions = {}) { + this.name = name; + + Object.keys(options).forEach((key) => { + Object.defineProperty(this, key, { + readable: true, + writable: true, + enumerable: true, + value: options[key], + }); + }); + } +} diff --git a/src/Resource.js b/src/Resource.js index 8084700..9b846d8 100644 --- a/src/Resource.js +++ b/src/Resource.js @@ -1,12 +1,14 @@ // @flow import Field from './Field'; +import Operation from './Operation'; type ResourceOptions = { id?: string, title?: string, readableFields?: Field[], writableFields?: Field[], + operations?: Operation[], }; /** diff --git a/src/hydra/parseHydraDocumentation.js b/src/hydra/parseHydraDocumentation.js index 28f114b..79862c8 100644 --- a/src/hydra/parseHydraDocumentation.js +++ b/src/hydra/parseHydraDocumentation.js @@ -3,6 +3,7 @@ import get from 'lodash.get'; import Api from '../Api' import Field from '../Field' import Resource from '../Resource' +import Operation from '../Operation' import fetchJsonLd from './fetchJsonLd'; /** @@ -144,7 +145,7 @@ export default function parseHydraDocumentation(entrypointUrl, options = {}) { return fetchEntrypointAndDocs(entrypointUrl, options).then( ({ entrypoint, docs, response }) => { - const resources = [], fields = []; + const resources = [], fields = [], operations = []; const title = get(docs, '[0]["http://www.w3.org/ns/hydra/core#title"][0]["@value"]', 'API Platform'); const entrypointType = get(entrypoint, '[0]["@type"][0]'); @@ -159,7 +160,7 @@ export default function parseHydraDocumentation(entrypointUrl, options = {}) { // Add resources for (const properties of entrypointClass['http://www.w3.org/ns/hydra/core#supportedProperty']) { - const readableFields = [], resourceFields = [], writableFields = []; + const readableFields = [], resourceFields = [], writableFields = [], resourceOperations = []; const property = get(properties, '["http://www.w3.org/ns/hydra/core#property"][0]'); if (!property) { @@ -196,6 +197,50 @@ export default function parseHydraDocumentation(entrypointUrl, options = {}) { } } + // parse entrypoint's operations (a.k.a. collection operations) + if (property['http://www.w3.org/ns/hydra/core#supportedOperation']) { + for (const entrypointOperation of property['http://www.w3.org/ns/hydra/core#supportedOperation']) { + if (!entrypointOperation['http://www.w3.org/ns/hydra/core#returns']) { + continue; + } + + const range = entrypointOperation['http://www.w3.org/ns/hydra/core#returns'][0]['@id']; + const operation = new Operation( + entrypointOperation['http://www.w3.org/2000/01/rdf-schema#label'][0]['@value'], + { + method: entrypointOperation['http://www.w3.org/ns/hydra/core#method'][0]['@value'], + expects: entrypointOperation['http://www.w3.org/ns/hydra/core#expects'] && entrypointOperation['http://www.w3.org/ns/hydra/core#expects'][0]['@id'], + returns: range, + types: entrypointOperation['@type'], + }, + ); + + resourceOperations.push(operation); + operations.push(operation); + } + } + + // parse resource operations (a.k.a. item operations) + for (const supportedOperation of relatedClass['http://www.w3.org/ns/hydra/core#supportedOperation']) { + if (!supportedOperation['http://www.w3.org/ns/hydra/core#returns']) { + continue; + } + + const range = supportedOperation['http://www.w3.org/ns/hydra/core#returns'][0]['@id']; + const operation = new Operation( + supportedOperation['http://www.w3.org/2000/01/rdf-schema#label'][0]['@value'], + { + method: supportedOperation['http://www.w3.org/ns/hydra/core#method'][0]['@value'], + expects: supportedOperation['http://www.w3.org/ns/hydra/core#expects'] && supportedOperation['http://www.w3.org/ns/hydra/core#expects'][0]['@id'], + returns: range, + types: supportedOperation['@type'], + }, + ); + + resourceOperations.push(operation); + operations.push(operation); + } + const url = get(entrypoint, `[0]["${property['@id']}"][0]["@id"]`); if (!url) { throw new Error(`Unable to find the URL for "${property['@id']}".`); @@ -209,7 +254,8 @@ export default function parseHydraDocumentation(entrypointUrl, options = {}) { title: get(relatedClass, '["http://www.w3.org/ns/hydra/core#title"][0]["@value"]', ''), fields: resourceFields, readableFields, - writableFields + writableFields, + operations: resourceOperations } )); } diff --git a/src/hydra/parseHydraDocumentation.test.js b/src/hydra/parseHydraDocumentation.test.js index d1f2d35..1f68e60 100644 --- a/src/hydra/parseHydraDocumentation.test.js +++ b/src/hydra/parseHydraDocumentation.test.js @@ -612,194 +612,289 @@ const book = { "description": "The date on which the CreativeWork was created or the item was added to a DataFeed", "maxCardinality": null, } + ], + "operations": [ + { + "name": "Retrieves Book resource.", + "method": "GET", + "returns": "http://schema.org/Book", + "types": ["http://www.w3.org/ns/hydra/core#Operation"], + }, + { + "name": "Replaces the Book resource.", + "method": "PUT", + "expects": "http://schema.org/Book", + "returns": "http://schema.org/Book", + "types": ["http://www.w3.org/ns/hydra/core#ReplaceResourceOperation"], + }, + { + "name": "Deletes the Book resource.", + "method": "DELETE", + "returns": "http://www.w3.org/2002/07/owl#Nothing", + "types": ["http://www.w3.org/ns/hydra/core#Operation"], + } ] }; -const expectedApi = { - "entrypoint": "http://localhost", - "title": "API Platform's demo", - "resources": [ - book, +const review = { + "name": "reviews", + "url": "http://localhost/reviews", + "id": "http://schema.org/Review", + "title": "Review", + "fields": [ { - "name": "reviews", - "url": "http://localhost/reviews", - "id": "http://schema.org/Review", - "title": "Review", - "fields": [ - { - "name": "reviewBody", - "id": "http://schema.org/reviewBody", - "range": "http://www.w3.org/2001/XMLSchema#string", - "reference": null, - "required": false, - "description": "The actual body of the review", - "maxCardinality": null, - }, - { - "name": "rating", - "id": "http://localhost/docs.jsonld#Review/rating", - "range": "http://www.w3.org/2001/XMLSchema#integer", - "reference": null, - "required": false, - "description": "", - "maxCardinality": null, - }, - { - "name": "itemReviewed", - "id": "http://schema.org/itemReviewed", - "range": "http://schema.org/Book", - "reference": book, - "required": true, - "description": "The item that is being reviewed/rated", - "maxCardinality": 1, - } + "name": "reviewBody", + "id": "http://schema.org/reviewBody", + "range": "http://www.w3.org/2001/XMLSchema#string", + "reference": null, + "required": false, + "description": "The actual body of the review", + "maxCardinality": null, + }, + { + "name": "rating", + "id": "http://localhost/docs.jsonld#Review/rating", + "range": "http://www.w3.org/2001/XMLSchema#integer", + "reference": null, + "required": false, + "description": "", + "maxCardinality": null, + }, + { + "name": "itemReviewed", + "id": "http://schema.org/itemReviewed", + "range": "http://schema.org/Book", + "reference": book, + "required": true, + "description": "The item that is being reviewed/rated", + "maxCardinality": 1, + } + ], + "readableFields": [ + { + "name": "reviewBody", + "id": "http://schema.org/reviewBody", + "range": "http://www.w3.org/2001/XMLSchema#string", + "reference": null, + "required": false, + "description": "The actual body of the review", + "maxCardinality": null, + }, + { + "name": "rating", + "id": "http://localhost/docs.jsonld#Review/rating", + "range": "http://www.w3.org/2001/XMLSchema#integer", + "reference": null, + "required": false, + "description": "", + "maxCardinality": null, + }, + { + "name": "itemReviewed", + "id": "http://schema.org/itemReviewed", + "range": "http://schema.org/Book", + "reference": book, + "required": true, + "description": "The item that is being reviewed/rated", + "maxCardinality": 1, + } + ], + "writableFields": [ + { + "name": "reviewBody", + "id": "http://schema.org/reviewBody", + "range": "http://www.w3.org/2001/XMLSchema#string", + "reference": null, + "required": false, + "description": "The actual body of the review", + "maxCardinality": null, + }, + { + "name": "rating", + "id": "http://localhost/docs.jsonld#Review/rating", + "range": "http://www.w3.org/2001/XMLSchema#integer", + "reference": null, + "required": false, + "description": "", + "maxCardinality": null, + }, + { + "name": "itemReviewed", + "id": "http://schema.org/itemReviewed", + "range": "http://schema.org/Book", + "reference": book, + "required": true, + "description": "The item that is being reviewed/rated", + "maxCardinality": 1, + } + ], + "operations": [ + { + "name": "Retrieves the collection of Review resources.", + "method": "GET", + "returns": "http://www.w3.org/ns/hydra/core#PagedCollection", + "types": [ + "http://www.w3.org/ns/hydra/core#Operation" ], - "readableFields": [ - { - "name": "reviewBody", - "id": "http://schema.org/reviewBody", - "range": "http://www.w3.org/2001/XMLSchema#string", - "reference": null, - "required": false, - "description": "The actual body of the review", - "maxCardinality": null, - }, - { - "name": "rating", - "id": "http://localhost/docs.jsonld#Review/rating", - "range": "http://www.w3.org/2001/XMLSchema#integer", - "reference": null, - "required": false, - "description": "", - "maxCardinality": null, - }, - { - "name": "itemReviewed", - "id": "http://schema.org/itemReviewed", - "range": "http://schema.org/Book", - "reference": book, - "required": true, - "description": "The item that is being reviewed/rated", - "maxCardinality": 1, - } + }, + { + "name": "Creates a Review resource.", + "method": "POST", + "expects": "http://schema.org/Review", + "returns": "http://schema.org/Review", + "types": [ + "http://www.w3.org/ns/hydra/core#CreateResourceOperation" ], - "writableFields": [ - { - "name": "reviewBody", - "id": "http://schema.org/reviewBody", - "range": "http://www.w3.org/2001/XMLSchema#string", - "reference": null, - "required": false, - "description": "The actual body of the review", - "maxCardinality": null, - }, - { - "name": "rating", - "id": "http://localhost/docs.jsonld#Review/rating", - "range": "http://www.w3.org/2001/XMLSchema#integer", - "reference": null, - "required": false, - "description": "", - "maxCardinality": null, - }, - { - "name": "itemReviewed", - "id": "http://schema.org/itemReviewed", - "range": "http://schema.org/Book", - "reference": book, - "required": true, - "description": "The item that is being reviewed/rated", - "maxCardinality": 1, - } - ] }, { - "name": "customResources", - "url": "http://localhost/customResources", - "id": "http://localhost/docs.jsonld#CustomResource", - "title": "CustomResource", - "fields": [ - { - "name": "label", - "id": "http://localhost/docs.jsonld#CustomResource/label", - "range": "http://www.w3.org/2001/XMLSchema#string", - "reference": null, - "required": true, - "description": "", - "maxCardinality": null, - }, - { - "name": "description", - "id": "http://localhost/docs.jsonld#CustomResource/description", - "range": "http://www.w3.org/2001/XMLSchema#string", - "reference": null, - "required": true, - "description": "", - "maxCardinality": null, - }, - { - "name": "sanitizedDescription", - "id": "http://localhost/docs.jsonld#CustomResource/sanitizedDescription", - "range": null, - "reference": null, - "required": false, - "description": "", - "maxCardinality": null, - } + "name": "Retrieves Review resource.", + "method": "GET", + "returns": "http://schema.org/Review", + "types": ["http://www.w3.org/ns/hydra/core#Operation"], + }, + { + "name": "Replaces the Review resource.", + "method": "PUT", + "expects": "http://schema.org/Review", + "returns": "http://schema.org/Review", + "types": ["http://www.w3.org/ns/hydra/core#ReplaceResourceOperation"], + }, + { + "name": "Deletes the Review resource.", + "method": "DELETE", + "returns": "http://www.w3.org/2002/07/owl#Nothing", + "types": ["http://www.w3.org/ns/hydra/core#Operation"], + } + ] +}; + +const customResource = { + "name": "customResources", + "url": "http://localhost/customResources", + "id": "http://localhost/docs.jsonld#CustomResource", + "title": "CustomResource", + "fields": [ + { + "name": "label", + "id": "http://localhost/docs.jsonld#CustomResource/label", + "range": "http://www.w3.org/2001/XMLSchema#string", + "reference": null, + "required": true, + "description": "", + "maxCardinality": null, + }, + { + "name": "description", + "id": "http://localhost/docs.jsonld#CustomResource/description", + "range": "http://www.w3.org/2001/XMLSchema#string", + "reference": null, + "required": true, + "description": "", + "maxCardinality": null, + }, + { + "name": "sanitizedDescription", + "id": "http://localhost/docs.jsonld#CustomResource/sanitizedDescription", + "range": null, + "reference": null, + "required": false, + "description": "", + "maxCardinality": null, + } + ], + "readableFields": [ + { + "name": "label", + "id": "http://localhost/docs.jsonld#CustomResource/label", + "range": "http://www.w3.org/2001/XMLSchema#string", + "reference": null, + "required": true, + "description": "", + "maxCardinality": null, + }, + { + "name": "description", + "id": "http://localhost/docs.jsonld#CustomResource/description", + "range": "http://www.w3.org/2001/XMLSchema#string", + "reference": null, + "required": true, + "description": "", + "maxCardinality": null, + }, + { + "name": "sanitizedDescription", + "id": "http://localhost/docs.jsonld#CustomResource/sanitizedDescription", + "range": null, + "reference": null, + "required": false, + "description": "", + "maxCardinality": null, + } + ], + "writableFields": [ + { + "name": "label", + "id": "http://localhost/docs.jsonld#CustomResource/label", + "range": "http://www.w3.org/2001/XMLSchema#string", + "reference": null, + "required": true, + "description": "", + "maxCardinality": null, + }, + { + "name": "description", + "id": "http://localhost/docs.jsonld#CustomResource/description", + "range": "http://www.w3.org/2001/XMLSchema#string", + "reference": null, + "required": true, + "description": "", + "maxCardinality": null, + } + ], + "operations": [ + { + "name": "Retrieves the collection of custom resources.", + "method": "GET", + "returns": "http://www.w3.org/ns/hydra/core#PagedCollection", + "types": [ + "http://www.w3.org/ns/hydra/core#Operation" ], - "readableFields": [ - { - "name": "label", - "id": "http://localhost/docs.jsonld#CustomResource/label", - "range": "http://www.w3.org/2001/XMLSchema#string", - "reference": null, - "required": true, - "description": "", - "maxCardinality": null, - }, - { - "name": "description", - "id": "http://localhost/docs.jsonld#CustomResource/description", - "range": "http://www.w3.org/2001/XMLSchema#string", - "reference": null, - "required": true, - "description": "", - "maxCardinality": null, - }, - { - "name": "sanitizedDescription", - "id": "http://localhost/docs.jsonld#CustomResource/sanitizedDescription", - "range": null, - "reference": null, - "required": false, - "description": "", - "maxCardinality": null, - } + }, + { + "name": "Creates a custom resource.", + "method": "POST", + "expects": "http://localhost/docs.jsonld#CustomResource", + "returns": "http://localhost/docs.jsonld#CustomResource", + "types": [ + "http://www.w3.org/ns/hydra/core#CreateResourceOperation" ], - "writableFields": [ - { - "name": "label", - "id": "http://localhost/docs.jsonld#CustomResource/label", - "range": "http://www.w3.org/2001/XMLSchema#string", - "reference": null, - "required": true, - "description": "", - "maxCardinality": null, - }, - { - "name": "description", - "id": "http://localhost/docs.jsonld#CustomResource/description", - "range": "http://www.w3.org/2001/XMLSchema#string", - "reference": null, - "required": true, - "description": "", - "maxCardinality": null, - } - ] + }, + { + "name": "Retrieves custom resources.", + "method": "GET", + "returns": "http://localhost/docs.jsonld#CustomResource", + "types": ["http://www.w3.org/ns/hydra/core#Operation"], + }, + { + "name": "Creates a custom resource.", + "method": "POST", + "expects": "http://localhost/docs.jsonld#CustomResource", + "returns": "http://localhost/docs.jsonld#CustomResource", + "types": ["http://www.w3.org/ns/hydra/core#CreateResourceOperation"], } ] }; +const expectedApi = { + "entrypoint": "http://localhost", + "title": "API Platform's demo", + "resources": [ + book, + review, + customResource + ] +}; + const init = { status: 200, statusText: 'OK',