-
-
Notifications
You must be signed in to change notification settings - Fork 72
Description
Hi,
I'd like to be able to initialize the API specs from an existing cache in order to get the most recently known state of the api docs in case of offline usage.
Basically I need to be able to serialize the specs into JSON and deserialize from JSON. As far as I can see the tests currently utilize JSON.stringify()
already which is nice but in my case I'm getting issues with an API spec which has circular references internally (e.g. a Resource
is referencing another Resource
its Field.reference
property).
As the parseHydraDocumentation
is capable of creating those references properly based on the api docs I'm currently thinking that utilizing the collected response(s) for caching and instantiate a new instance of Api
with its belongings from that state.
If I see it correctly it should be sufficient to separate the "fetch" and "parse" capabilities that are currently handled implicitly in the parseHydraDocumentation
- like having one separately exported function that does only the parsing which is currently done here:
api-doc-parser/src/hydra/parseHydraDocumentation.js
Lines 196 to 442 in a9e0973
({ entrypoint, docs, response }) => { | |
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]'); | |
if (!entrypointType) { | |
throw new Error('The API entrypoint has no "@type" key.'); | |
} | |
const entrypointClass = findSupportedClass(docs, entrypointType); | |
if ( | |
!Array.isArray( | |
entrypointClass["http://www.w3.org/ns/hydra/core#supportedProperty"] | |
) | |
) { | |
throw new Error( | |
'The entrypoint definition has no "http://www.w3.org/ns/hydra/core#supportedProperty" key or it is not an array.' | |
); | |
} | |
// Add resources | |
for (const properties of entrypointClass[ | |
"http://www.w3.org/ns/hydra/core#supportedProperty" | |
]) { | |
const readableFields = [], | |
resourceFields = [], | |
writableFields = [], | |
resourceOperations = []; | |
const property = get( | |
properties, | |
'["http://www.w3.org/ns/hydra/core#property"][0]' | |
); | |
if (!property) { | |
continue; | |
} | |
// Add fields | |
const relatedClass = findRelatedClass(docs, property); | |
for (const supportedProperties of relatedClass[ | |
"http://www.w3.org/ns/hydra/core#supportedProperty" | |
]) { | |
const supportedProperty = get( | |
supportedProperties, | |
'["http://www.w3.org/ns/hydra/core#property"][0]' | |
); | |
const range = get( | |
supportedProperty, | |
'["http://www.w3.org/2000/01/rdf-schema#range"][0]["@id"]', | |
null | |
); | |
const field = new Field( | |
supportedProperty[ | |
"http://www.w3.org/2000/01/rdf-schema#label" | |
][0]["@value"], | |
{ | |
id: supportedProperty["@id"], | |
range: range, | |
reference: | |
"http://www.w3.org/ns/hydra/core#Link" === | |
get(property, '["@type"][0]') | |
? range | |
: null, // Will be updated in a subsequent pass | |
required: get( | |
supportedProperties, | |
'["http://www.w3.org/ns/hydra/core#required"][0]["@value"]', | |
false | |
), | |
description: get( | |
supportedProperties, | |
'["http://www.w3.org/ns/hydra/core#description"][0]["@value"]', | |
"" | |
), | |
maxCardinality: get( | |
supportedProperty, | |
'["http://www.w3.org/2002/07/owl#maxCardinality"][0]["@value"]', | |
null | |
), | |
deprecated: get( | |
supportedProperties, | |
'["http://www.w3.org/2002/07/owl#deprecated"][0]["@value"]', | |
false | |
) | |
} | |
); | |
fields.push(field); | |
resourceFields.push(field); | |
if ( | |
get( | |
supportedProperties, | |
'["http://www.w3.org/ns/hydra/core#readable"][0]["@value"]' | |
) | |
) { | |
readableFields.push(field); | |
} | |
if ( | |
get( | |
supportedProperties, | |
'["http://www.w3.org/ns/hydra/core#writable"][0]["@value"]' | |
) | |
) { | |
writableFields.push(field); | |
} | |
} | |
// 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"], | |
deprecated: get( | |
entrypointOperation, | |
'["http://www.w3.org/2002/07/owl#deprecated"][0]["@value"]', | |
false | |
) | |
} | |
); | |
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"], | |
deprecated: get( | |
supportedOperation, | |
'["http://www.w3.org/2002/07/owl#deprecated"][0]["@value"]', | |
false | |
) | |
} | |
); | |
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"]}".`); | |
} | |
resources.push( | |
new Resource(guessNameFromUrl(url, entrypointUrl), url, { | |
id: relatedClass["@id"], | |
title: get( | |
relatedClass, | |
'["http://www.w3.org/ns/hydra/core#title"][0]["@value"]', | |
"" | |
), | |
fields: resourceFields, | |
readableFields, | |
writableFields, | |
operations: resourceOperations, | |
deprecated: get( | |
relatedClass, | |
'["http://www.w3.org/2002/07/owl#deprecated"][0]["@value"]', | |
false | |
), | |
parameters: [] | |
}) | |
); | |
} | |
// Resolve references | |
for (const field of fields) { | |
if (null !== field.reference) { | |
field.reference = | |
resources.find(resource => resource.id === field.reference) || | |
null; | |
} | |
} | |
return Promise.resolve({ | |
api: new Api(entrypointUrl, { title, resources }), | |
response, | |
status: response.status | |
}); | |
}, |
I could try to come up with a PR for this but I'd like to get some opinions on that first.
Best