Skip to content

Commit f8fa704

Browse files
authored
fix: loading of examples (#92)
* fix: loading of examples * fix: test in GHA
1 parent 6f55b71 commit f8fa704

File tree

6 files changed

+66
-41
lines changed

6 files changed

+66
-41
lines changed

.github/workflows/test.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ jobs:
1313
runs-on: "depot-ubuntu-24.04-small"
1414
steps:
1515
- uses: "actions/checkout@v4"
16+
with:
17+
submodules: true
1618
- uses: "authzed/action-spicedb@v1"
1719
with:
1820
version: "latest"

examples

Submodule examples updated 46 files

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
"clsx": "^2.1.1",
3434
"d3-scale-chromatic": "^2.0.0",
3535
"dequal": "^2.0.2",
36-
"es-toolkit": "^1.36.0",
3736
"file-saver": "^2.0.5",
3837
"file-select-dialog": "^1.5.4",
3938
"line-column": "^1.0.2",
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, it, expect } from "vitest";
2+
import { LoadExamples } from "./examples";
3+
4+
describe("LoadExamples", () => {
5+
it("should correctly parse and return examples and skip those that are missing files", () => {
6+
const examples = LoadExamples();
7+
8+
expect(examples).toHaveLength(6);
9+
10+
examples.forEach(function (example) {
11+
expect(example.id).not.toBeNull();
12+
expect(example.title).not.toBeNull();
13+
expect(example.subtitle).not.toBeNull();
14+
expect(example.data).not.toBeNull();
15+
expect(example.data.schema).not.toBeNull();
16+
expect(example.data.relationships).not.toBeNull();
17+
expect(example.data.assertions).not.toBeNull();
18+
});
19+
});
20+
});

src/spicedb-common/examples.ts

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,71 @@
11
import yaml from "yaml";
2-
import { sortBy, zip } from "es-toolkit";
32
import { ParsedValidation } from "./validationfileformat";
43
export interface Example {
54
id: string;
65
title: string;
7-
documentation: string;
86
subtitle: string;
97
data: ParsedValidation;
108
}
119

1210
const readmesImports = import.meta.glob("/examples/schemas/*/README.md", {
1311
// Get just the raw text
1412
query: "?raw",
13+
eager: true,
1514
});
1615
const schemasImports = import.meta.glob(
1716
"/examples/schemas/*/schema-and-data.yaml",
1817
{
1918
// Get just the raw text
2019
query: "?raw",
20+
eager: true,
2121
},
2222
);
2323

2424
const isTextImport = (input: unknown): input is { default: string } => {
2525
return typeof input === "object" && input !== null && "default" in input;
2626
};
2727

28-
const sortAndRealizeImports = async (
29-
imports: Record<string, () => Promise<unknown>>,
30-
) => {
31-
const realizedImports = await Promise.all(
32-
sortBy(Object.entries(imports), [0]).map(([, importThunk]) =>
33-
importThunk(),
34-
),
35-
);
36-
return realizedImports
37-
.filter(isTextImport)
38-
.map(({ default: defaultExport }) => defaultExport);
39-
};
40-
4128
/**
42-
* LoadExamples loads the examples defined statically and compiled in.
29+
* LoadExamples loads the examples defined statically and compiled in the /examples repo.
30+
* If one example is lacking the schema file, we don't include it.
4331
*/
44-
export async function LoadExamples(): Promise<Example[]> {
45-
const names = Object.entries(readmesImports).map(([path]) => path);
46-
names.sort();
47-
const sortedReadmes = await sortAndRealizeImports(readmesImports);
48-
const sortedSchemas = await sortAndRealizeImports(schemasImports);
49-
const zippedImports = zip(names, sortedReadmes, sortedSchemas);
50-
return zippedImports.map(([name, readme, schema]) => {
51-
const lines = readme.split("\n");
52-
const title = lines[0].trim().substring(1).trim(); // Strip the # for the markdown header
53-
const subtitle = lines[2].trim();
54-
return {
55-
id: name,
56-
title,
57-
subtitle,
58-
documentation: readme,
59-
data: yaml.parse(schema),
60-
};
61-
});
32+
export function LoadExamples(): Example[] {
33+
return Object.keys(readmesImports)
34+
.map((path) => {
35+
// Grab the id
36+
const match = path.match(/\/examples\/schemas\/(.*)\/README\.md/);
37+
return match ? match[1] : "";
38+
})
39+
.filter((name): name is string => {
40+
if (name === "") return false;
41+
// Skip examples that don't have the schema file
42+
const schemaPath = `/examples/schemas/${name}/schema-and-data.yaml`;
43+
return schemaPath in schemasImports;
44+
})
45+
.sort()
46+
.map((name) => {
47+
const readmeModule =
48+
readmesImports[`/examples/schemas/${name}/README.md`];
49+
const schemaModule =
50+
schemasImports[`/examples/schemas/${name}/schema-and-data.yaml`];
51+
52+
const readme = isTextImport(readmeModule) ? readmeModule.default : "";
53+
const schema = isTextImport(schemaModule) ? schemaModule.default : "";
54+
55+
const lines = readme.split("\n");
56+
const title = lines[0].trim().substring(1).trim(); // Strip the # for the markdown header
57+
const subtitle = lines[2].trim();
58+
let schemaRes: ParsedValidation;
59+
try {
60+
schemaRes = yaml.parse(schema);
61+
} catch (e) {
62+
throw new Error("error parsing schema for example " + name + ": " + e);
63+
}
64+
return {
65+
id: name,
66+
title: title,
67+
subtitle: subtitle,
68+
data: schemaRes,
69+
};
70+
});
6271
}

yarn.lock

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4833,11 +4833,6 @@ es-to-primitive@^1.3.0:
48334833
is-date-object "^1.0.5"
48344834
is-symbol "^1.0.4"
48354835

4836-
es-toolkit@^1.36.0:
4837-
version "1.36.0"
4838-
resolved "https://registry.yarnpkg.com/es-toolkit/-/es-toolkit-1.36.0.tgz#98a9ddba346cde1aeca099f9bc27c05838c68b9a"
4839-
integrity sha512-5lpkRpDELuTSeAL//Rcg5urg+K/yOD1BobJSiNeCc89snMqgrhckmj8jdljqraDbpREiXTNW311RN518eVHBng==
4840-
48414836
48424837
version "0.14.47"
48434838
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.47.tgz#ef95b42c67bcf4268c869153fa3ad1466c4cea6b"

0 commit comments

Comments
 (0)