Skip to content

Commit 0d7959b

Browse files
fix: empty container dataset (#38)
* Add LDP container type IRIs to vocabulary * Find empty containers by type, not only by ldp:contains * Add tests for empty and non-empty containers * chore: reverted to original comment * chore: apply jesse's suggested comment
1 parent 76f150a commit 0d7959b

3 files changed

Lines changed: 64 additions & 1 deletion

File tree

src/solid/ContainerDataset.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,24 @@ import { Container } from "./Container.js"
33
import { LDP } from "../vocabulary/mod.js"
44

55
export class ContainerDataset extends DatasetWrapper {
6-
// TODO: Consider that this might be undefined if there are no contained resources. We might need different matching.
6+
77
get container(): Container | undefined {
88
// Return the first container in the dataset
99
for (const s of this.subjectsOf(LDP.contains, Container)) {
1010
return s
1111
}
1212

13+
// If the container is empty, then the only instance of `ldp:Container`
14+
// and `ldp:BasicContainer` in this dataset should be the container
15+
// itself.
16+
for (const s of this.instancesOf(LDP.Container, Container)) {
17+
return s
18+
}
19+
20+
for (const s of this.instancesOf(LDP.BasicContainer, Container)) {
21+
return s
22+
}
23+
1324
return
1425
}
1526
}

src/vocabulary/ldp.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
export const LDP = {
22
contains: "http://www.w3.org/ns/ldp#contains",
3+
Container: "http://www.w3.org/ns/ldp#Container",
4+
BasicContainer: "http://www.w3.org/ns/ldp#BasicContainer",
35
} as const;

test/unit/container.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { DataFactory, Parser, Store } from "n3";
2+
import assert from "node:assert";
3+
import { describe, it } from "node:test";
4+
import { ContainerDataset } from "@solid/object";
5+
6+
describe("ContainerDataset", () => {
7+
const emptyContainerRDF = `
8+
@prefix dc: <http://purl.org/dc/terms/>.
9+
@prefix ldp: <http://www.w3.org/ns/ldp#>.
10+
@prefix posix: <http://www.w3.org/ns/posix/stat#>.
11+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
12+
<> a ldp:Container, ldp:BasicContainer, ldp:Resource;
13+
dc:modified "2026-07-23T10:41:12.846Z"^^xsd:dateTime;
14+
posix:mtime 1784803272.
15+
`;
16+
17+
const nonEmptyContainerRDF = `
18+
@prefix ldp: <http://www.w3.org/ns/ldp#>.
19+
<https://pod.example/container/>
20+
a ldp:Container;
21+
ldp:contains <https://pod.example/container/file.txt> .
22+
<https://pod.example/container/file.txt>
23+
a ldp:Resource .
24+
`;
25+
26+
it("resolves an empty container via rdf:type when ldp:contains is absent", () => {
27+
const store = new Store();
28+
const parser = new Parser({ baseIRI: "https://pod.example/empty/" });
29+
store.addQuads(parser.parse(emptyContainerRDF));
30+
const dataset = new ContainerDataset(store, DataFactory);
31+
const container = dataset.container;
32+
assert.ok(container !== undefined);
33+
assert.equal(container.id, "https://pod.example/empty/");
34+
assert.equal(container.contains.size, 0);
35+
});
36+
37+
it("still resolves a non-empty container via ldp:contains", () => {
38+
const store = new Store();
39+
store.addQuads(new Parser().parse(nonEmptyContainerRDF));
40+
const dataset = new ContainerDataset(store, DataFactory);
41+
const container = dataset.container;
42+
assert.ok(container !== undefined);
43+
assert.equal(container.id, "https://pod.example/container/");
44+
assert.equal(container.contains.size, 1);
45+
assert.equal(
46+
container.contains.values().next().value?.id,
47+
"https://pod.example/container/file.txt",
48+
);
49+
});
50+
});

0 commit comments

Comments
 (0)