Skip to content

Commit ef10e12

Browse files
authored
fix(hydra): handle owl:Nothing returns from operations with output: false (#167)
When a Post operation has output: false, API Platform's Hydra documentation sets hydra:returns to owl:Nothing. The findRelatedClass function's Strategy 2 tried to look up this class and threw because it's not a supported class. Wrap the findSupportedClass call in Strategy 2 with try/catch (matching Strategy 3's pattern) so it falls through to other strategies when a class like owl:Nothing is not found. Closes #163
1 parent 13315a1 commit ef10e12

File tree

2 files changed

+199
-1
lines changed

2 files changed

+199
-1
lines changed

src/hydra/parseHydraDocumentation.test.ts

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1981,3 +1981,197 @@ test("parse a Hydra documentation with owl:equivalentClass without onProperty hy
19811981
expect(bookConditionResource.name).toBe("book_conditions");
19821982
expect(bookConditionResource.title).toBe("BookCondition");
19831983
});
1984+
1985+
test("parse a Hydra documentation with Post output: false (owl:Nothing returns)", async () => {
1986+
const owlNothingEntrypoint = {
1987+
"@context": {
1988+
"@vocab": "http://localhost/docs.jsonld#",
1989+
hydra: "http://www.w3.org/ns/hydra/core#",
1990+
book: {
1991+
"@id": "Entrypoint/book",
1992+
"@type": "@id",
1993+
},
1994+
},
1995+
"@id": "/",
1996+
"@type": "Entrypoint",
1997+
book: "/books",
1998+
};
1999+
2000+
const owlNothingDocs = {
2001+
"@context": {
2002+
"@vocab": "http://localhost/docs.jsonld#",
2003+
hydra: "http://www.w3.org/ns/hydra/core#",
2004+
rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
2005+
rdfs: "http://www.w3.org/2000/01/rdf-schema#",
2006+
xmls: "http://www.w3.org/2001/XMLSchema#",
2007+
owl: "http://www.w3.org/2002/07/owl#",
2008+
domain: {
2009+
"@id": "rdfs:domain",
2010+
"@type": "@id",
2011+
},
2012+
range: {
2013+
"@id": "rdfs:range",
2014+
"@type": "@id",
2015+
},
2016+
subClassOf: {
2017+
"@id": "rdfs:subClassOf",
2018+
"@type": "@id",
2019+
},
2020+
expects: {
2021+
"@id": "hydra:expects",
2022+
"@type": "@id",
2023+
},
2024+
returns: {
2025+
"@id": "hydra:returns",
2026+
"@type": "@id",
2027+
},
2028+
},
2029+
"@id": "/docs.jsonld",
2030+
"hydra:title": "API with owl:Nothing",
2031+
"hydra:description": "An API with Post output: false",
2032+
"hydra:entrypoint": "/",
2033+
"hydra:supportedClass": [
2034+
{
2035+
"@id": "http://schema.org/Book",
2036+
"@type": "hydra:Class",
2037+
"rdfs:label": "Book",
2038+
"hydra:title": "Book",
2039+
"hydra:supportedProperty": [
2040+
{
2041+
"@type": "hydra:SupportedProperty",
2042+
"hydra:property": {
2043+
"@id": "http://schema.org/name",
2044+
"@type": "rdf:Property",
2045+
"rdfs:label": "name",
2046+
domain: "http://schema.org/Book",
2047+
range: "xmls:string",
2048+
},
2049+
"hydra:title": "name",
2050+
"hydra:required": true,
2051+
"hydra:readable": true,
2052+
"hydra:writeable": true,
2053+
},
2054+
],
2055+
"hydra:supportedOperation": [
2056+
{
2057+
"@type": "hydra:Operation",
2058+
"hydra:method": "GET",
2059+
"hydra:title": "Retrieves Book resource.",
2060+
"rdfs:label": "Retrieves Book resource.",
2061+
returns: "http://schema.org/Book",
2062+
},
2063+
{
2064+
"@type": "hydra:Operation",
2065+
"hydra:method": "DELETE",
2066+
"hydra:title": "Deletes the Book resource.",
2067+
"rdfs:label": "Deletes the Book resource.",
2068+
returns: "owl:Nothing",
2069+
},
2070+
],
2071+
},
2072+
{
2073+
"@id": "#Entrypoint",
2074+
"@type": "hydra:Class",
2075+
"hydra:title": "The API entrypoint",
2076+
"hydra:supportedProperty": [
2077+
{
2078+
"@type": "hydra:SupportedProperty",
2079+
"hydra:property": {
2080+
"@id": "#Entrypoint/book",
2081+
"@type": "hydra:Link",
2082+
domain: "#Entrypoint",
2083+
"rdfs:label": "The collection of Book resources",
2084+
"rdfs:range": [
2085+
{ "@id": "hydra:PagedCollection" },
2086+
{
2087+
"owl:equivalentClass": {
2088+
"owl:onProperty": { "@id": "hydra:member" },
2089+
"owl:allValuesFrom": {
2090+
"@id": "http://schema.org/Book",
2091+
},
2092+
},
2093+
},
2094+
],
2095+
"hydra:supportedOperation": [
2096+
{
2097+
"@type": "hydra:Operation",
2098+
"hydra:method": "GET",
2099+
"hydra:title": "Retrieves the collection of Book resources.",
2100+
"rdfs:label": "Retrieves the collection of Book resources.",
2101+
returns: "hydra:PagedCollection",
2102+
},
2103+
{
2104+
"@type": "hydra:CreateResourceOperation",
2105+
expects: "http://schema.org/Book",
2106+
"hydra:method": "POST",
2107+
"hydra:title": "Creates a Book resource.",
2108+
"rdfs:label": "Creates a Book resource.",
2109+
returns: "owl:Nothing",
2110+
},
2111+
],
2112+
},
2113+
"hydra:title": "The collection of Book resources",
2114+
"hydra:readable": true,
2115+
"hydra:writeable": false,
2116+
},
2117+
],
2118+
"hydra:supportedOperation": {
2119+
"@type": "hydra:Operation",
2120+
"hydra:method": "GET",
2121+
"rdfs:label": "The API entrypoint.",
2122+
returns: "#EntryPoint",
2123+
},
2124+
},
2125+
{
2126+
"@id": "#ConstraintViolation",
2127+
"@type": "hydra:Class",
2128+
"hydra:title": "A constraint violation",
2129+
"hydra:supportedProperty": [],
2130+
},
2131+
{
2132+
"@id": "#ConstraintViolationList",
2133+
"@type": "hydra:Class",
2134+
subClassOf: "hydra:Error",
2135+
"hydra:title": "A constraint violation list",
2136+
"hydra:supportedProperty": [],
2137+
},
2138+
],
2139+
};
2140+
2141+
server.use(
2142+
http.get("http://localhost", () =>
2143+
Response.json(owlNothingEntrypoint, init),
2144+
),
2145+
http.get("http://localhost/docs.jsonld", () =>
2146+
Response.json(owlNothingDocs, init),
2147+
),
2148+
);
2149+
2150+
const data = await parseHydraDocumentation("http://localhost");
2151+
expect(data.status).toBe(200);
2152+
2153+
const bookResource = data.api.resources?.find(
2154+
(r) => r.id === "http://schema.org/Book",
2155+
);
2156+
2157+
expect(bookResource).toBeDefined();
2158+
assert(bookResource !== undefined);
2159+
expect(bookResource.name).toBe("books");
2160+
expect(bookResource.title).toBe("Book");
2161+
2162+
// Verify fields parsed correctly
2163+
assert(bookResource.fields !== null);
2164+
assert(bookResource.fields !== undefined);
2165+
expect(bookResource.fields).toHaveLength(1);
2166+
expect(bookResource.fields[0]?.name).toBe("name");
2167+
2168+
// Verify operations include both collection and item operations
2169+
assert(bookResource.operations !== null);
2170+
assert(bookResource.operations !== undefined);
2171+
2172+
// The POST operation with owl:Nothing returns should still be listed
2173+
const postOp = bookResource.operations.find((op) => op.method === "POST");
2174+
expect(postOp).toBeDefined();
2175+
assert(postOp !== undefined);
2176+
expect(postOp.returns).toBe("http://www.w3.org/2002/07/owl#Nothing");
2177+
});

src/hydra/parseHydraDocumentation.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,11 @@ function findRelatedClass(
229229
typeof returns === "string" &&
230230
returns.indexOf("http://www.w3.org/ns/hydra/core") !== 0
231231
) {
232-
return findSupportedClass(docs, returns);
232+
try {
233+
return findSupportedClass(docs, returns);
234+
} catch {
235+
continue;
236+
}
233237
}
234238
}
235239

0 commit comments

Comments
 (0)