Skip to content

Commit f765ed7

Browse files
wip: a schemas hook
1 parent a905056 commit f765ed7

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed

acs-git/lib/hooks/schemas.js

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* ACS Git server
3+
* Schema loader hook
4+
* Copyright 2025 University of Sheffield AMRC
5+
*/
6+
7+
/* XXX The logic in here duplicates that in acs-schemas. As that is not
8+
* in the monorepo it's more difficult to factor out; probably we would
9+
* need a full @amrc-factoryplus/schema-loader module on NPM.
10+
*/
11+
12+
import $fs from "fs";
13+
import $fsp from "fs/promises";
14+
import $path from "path";
15+
16+
import Walk from "@root/walk";
17+
import git from "isomorphic-git";
18+
import YAML from "yaml";
19+
20+
import { UUIDs } from "@amrc-factoryplus/service-client";
21+
22+
const App = {
23+
Schema: "b16e85fb-53c2-49f9-8d83-cdf6763304ba",
24+
Metadata: "32093857-9d29-470e-a897-d2b56d5aa978",
25+
};
26+
///const source = process.env.source_repo;
27+
28+
const path_rx = RegExp(`([\\w/_]+)-v(\\d+).yaml$`);
29+
30+
const load_yaml = async f => YAML.parse(
31+
await $fsp.readFile(f, { encoding: "utf8" }));
32+
33+
export class Schemas {
34+
constructor (opts) {
35+
this.dir = opts.dir;
36+
this.gitdir = opts.gitdir;
37+
this.source = opts.remote;
38+
39+
this.cdb = opts.fplus.ConfigDB;
40+
this.log = opts.fplus.debug.bound("schemas");
41+
}
42+
43+
async run () {
44+
const yamls = await this.find_yamls();
45+
const schemas = await this.find_schemas(yamls);
46+
await this.load_schemas(schemas);
47+
}
48+
49+
async find_yamls (schemas) {
50+
const yamls = new Map();
51+
52+
await Walk.walk(schemas, async (err, fullpath, dirent) => {
53+
if (err) throw err;
54+
55+
if (dirent.isDirectory()) return;
56+
57+
const path = $path.relative(schemas, fullpath);
58+
const matches = path.match(path_rx);
59+
if (!matches)
60+
throw `Bad schema filename: ${path}`;
61+
const [, name, version] = matches;
62+
if (!name || !version)
63+
throw `Bad name or version for ${path}`;
64+
65+
yamls.set(path, { name, version });
66+
});
67+
68+
return yamls;
69+
}
70+
71+
async find_schemas (yamls) {
72+
const configs = new Map();
73+
for (const [path, meta] of yamls.entries()) {
74+
this.log("Processing %s", path);
75+
76+
const schema = await load_yaml(path);
77+
78+
const uuid = schema.properties?.Schema_UUID?.const;
79+
if (schema.$id != `urn:uuid:${uuid}`)
80+
throw `Schema_UUID mismatch for ${path}`;
81+
this.log("Found Schema_UUID %s", uuid);
82+
83+
const changes = (await git.log({
84+
fs: $fs,
85+
gitdir: this.girdir,
86+
filepath: `schemas/${path}`,
87+
})).map(l => l.commit.author.timestamp);
88+
89+
configs.set(uuid, {
90+
metadata: {
91+
...meta,
92+
created: changes.at(-1),
93+
modified: changes.at(0),
94+
source: this.source,
95+
},
96+
schema,
97+
});
98+
}
99+
100+
return configs;
101+
}
102+
103+
async load_schemas (schemas) {
104+
const { cdb } = this;
105+
106+
for (const [uuid, { metadata, schema }] of schemas.entries()) {
107+
this.log("Updating schema %s v%s (%s)",
108+
metadata.name, metadata.version, uuid);
109+
110+
const existing = await cdb.get_config(App.Metadata, uuid);
111+
if (existing && existing.source != this.source) {
112+
this.log("Schema %s comes from %s, skipping", uuid, existing.source);
113+
continue;
114+
}
115+
116+
await cdb.create_object(UUIDs.Class.Schema, uuid);
117+
118+
/* XXX It might be better to use the schema title here? But at the
119+
* moment those aren't unique. */
120+
const name = `${metadata.name} (v${metadata.version})`;
121+
await cdb.put_config(UUIDs.App.Info, uuid, { name });
122+
await cdb.put_config(App.Metadata, uuid, metadata);
123+
await cdb.put_config(App.Schema, uuid, schema);
124+
}
125+
}
126+
}

0 commit comments

Comments
 (0)