@rdfc/js-runner 2.0.0-alpha.1 → 2.0.0-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +9 -0
- package/__tests__/channels.test.ts +1 -38
- package/examples/echo/.idea/echo.iml +9 -0
- package/examples/echo/.idea/misc.xml +6 -0
- package/examples/echo/.idea/modules.xml +8 -0
- package/examples/echo/.idea/vcs.xml +7 -0
- package/examples/echo/.swls/config.json +1 -0
- package/examples/echo/index.ttl +3 -0
- package/examples/echo/minimal.ttl +90 -0
- package/examples/echo/pipeline.ttl +22 -4
- package/examples/echo/processors.ttl +5 -7
- package/examples/echo/shacl.ttl +9 -0
- package/examples/echo/shape.ttl +1339 -0
- package/examples/echo/src/processors.ts +3 -4
- package/examples/echo/test.ttl +9 -12
- package/file:/home/silvius/Projects/mumo-pipeline/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +3 -0
- package/index.ttl +29 -21
- package/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +3 -0
- package/lib/client.js +2 -2
- package/lib/index.d.ts +3 -0
- package/lib/index.js +4 -1
- package/lib/jsonld.js +4 -3
- package/lib/logger.d.ts +3 -1
- package/lib/logger.js +23 -1
- package/lib/reader.d.ts +2 -1
- package/lib/reader.js +7 -16
- package/lib/reexports.d.ts +3 -0
- package/lib/reexports.js +4 -0
- package/lib/runner.d.ts +4 -3
- package/lib/runner.js +30 -20
- package/lib/testUtils.d.ts +24 -0
- package/lib/testUtils.js +150 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/writer.d.ts +2 -1
- package/lib/writer.js +1 -1
- package/minimal.ttl +99 -0
- package/package.json +3 -3
- package/src/client.ts +15 -15
- package/src/index.ts +4 -0
- package/src/jsonld.ts +5 -2
- package/src/logger.ts +27 -1
- package/src/reader.ts +9 -15
- package/src/reexports.ts +6 -0
- package/src/runner.ts +51 -40
- package/src/testUtils.ts +196 -0
- package/src/writer.ts +2 -1
package/minimal.ttl
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
@prefix prov: <http://www.w3.org/ns/prov#>.
|
|
2
|
+
@prefix sds: <https://w3id.org/sds#>.
|
|
3
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
|
|
4
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#>.
|
|
5
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
|
|
6
|
+
@prefix sh: <http://www.w3.org/ns/shacl#>.
|
|
7
|
+
@prefix rdfc: <https://w3id.org/rdf-connect#>.
|
|
8
|
+
|
|
9
|
+
############################################################
|
|
10
|
+
# General statements #
|
|
11
|
+
############################################################
|
|
12
|
+
# sds:Activity is a prov:Activity
|
|
13
|
+
sds:Activity rdfs:subClassOf prov:Activity.
|
|
14
|
+
|
|
15
|
+
# rdfc:Processor too
|
|
16
|
+
rdfc:Processor rdfs:subClassOf sds:Activity.
|
|
17
|
+
|
|
18
|
+
# sds:implementationOf is subClassOf
|
|
19
|
+
sds:implementationOf rdfs:subPropertyOf rdfs:subClassOf.
|
|
20
|
+
|
|
21
|
+
############################################################
|
|
22
|
+
# Javascript statements #
|
|
23
|
+
############################################################
|
|
24
|
+
# specialized for js too
|
|
25
|
+
rdfc:jsImplementationOf rdfs:subPropertyOf sds:implementationOf.
|
|
26
|
+
|
|
27
|
+
# A node runner, runs things that are rdfc:jsImplementationOf rdfc:Processor (aka, rdfs:subClassOf prov:Activity)
|
|
28
|
+
rdfc:NodeRunner a rdfc:Runner;
|
|
29
|
+
rdfc:handlesSubjectsOf rdfc:jsImplementationOf;
|
|
30
|
+
rdfc:command "npx js-runner".
|
|
31
|
+
|
|
32
|
+
# This shouldn't be necessary, should work with sh:targetSubjectsOf
|
|
33
|
+
# rdfc:processor_definition <JsProcessorShape>.
|
|
34
|
+
#
|
|
35
|
+
# Shape that a Js Processor should fulfil;
|
|
36
|
+
[ ] a sh:NodeShape;
|
|
37
|
+
# This shouldn't be necessary,should work with sh:targetSubjectsOf and this isn't a real Class
|
|
38
|
+
sh:targetClass <JsProcessorShape>;
|
|
39
|
+
# We target it with jsImplementationOf
|
|
40
|
+
sh:targetSubjectsOf rdfc:jsImplementationOf;
|
|
41
|
+
sh:property [
|
|
42
|
+
sh:path rdfc:entrypoint;
|
|
43
|
+
sh:name "location";
|
|
44
|
+
sh:minCount 1;
|
|
45
|
+
sh:maxCount 1;
|
|
46
|
+
sh:datatype xsd:iri;
|
|
47
|
+
], [
|
|
48
|
+
sh:path rdfc:file;
|
|
49
|
+
sh:name "file";
|
|
50
|
+
sh:minCount 1;
|
|
51
|
+
sh:maxCount 1;
|
|
52
|
+
sh:datatype xsd:iri;
|
|
53
|
+
], [
|
|
54
|
+
sh:path rdfc:class;
|
|
55
|
+
sh:name "clazz";
|
|
56
|
+
sh:maxCount 1;
|
|
57
|
+
sh:datatype xsd:string;
|
|
58
|
+
].
|
|
59
|
+
|
|
60
|
+
############################################################
|
|
61
|
+
# Processor statements #
|
|
62
|
+
############################################################
|
|
63
|
+
rdfc:FooBarProcessor a owl:Class;
|
|
64
|
+
rdfs:label "My Epic FooBar Processor";
|
|
65
|
+
rdfs:description "FooBars everything!";
|
|
66
|
+
rdfc:jsImplementationOf rdfc:Processor;
|
|
67
|
+
rdfc:entrypoint <./>;
|
|
68
|
+
rdfc:file <./lib/processors.js>;
|
|
69
|
+
rdfc:class "FooBarProcessor".
|
|
70
|
+
|
|
71
|
+
[ ] a sh:NodeShape;
|
|
72
|
+
sh:targetClass rdfc:FooBarProcessor;
|
|
73
|
+
sh:property [
|
|
74
|
+
sh:path rdfc:reader;
|
|
75
|
+
sh:name "reader";
|
|
76
|
+
sh:minCount 1;
|
|
77
|
+
sh:maxCount 1;
|
|
78
|
+
sh:class rdfc:Reader;
|
|
79
|
+
], [
|
|
80
|
+
sh:path rdfc:writer;
|
|
81
|
+
sh:name "writer";
|
|
82
|
+
sh:maxCount 1;
|
|
83
|
+
sh:class rdfc:Writer;
|
|
84
|
+
].
|
|
85
|
+
|
|
86
|
+
############################################################
|
|
87
|
+
# Pipeline statements #
|
|
88
|
+
############################################################
|
|
89
|
+
<> a rdfc:Pipeline;
|
|
90
|
+
rdfc:consistsOf [
|
|
91
|
+
rdfc:instantiates rdfc:NodeRunner;
|
|
92
|
+
rdfc:processor <foobar>;
|
|
93
|
+
].
|
|
94
|
+
|
|
95
|
+
<incomingMessages> a rdfc:Reader, rdfc:Writer.
|
|
96
|
+
<foobar> a rdfc:FooBarProcessor;
|
|
97
|
+
rdfc:reader <incomingMessages>;
|
|
98
|
+
rdfc:writer <incomingMessages>.
|
|
99
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rdfc/js-runner",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.11",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@types/node": "^22.13.5",
|
|
38
38
|
"@typescript-eslint/eslint-plugin": "^8.25.0",
|
|
39
39
|
"@typescript-eslint/parser": "^8.25.0",
|
|
40
|
-
"@vitest/coverage-v8": "^3.
|
|
40
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
41
41
|
"eslint": "^9.21.0",
|
|
42
42
|
"eslint-config-prettier": "^10.0.1",
|
|
43
43
|
"eslint-plugin-prettier": "^5.2.3",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"tsc-alias": "^1.8.10",
|
|
51
51
|
"typescript": "^5.7.3",
|
|
52
52
|
"typescript-eslint": "^8.25.0",
|
|
53
|
-
"vitest": "^3.
|
|
53
|
+
"vitest": "^3.2.4"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@grpc/grpc-js": "^1.12.6",
|
package/src/client.ts
CHANGED
|
@@ -12,7 +12,7 @@ export async function start(addr: string, uri: string) {
|
|
|
12
12
|
transports: [
|
|
13
13
|
new RpcTransport({
|
|
14
14
|
entities: [uri, 'cli'],
|
|
15
|
-
stream: client.logStream(() => {}),
|
|
15
|
+
stream: client.logStream(() => { }),
|
|
16
16
|
}),
|
|
17
17
|
],
|
|
18
18
|
})
|
|
@@ -27,26 +27,26 @@ export async function start(addr: string, uri: string) {
|
|
|
27
27
|
|
|
28
28
|
let processorsEnd!: (v: unknown) => unknown
|
|
29
29
|
const processorsEnded = new Promise((res) => (processorsEnd = res))
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
; (async () => {
|
|
31
|
+
for await (const chunk of stream) {
|
|
32
|
+
const msg: RunnerMessage = chunk
|
|
33
|
+
if (msg.proc) {
|
|
34
|
+
await runner.addProcessor(msg.proc)
|
|
35
|
+
}
|
|
36
|
+
if (msg.start) {
|
|
37
|
+
runner.start().then(processorsEnd)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
await runner.handleOrchMessage(msg)
|
|
35
41
|
}
|
|
36
|
-
if (msg.start) {
|
|
37
|
-
runner.start().then(processorsEnd)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
await runner.handleOrchMessage(msg)
|
|
41
|
-
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
logger.error('Stream ended')
|
|
44
|
+
})()
|
|
45
45
|
|
|
46
46
|
await processorsEnded
|
|
47
47
|
|
|
48
48
|
logger.info('All processors are finished')
|
|
49
49
|
stream.end()
|
|
50
50
|
client.close()
|
|
51
|
-
process.exit(0)
|
|
51
|
+
setTimeout(() => process.exit(0), 500)
|
|
52
52
|
}
|
package/src/index.ts
CHANGED
package/src/jsonld.ts
CHANGED
|
@@ -30,7 +30,7 @@ const RDFL = createUriAndTermNamespace(
|
|
|
30
30
|
|
|
31
31
|
type Writable = (msg: OrchestratorMessage) => Promise<unknown>
|
|
32
32
|
const RDFC = createNamespace(
|
|
33
|
-
'https://w3id.org/rdf-connect
|
|
33
|
+
'https://w3id.org/rdf-connect#',
|
|
34
34
|
(x) => x,
|
|
35
35
|
'Reader',
|
|
36
36
|
'Writer',
|
|
@@ -189,7 +189,10 @@ function expandArgs(args: Arg, cont: Cont, logger: Logger) {
|
|
|
189
189
|
let x = 'object'
|
|
190
190
|
try {
|
|
191
191
|
x = JSON.stringify(v)
|
|
192
|
-
|
|
192
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
193
|
+
} catch (_e: unknown) {
|
|
194
|
+
// default is set
|
|
195
|
+
}
|
|
193
196
|
|
|
194
197
|
return k + ': ' + x
|
|
195
198
|
}),
|
package/src/logger.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import winston from 'winston'
|
|
1
|
+
import winston, { Logger } from 'winston'
|
|
2
2
|
import Transport from 'winston-transport'
|
|
3
3
|
|
|
4
4
|
import * as grpc from '@grpc/grpc-js'
|
|
5
5
|
import { LogMessage } from '@rdfc/proto'
|
|
6
|
+
|
|
6
7
|
export class RpcTransport extends Transport {
|
|
7
8
|
private readonly stream: grpc.ClientWritableStream<LogMessage>
|
|
8
9
|
private readonly entities: string[]
|
|
9
10
|
private readonly aliases: string[]
|
|
11
|
+
|
|
10
12
|
constructor(opts: {
|
|
11
13
|
stream: grpc.ClientWritableStream<LogMessage>
|
|
12
14
|
entities: string[]
|
|
@@ -35,4 +37,28 @@ export class RpcTransport extends Transport {
|
|
|
35
37
|
callback()
|
|
36
38
|
}
|
|
37
39
|
}
|
|
40
|
+
|
|
41
|
+
withEntity(entity: string): RpcTransport {
|
|
42
|
+
return new RpcTransport({
|
|
43
|
+
stream: this.stream,
|
|
44
|
+
entities: [...this.entities, entity],
|
|
45
|
+
aliases: this.aliases,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function extendLogger(baseLogger: Logger, newEntity: string): Logger {
|
|
51
|
+
const newTransports = baseLogger.transports.map((t) => {
|
|
52
|
+
if (t instanceof RpcTransport) {
|
|
53
|
+
return t.withEntity(newEntity)
|
|
54
|
+
}
|
|
55
|
+
return t
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
return winston.createLogger({
|
|
59
|
+
level: baseLogger.level,
|
|
60
|
+
format: baseLogger.format,
|
|
61
|
+
defaultMeta: baseLogger.defaultMeta,
|
|
62
|
+
transports: newTransports,
|
|
63
|
+
})
|
|
38
64
|
}
|
package/src/reader.ts
CHANGED
|
@@ -21,6 +21,7 @@ export type Any =
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export interface Reader {
|
|
24
|
+
readonly uri: string
|
|
24
25
|
strings(): AsyncIterable<string>
|
|
25
26
|
streams(): AsyncIterable<AsyncGenerator<Uint8Array>>
|
|
26
27
|
buffers(): AsyncIterable<Uint8Array>
|
|
@@ -30,7 +31,7 @@ export interface Reader {
|
|
|
30
31
|
class MyIter<T> implements AsyncIterable<T> {
|
|
31
32
|
private convertor: Convertor<T>
|
|
32
33
|
private queue: (T | undefined)[] = []
|
|
33
|
-
private resolveNext: ((value:
|
|
34
|
+
private resolveNext: ((value: undefined) => void) | null = null
|
|
34
35
|
|
|
35
36
|
constructor(convertor: Convertor<T>) {
|
|
36
37
|
this.convertor = convertor
|
|
@@ -38,20 +39,18 @@ class MyIter<T> implements AsyncIterable<T> {
|
|
|
38
39
|
|
|
39
40
|
push(buffer: Uint8Array) {
|
|
40
41
|
const item = this.convertor.from(buffer)
|
|
42
|
+
this.queue.push(item)
|
|
41
43
|
if (this.resolveNext) {
|
|
42
|
-
this.resolveNext(
|
|
44
|
+
this.resolveNext(undefined)
|
|
43
45
|
this.resolveNext = null
|
|
44
|
-
} else {
|
|
45
|
-
this.queue.push(item)
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
close() {
|
|
50
|
+
this.queue.push(undefined)
|
|
50
51
|
if (this.resolveNext) {
|
|
51
52
|
this.resolveNext(undefined)
|
|
52
53
|
this.resolveNext = null
|
|
53
|
-
} else {
|
|
54
|
-
this.queue.push(undefined)
|
|
55
54
|
}
|
|
56
55
|
}
|
|
57
56
|
|
|
@@ -63,11 +62,10 @@ class MyIter<T> implements AsyncIterable<T> {
|
|
|
63
62
|
}
|
|
64
63
|
})(chunks)
|
|
65
64
|
const item = await this.convertor.fromStream(stream)
|
|
65
|
+
this.queue.push(item)
|
|
66
66
|
if (this.resolveNext) {
|
|
67
|
-
this.resolveNext(
|
|
67
|
+
this.resolveNext(undefined)
|
|
68
68
|
this.resolveNext = null
|
|
69
|
-
} else {
|
|
70
|
-
this.queue.push(item)
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
|
|
@@ -78,11 +76,7 @@ class MyIter<T> implements AsyncIterable<T> {
|
|
|
78
76
|
if (item === undefined) break
|
|
79
77
|
yield item
|
|
80
78
|
} else {
|
|
81
|
-
|
|
82
|
-
(resolve) => (this.resolveNext = resolve),
|
|
83
|
-
)
|
|
84
|
-
if (item === undefined) break
|
|
85
|
-
yield item
|
|
79
|
+
await new Promise<undefined>((resolve) => (this.resolveNext = resolve))
|
|
86
80
|
}
|
|
87
81
|
}
|
|
88
82
|
}
|
|
@@ -90,7 +84,7 @@ class MyIter<T> implements AsyncIterable<T> {
|
|
|
90
84
|
|
|
91
85
|
export class ReaderInstance implements Reader {
|
|
92
86
|
private client: RunnerClient
|
|
93
|
-
|
|
87
|
+
readonly uri: string
|
|
94
88
|
private logger: winston.Logger
|
|
95
89
|
|
|
96
90
|
private iterators: MyIter<unknown>[] = []
|
package/src/reexports.ts
ADDED
package/src/runner.ts
CHANGED
|
@@ -11,7 +11,6 @@ import { Logger } from 'winston'
|
|
|
11
11
|
|
|
12
12
|
import winston from 'winston'
|
|
13
13
|
import { RpcTransport } from './logger'
|
|
14
|
-
import { parse_jsonld } from './jsonld'
|
|
15
14
|
import { Cont, empty, extractShapes, Shapes } from 'rdf-lens'
|
|
16
15
|
import { NamedNode, Parser } from 'n3'
|
|
17
16
|
import { createNamespace, createUriAndTermNamespace, RDF } from '@treecg/types'
|
|
@@ -31,13 +30,13 @@ const RDFL = createUriAndTermNamespace(
|
|
|
31
30
|
)
|
|
32
31
|
|
|
33
32
|
const RDFC = createNamespace(
|
|
34
|
-
'https://w3id.org/rdf-connect
|
|
33
|
+
'https://w3id.org/rdf-connect#',
|
|
35
34
|
(x) => x,
|
|
36
35
|
'Reader',
|
|
37
36
|
'Writer',
|
|
38
37
|
)
|
|
39
38
|
|
|
40
|
-
type Writable = (msg: OrchestratorMessage) => Promise<unknown>
|
|
39
|
+
export type Writable = (msg: OrchestratorMessage) => Promise<unknown>
|
|
41
40
|
|
|
42
41
|
type ProcessorConfig = {
|
|
43
42
|
location: string
|
|
@@ -45,6 +44,8 @@ type ProcessorConfig = {
|
|
|
45
44
|
clazz?: string
|
|
46
45
|
}
|
|
47
46
|
|
|
47
|
+
export type FullProc<C extends Proc<unknown>> =
|
|
48
|
+
C extends Proc<infer T> ? T & C : unknown
|
|
48
49
|
export class Runner {
|
|
49
50
|
private readonly readers: { [uri: string]: ReaderInstance[] } = {}
|
|
50
51
|
private readonly writers: { [uri: string]: WriterInstance[] } = {}
|
|
@@ -71,69 +72,72 @@ export class Runner {
|
|
|
71
72
|
this.logger = logger
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
async addProcessor
|
|
75
|
+
async addProcessor<P extends Proc<unknown>>(
|
|
76
|
+
proc: Processor,
|
|
77
|
+
): Promise<FullProc<P>> {
|
|
75
78
|
const procLogger = winston.createLogger({
|
|
76
79
|
transports: [
|
|
77
80
|
new RpcTransport({
|
|
78
81
|
entities: [proc.uri, this.uri],
|
|
79
|
-
stream: this.client.logStream(() => {
|
|
82
|
+
stream: this.client.logStream(() => {}),
|
|
80
83
|
}),
|
|
81
84
|
],
|
|
82
85
|
})
|
|
83
86
|
|
|
84
|
-
const ty = JSON.stringify(
|
|
85
|
-
|
|
87
|
+
const ty = JSON.stringify(
|
|
88
|
+
this.quads
|
|
89
|
+
.filter(
|
|
90
|
+
(x) =>
|
|
91
|
+
x.subject.value === proc.uri && x.predicate.equals(RDF.terms.type),
|
|
92
|
+
)
|
|
93
|
+
.map((x) => x.object.value),
|
|
94
|
+
)
|
|
95
|
+
this.logger.info('parsing ' + proc.uri + ' type ' + ty)
|
|
86
96
|
const args = this.shapes.lenses[RDFL.TypedExtract].execute({
|
|
87
97
|
id: new NamedNode(proc.uri),
|
|
88
98
|
quads: this.quads,
|
|
89
99
|
})
|
|
90
|
-
// this.logger.info('Parsed ' + JSON.stringify(args))
|
|
91
|
-
|
|
92
|
-
// const args = await parse_jsonld(proc.arguments, procLogger, {
|
|
93
|
-
// client: this.client,
|
|
94
|
-
// write: this.write,
|
|
95
|
-
// readers: this.readers,
|
|
96
|
-
// writers: this.writers,
|
|
97
|
-
// })
|
|
98
100
|
|
|
99
101
|
const config: ProcessorConfig = JSON.parse(proc.config)
|
|
100
|
-
const url = new URL(config.location)
|
|
101
|
-
process.chdir(url.pathname)
|
|
102
|
+
// const url = new URL(config.location)
|
|
103
|
+
// process.chdir(url.pathname)
|
|
102
104
|
const jsProgram = await import(config.file)
|
|
103
105
|
const clazz = jsProgram[config.clazz || 'default']
|
|
104
106
|
const instance: Proc<unknown> = new clazz(args, procLogger)
|
|
105
107
|
await instance.init()
|
|
106
108
|
|
|
107
|
-
this.logger.info(
|
|
108
|
-
await this.write({ init: { uri: proc.uri } })
|
|
109
|
+
this.logger.info('inited ' + proc.uri + ' type ' + ty)
|
|
109
110
|
|
|
110
111
|
this.processors.push(instance)
|
|
111
112
|
this.processorTransforms.push(instance.transform())
|
|
113
|
+
|
|
114
|
+
await this.write({ init: { uri: proc.uri } })
|
|
115
|
+
|
|
116
|
+
return <FullProc<P>>instance
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
async start() {
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
try {
|
|
121
|
+
await Promise.all(this.processors.map((x) => x.produce()))
|
|
122
|
+
await Promise.all(this.processorTransforms)
|
|
123
|
+
} catch (ex: unknown) {
|
|
124
|
+
this.logger.error('Start failed: ' + JSON.stringify(ex))
|
|
125
|
+
}
|
|
117
126
|
}
|
|
118
127
|
|
|
119
128
|
createWriter(uri: Term): Writer {
|
|
120
|
-
const ids = uri.value
|
|
129
|
+
const ids = uri.value
|
|
121
130
|
|
|
122
131
|
if (this.writers[ids] === undefined) {
|
|
123
132
|
this.writers[ids] = []
|
|
124
133
|
}
|
|
125
|
-
const writer = new WriterInstance(
|
|
126
|
-
ids,
|
|
127
|
-
this.client,
|
|
128
|
-
this.write,
|
|
129
|
-
this.logger,
|
|
130
|
-
)
|
|
134
|
+
const writer = new WriterInstance(ids, this.client, this.write, this.logger)
|
|
131
135
|
this.writers[ids].push(writer)
|
|
132
136
|
return writer
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
createReader(uri: Term): Reader {
|
|
136
|
-
const ids = uri.value
|
|
140
|
+
const ids = uri.value
|
|
137
141
|
|
|
138
142
|
if (this.readers[ids] === undefined) {
|
|
139
143
|
this.readers[ids] = []
|
|
@@ -170,17 +174,24 @@ export class Runner {
|
|
|
170
174
|
}
|
|
171
175
|
|
|
172
176
|
if (msg.pipeline) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
177
|
+
try {
|
|
178
|
+
// here
|
|
179
|
+
const quads = new Parser().parse(msg.pipeline)
|
|
180
|
+
this.shapes = extractShapes(
|
|
181
|
+
quads,
|
|
182
|
+
{
|
|
183
|
+
[RDFC.Reader]: (x: Cont) => this.createReader(x.id),
|
|
184
|
+
[RDFC.Writer]: (x: Cont) => this.createWriter(x.id),
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
[RDFC.Reader]: empty<Cont>(),
|
|
188
|
+
[RDFC.Writer]: empty<Cont>(),
|
|
189
|
+
},
|
|
190
|
+
)
|
|
191
|
+
this.quads = quads
|
|
192
|
+
} catch (ex: unknown) {
|
|
193
|
+
this.logger.error('Pipeline failed: ' + JSON.stringify(ex))
|
|
194
|
+
}
|
|
184
195
|
}
|
|
185
196
|
}
|
|
186
197
|
}
|
package/src/testUtils.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { DataChunk, RunnerClient } from '@rdfc/proto'
|
|
2
|
+
import * as grpc from '@grpc/grpc-js'
|
|
3
|
+
import { ClientReadableStream } from '@grpc/grpc-js'
|
|
4
|
+
import { ClientReadableStreamImpl, OrchestratorMessage } from './reexports'
|
|
5
|
+
import { extractShapes } from 'rdf-lens'
|
|
6
|
+
import { NamedNode, Parser, Writer } from 'n3'
|
|
7
|
+
import { readFile } from 'fs/promises'
|
|
8
|
+
import winston, { createLogger } from 'winston'
|
|
9
|
+
import { WriterInstance } from './writer'
|
|
10
|
+
import { ReaderInstance } from './reader'
|
|
11
|
+
import { Processor } from './processor'
|
|
12
|
+
import { FullProc, Runner } from './runner'
|
|
13
|
+
import { Quad, Term } from '@rdfjs/types'
|
|
14
|
+
import { createTermNamespace } from '@treecg/types'
|
|
15
|
+
import { expect } from 'vitest'
|
|
16
|
+
|
|
17
|
+
export async function getProcessorShape(
|
|
18
|
+
baseIRI = process.cwd() + '/node_modules/@rdfc/js-runner/index.ttl',
|
|
19
|
+
) {
|
|
20
|
+
const configFile = await readFile(baseIRI, { encoding: 'utf8' })
|
|
21
|
+
const configQuads = new Parser().parse(configFile)
|
|
22
|
+
const shapes = extractShapes(configQuads)
|
|
23
|
+
|
|
24
|
+
return shapes
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class TestClient extends RunnerClient {
|
|
28
|
+
next: (stream: ClientReadableStream<DataChunk>) => unknown
|
|
29
|
+
|
|
30
|
+
constructor() {
|
|
31
|
+
super('localhost:5400', grpc.credentials.createInsecure())
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
nextStream(): Promise<ClientReadableStream<DataChunk>> {
|
|
35
|
+
return new Promise((res) => (this.next = res))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
receiveStreamMessage(): ClientReadableStream<DataChunk> {
|
|
39
|
+
const stream = new ClientReadableStreamImpl<DataChunk>((data: Buffer) => {
|
|
40
|
+
return { data }
|
|
41
|
+
})
|
|
42
|
+
this.next(stream)
|
|
43
|
+
return stream
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function one<T>(iter: AsyncIterable<T>): Promise<T | undefined> {
|
|
48
|
+
for await (const item of iter) {
|
|
49
|
+
return item
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const client = new TestClient()
|
|
54
|
+
export const uri = 'someUri'
|
|
55
|
+
export const logger = createLogger({
|
|
56
|
+
transports: new winston.transports.Console({
|
|
57
|
+
level: process.env['DEBUG'] || 'info',
|
|
58
|
+
}),
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
export function createWriter(iri = uri): [WriterInstance, ReaderInstance] {
|
|
62
|
+
const reader = createReader(iri)
|
|
63
|
+
const writeStream = new WriterInstance(
|
|
64
|
+
iri,
|
|
65
|
+
client,
|
|
66
|
+
async (msg) => {
|
|
67
|
+
if (msg.msg) {
|
|
68
|
+
reader.handleMsg(msg.msg)
|
|
69
|
+
}
|
|
70
|
+
if (msg.close) {
|
|
71
|
+
reader.close()
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
logger,
|
|
75
|
+
)
|
|
76
|
+
return [writeStream, reader]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function createReader(iri = uri): ReaderInstance {
|
|
80
|
+
const reader = new ReaderInstance(iri, client, logger)
|
|
81
|
+
return reader
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const shapeQuads = `
|
|
85
|
+
@prefix rdfc: <https://w3id.org/rdf-connect#>.
|
|
86
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
|
|
87
|
+
@prefix sh: <http://www.w3.org/ns/shacl#>.
|
|
88
|
+
[ ] a sh:NodeShape;
|
|
89
|
+
sh:targetClass <JsProcessorShape>;
|
|
90
|
+
sh:property [
|
|
91
|
+
sh:path rdfc:entrypoint;
|
|
92
|
+
sh:name "location";
|
|
93
|
+
sh:minCount 1;
|
|
94
|
+
sh:maxCount 1;
|
|
95
|
+
sh:datatype xsd:string;
|
|
96
|
+
], [
|
|
97
|
+
sh:path rdfc:file;
|
|
98
|
+
sh:name "file";
|
|
99
|
+
sh:minCount 1;
|
|
100
|
+
sh:maxCount 1;
|
|
101
|
+
sh:datatype xsd:string;
|
|
102
|
+
], [
|
|
103
|
+
sh:path rdfc:class;
|
|
104
|
+
sh:name "clazz";
|
|
105
|
+
sh:maxCount 1;
|
|
106
|
+
sh:datatype xsd:string;
|
|
107
|
+
].
|
|
108
|
+
`
|
|
109
|
+
const OWL = createTermNamespace('http://www.w3.org/2002/07/owl#', 'imports')
|
|
110
|
+
const processorShapes = extractShapes(new Parser().parse(shapeQuads))
|
|
111
|
+
const base = 'https://w3id.org/rdf-connect#'
|
|
112
|
+
|
|
113
|
+
export async function importFile(file: string): Promise<Quad[]> {
|
|
114
|
+
const done = new Set<string>()
|
|
115
|
+
const todo = [new URL('file://' + file)]
|
|
116
|
+
const quads: Quad[] = []
|
|
117
|
+
|
|
118
|
+
let item = todo.pop()
|
|
119
|
+
while (item !== undefined) {
|
|
120
|
+
if (done.has(item.toString())) {
|
|
121
|
+
item = todo.pop()
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
done.add(item.toString())
|
|
125
|
+
if (item.protocol !== 'file:') {
|
|
126
|
+
throw 'No supported protocol ' + item.protocol
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const txt = await readFile(item.pathname, { encoding: 'utf8' })
|
|
130
|
+
const extras = new Parser({ baseIRI: item.toString() }).parse(txt)
|
|
131
|
+
|
|
132
|
+
for (const o of extras
|
|
133
|
+
.filter(
|
|
134
|
+
(x) =>
|
|
135
|
+
x.subject.value === item?.toString() &&
|
|
136
|
+
x.predicate.equals(OWL.imports),
|
|
137
|
+
)
|
|
138
|
+
.map((x) => x.object.value)) {
|
|
139
|
+
todo.push(new URL(o))
|
|
140
|
+
}
|
|
141
|
+
quads.push(...extras)
|
|
142
|
+
|
|
143
|
+
item = todo.pop()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return quads
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export async function getProc<T extends Processor<unknown>>(
|
|
150
|
+
config: string,
|
|
151
|
+
ty: string,
|
|
152
|
+
configLocation: string,
|
|
153
|
+
uri = 'http://example.com/ns#processor',
|
|
154
|
+
): Promise<FullProc<T>> {
|
|
155
|
+
const configQuads = await importFile(configLocation)
|
|
156
|
+
const procConfig = processorShapes.lenses['JsProcessorShape'].execute({
|
|
157
|
+
id: new NamedNode(base + ty),
|
|
158
|
+
quads: configQuads,
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
const msgs: OrchestratorMessage[] = []
|
|
162
|
+
const write = async (x: OrchestratorMessage) => {
|
|
163
|
+
msgs.push(x)
|
|
164
|
+
}
|
|
165
|
+
const runner = new Runner(
|
|
166
|
+
new TestClient(),
|
|
167
|
+
write,
|
|
168
|
+
'http://example.com/ns#',
|
|
169
|
+
logger,
|
|
170
|
+
)
|
|
171
|
+
configQuads.push(...new Parser().parse(config))
|
|
172
|
+
await runner.handleOrchMessage({
|
|
173
|
+
pipeline: new Writer().quadsToString(configQuads),
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
const proc = await runner.addProcessor<T>({
|
|
177
|
+
config: JSON.stringify(procConfig),
|
|
178
|
+
arguments: '',
|
|
179
|
+
uri,
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
return proc
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export async function checkProcDefinition(file: string, n: string) {
|
|
186
|
+
const quads = await importFile(file)
|
|
187
|
+
const procConfig = <{ file: Term; location: string; clazz: string }>(
|
|
188
|
+
processorShapes.lenses['JsProcessorShape'].execute({
|
|
189
|
+
id: new NamedNode(base + n),
|
|
190
|
+
quads: quads,
|
|
191
|
+
})
|
|
192
|
+
)
|
|
193
|
+
expect(procConfig.file, n + ' has file').toBeDefined()
|
|
194
|
+
expect(procConfig.location, n + ' has location').toBeDefined()
|
|
195
|
+
expect(procConfig.clazz, n + ' has clazz').toBeDefined()
|
|
196
|
+
}
|