@powerhousedao/reactor-api 1.9.4 → 1.10.0
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/CHANGELOG.md +18 -3
- package/dist/index.d.ts +86 -60
- package/dist/index.js +8107 -1402
- package/dist/index.js.map +1 -1
- package/package.json +10 -5
- package/src/index.ts +2 -0
- package/src/processor-manager.ts +67 -0
- package/src/processors/analytics-processor.ts +21 -0
- package/src/processors/index.ts +3 -0
- package/src/processors/processor.ts +75 -0
- package/src/router.ts +22 -66
- package/src/server.ts +31 -12
- package/src/subgraphs/analytics/db.ts +55 -0
- package/src/subgraphs/analytics/index.ts +12 -0
- package/src/subgraphs/drive/resolvers.ts +26 -14
- package/src/subgraphs/index.ts +1 -0
- package/src/types.ts +46 -4
- package/src/utils/create-schema.ts +16 -7
- package/src/utils/get-knex-client.ts +23 -0
- package/test/router.test.ts +27 -31
- package/src/internal-listener-manager.ts +0 -108
package/src/types.ts
CHANGED
|
@@ -1,22 +1,64 @@
|
|
|
1
1
|
import {
|
|
2
2
|
IDocumentDriveServer,
|
|
3
3
|
InternalTransmitterUpdate,
|
|
4
|
+
IReceiver,
|
|
4
5
|
Listener,
|
|
6
|
+
ListenerRevision,
|
|
5
7
|
} from "document-drive";
|
|
6
|
-
import {
|
|
8
|
+
import { Document, OperationScope } from "document-model/document";
|
|
7
9
|
import { IncomingHttpHeaders } from "http";
|
|
10
|
+
import { Express } from "express";
|
|
11
|
+
import { IAnalyticsStore } from "@powerhousedao/analytics-engine-core";
|
|
12
|
+
import { ReactorRouterManager } from "./router";
|
|
13
|
+
import { ProcessorClass } from "./processors/processor";
|
|
14
|
+
|
|
15
|
+
export type IProcessorManager = {
|
|
16
|
+
registerProcessor(module: IProcessor | ProcessorClass): Promise<IProcessor>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type API = {
|
|
20
|
+
app: Express;
|
|
21
|
+
reactorRouterManager: ReactorRouterManager;
|
|
22
|
+
processorManager: IProcessorManager;
|
|
23
|
+
};
|
|
8
24
|
|
|
9
25
|
export interface Context {
|
|
10
26
|
headers: IncomingHttpHeaders;
|
|
11
27
|
driveId: string | undefined;
|
|
12
28
|
driveServer: IDocumentDriveServer;
|
|
13
|
-
db: PgDatabase<any, any, any>;
|
|
14
29
|
}
|
|
15
30
|
|
|
16
|
-
export type
|
|
31
|
+
export type Subgraph = {
|
|
17
32
|
name: string;
|
|
18
33
|
resolvers: any;
|
|
19
34
|
typeDefs: string;
|
|
20
35
|
options?: Omit<Listener, "driveId">;
|
|
21
|
-
transmit?: (
|
|
36
|
+
transmit?: (
|
|
37
|
+
strands: InternalTransmitterUpdate[],
|
|
38
|
+
) => Promise<ListenerRevision[]>;
|
|
22
39
|
};
|
|
40
|
+
|
|
41
|
+
export type ProcessorType = "analytics" | "operational";
|
|
42
|
+
|
|
43
|
+
export type ProcessorSetupArgs = {
|
|
44
|
+
reactor: IDocumentDriveServer;
|
|
45
|
+
dataSources: {
|
|
46
|
+
analyticsStore: IAnalyticsStore;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type IProcessor<
|
|
51
|
+
D extends Document = Document,
|
|
52
|
+
S extends OperationScope = OperationScope,
|
|
53
|
+
> = IReceiver<D, S> & {
|
|
54
|
+
onSetup?: (args: ProcessorSetupArgs) => void;
|
|
55
|
+
getOptions: () => ProcessorOptions;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// export interface ProcessorType<T> extends Function {
|
|
59
|
+
// new (...args: any[]): T;
|
|
60
|
+
// TYPE: string;
|
|
61
|
+
// OPTIONS: ProcessorOptions;
|
|
62
|
+
// }
|
|
63
|
+
|
|
64
|
+
export type ProcessorOptions = Omit<Listener, "driveId"> & { label: string };
|
|
@@ -8,7 +8,7 @@ import { Context } from "src/types";
|
|
|
8
8
|
export const createSchema = (
|
|
9
9
|
documentDriveServer: IDocumentDriveServer,
|
|
10
10
|
resolvers: GraphQLResolverMap<Context>,
|
|
11
|
-
typeDefs: string
|
|
11
|
+
typeDefs: string,
|
|
12
12
|
) =>
|
|
13
13
|
buildSubgraphSchema([
|
|
14
14
|
{
|
|
@@ -19,7 +19,7 @@ export const createSchema = (
|
|
|
19
19
|
|
|
20
20
|
export const getDocumentModelTypeDefs = (
|
|
21
21
|
documentDriveServer: IDocumentDriveServer,
|
|
22
|
-
typeDefs: string
|
|
22
|
+
typeDefs: string,
|
|
23
23
|
) => {
|
|
24
24
|
const documentModels = documentDriveServer.getDocumentModels();
|
|
25
25
|
let dmSchema = "";
|
|
@@ -32,7 +32,7 @@ export const getDocumentModelTypeDefs = (
|
|
|
32
32
|
.replaceAll(`: Account`, `: ${documentModel.name}Account`)
|
|
33
33
|
.replaceAll(`[Account!]!`, `[${documentModel.name}Account!]!`)
|
|
34
34
|
.replaceAll("scalar DateTime", "")
|
|
35
|
-
.replaceAll(/input (.*?) {[\s\S]*?}/g, "")
|
|
35
|
+
.replaceAll(/input (.*?) {[\s\S]*?}/g, ""),
|
|
36
36
|
)
|
|
37
37
|
.join("\n")};
|
|
38
38
|
|
|
@@ -46,14 +46,15 @@ export const getDocumentModelTypeDefs = (
|
|
|
46
46
|
.replaceAll(/input (.*?) {[\s\S]*?}/g, "")
|
|
47
47
|
.replaceAll("type AccountSnapshotLocalState", "")
|
|
48
48
|
.replaceAll("type BudgetStatementLocalState", "")
|
|
49
|
-
.replaceAll("type ScopeFrameworkLocalState", "")
|
|
49
|
+
.replaceAll("type ScopeFrameworkLocalState", ""),
|
|
50
50
|
)
|
|
51
51
|
.join("\n")};
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
|
|
53
|
+
type ${documentModel.name} implements IDocument {
|
|
54
54
|
id: ID!
|
|
55
55
|
name: String!
|
|
56
56
|
documentType: String!
|
|
57
|
+
operations: [Operation!]!
|
|
57
58
|
revision: Int!
|
|
58
59
|
created: DateTime!
|
|
59
60
|
lastModified: DateTime!
|
|
@@ -65,13 +66,21 @@ export const getDocumentModelTypeDefs = (
|
|
|
65
66
|
const schema = `
|
|
66
67
|
${scalarsTypeDefs.join("\n")}
|
|
67
68
|
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
type Operation {
|
|
72
|
+
type: String!
|
|
73
|
+
index: Int!
|
|
74
|
+
timestamp: DateTime!
|
|
75
|
+
hash: String!
|
|
76
|
+
}
|
|
68
77
|
interface IDocument {
|
|
69
78
|
name: String!
|
|
70
79
|
documentType: String!
|
|
71
80
|
revision: Int!
|
|
72
81
|
created: DateTime!
|
|
73
82
|
lastModified: DateTime!
|
|
74
|
-
|
|
83
|
+
operations: [Operation!]!
|
|
75
84
|
}
|
|
76
85
|
${dmSchema}
|
|
77
86
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import pkg from "knex";
|
|
2
|
+
import ClientPgLite from "knex-pglite";
|
|
3
|
+
const knex = pkg;
|
|
4
|
+
|
|
5
|
+
function isPG(connectionString: string) {
|
|
6
|
+
if (connectionString.startsWith("postgres://")) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getKnexClient(connectionString: string) {
|
|
13
|
+
const isPg = isPG(connectionString);
|
|
14
|
+
const client = isPg ? "pg" : (ClientPgLite as typeof knex.Client);
|
|
15
|
+
const connection = connectionString;
|
|
16
|
+
|
|
17
|
+
return knex({
|
|
18
|
+
client,
|
|
19
|
+
connection,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getDrizzleClient(connectionString: string) {}
|
package/test/router.test.ts
CHANGED
|
@@ -8,8 +8,7 @@ import {
|
|
|
8
8
|
IDocumentDriveServer,
|
|
9
9
|
DocumentDriveServer,
|
|
10
10
|
} from "../../document-drive/src/server";
|
|
11
|
-
import {
|
|
12
|
-
import { initReactorRouter, reactorRouter } from "../src/router";
|
|
11
|
+
import { ReactorRouterManager } from "../src/router";
|
|
13
12
|
import { getDocumentModelTypeDefs } from "../src/utils/create-schema";
|
|
14
13
|
|
|
15
14
|
const documentModels = [
|
|
@@ -20,36 +19,33 @@ const documentModels = [
|
|
|
20
19
|
describe("Reactor Router", () => {
|
|
21
20
|
it("should be initialized", async () => {
|
|
22
21
|
const app = express();
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
expect(
|
|
26
|
-
expect(drive).toBeDefined();
|
|
27
|
-
expect("/system").toMatch(system.regexp);
|
|
28
|
-
expect("/drive").toMatch(drive.regexp);
|
|
22
|
+
const reactor = new DocumentDriveServer(documentModels);
|
|
23
|
+
const reactorRouter = new ReactorRouterManager("/", app, reactor);
|
|
24
|
+
await expect(reactorRouter.init()).resolves.toBeUndefined();
|
|
29
25
|
});
|
|
30
26
|
|
|
31
|
-
it("should be able to add a new subgraph", async () => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
27
|
+
// it("should be able to add a new subgraph", async () => {
|
|
28
|
+
// const driveServer = new DocumentDriveServer(documentModels);
|
|
29
|
+
// await driveServer.initialize();
|
|
30
|
+
// const newSubgraph = {
|
|
31
|
+
// name: "newSubgraph",
|
|
32
|
+
// getSchema: (documentDriveServer: IDocumentDriveServer) =>
|
|
33
|
+
// buildSubgraphSchema([
|
|
34
|
+
// {
|
|
35
|
+
// typeDefs: getDocumentModelTypeDefs(
|
|
36
|
+
// documentDriveServer,
|
|
37
|
+
// `
|
|
38
|
+
// type Query {
|
|
39
|
+
// hello: String
|
|
40
|
+
// }
|
|
41
|
+
// `,
|
|
42
|
+
// ),
|
|
43
|
+
// resolvers: { Query: { hello: () => "world" } },
|
|
44
|
+
// },
|
|
45
|
+
// ]),
|
|
46
|
+
// };
|
|
51
47
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
});
|
|
48
|
+
// await addSubgraph(newSubgraph);
|
|
49
|
+
// expect(reactorRouter.stack.length).gte(3);
|
|
50
|
+
// });
|
|
55
51
|
});
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
IDocumentDriveServer,
|
|
3
|
-
InternalTransmitter,
|
|
4
|
-
InternalTransmitterUpdate,
|
|
5
|
-
Listener,
|
|
6
|
-
} from "document-drive";
|
|
7
|
-
import { DocumentDriveDocument } from "document-model-libs/document-drive";
|
|
8
|
-
|
|
9
|
-
export type InternalListenerModule = {
|
|
10
|
-
name: string;
|
|
11
|
-
options: Omit<Listener, "driveId">;
|
|
12
|
-
transmit: (strands: InternalTransmitterUpdate[]) => Promise<void>;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export class InternalListenerManager {
|
|
16
|
-
private driveServer: IDocumentDriveServer;
|
|
17
|
-
private modules: InternalListenerModule[] = [];
|
|
18
|
-
|
|
19
|
-
constructor(driveServer: IDocumentDriveServer) {
|
|
20
|
-
this.driveServer = driveServer;
|
|
21
|
-
driveServer.on("driveAdded", this.#onDriveAdded.bind(this));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async #onDriveAdded(drive: DocumentDriveDocument) {
|
|
25
|
-
await Promise.all(
|
|
26
|
-
this.modules.map((module) =>
|
|
27
|
-
this.driveServer.addInternalListener(
|
|
28
|
-
drive.state.global.id,
|
|
29
|
-
{
|
|
30
|
-
transmit: (strands) => module.transmit(strands),
|
|
31
|
-
disconnect: async () => {
|
|
32
|
-
return Promise.resolve();
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
{ ...module.options, label: module.options.label ?? "" },
|
|
36
|
-
),
|
|
37
|
-
),
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async init() {
|
|
42
|
-
const drives = await this.driveServer.getDrives();
|
|
43
|
-
|
|
44
|
-
for (const { options, transmit } of this.modules) {
|
|
45
|
-
if (!options || !transmit) {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
for (const driveId of drives) {
|
|
50
|
-
try {
|
|
51
|
-
const { listenerId } = options;
|
|
52
|
-
const drive = await this.driveServer.getDrive(driveId);
|
|
53
|
-
const moduleRegistered =
|
|
54
|
-
drive.state.local.listeners.filter(
|
|
55
|
-
(l) => l.listenerId === listenerId,
|
|
56
|
-
).length > 0;
|
|
57
|
-
if (!moduleRegistered) {
|
|
58
|
-
await this.driveServer.addInternalListener(
|
|
59
|
-
driveId,
|
|
60
|
-
{
|
|
61
|
-
transmit: async (strands) => transmit(strands),
|
|
62
|
-
disconnect: async () => Promise.resolve(),
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
block: false,
|
|
66
|
-
filter: options.filter,
|
|
67
|
-
label: options.label!,
|
|
68
|
-
listenerId,
|
|
69
|
-
},
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const transmitter = await this.driveServer.getTransmitter(
|
|
76
|
-
driveId,
|
|
77
|
-
listenerId,
|
|
78
|
-
);
|
|
79
|
-
if (transmitter instanceof InternalTransmitter) {
|
|
80
|
-
transmitter.setReceiver({
|
|
81
|
-
transmit: async (strands: InternalTransmitterUpdate[]) => {
|
|
82
|
-
await transmit(strands);
|
|
83
|
-
return Promise.resolve();
|
|
84
|
-
},
|
|
85
|
-
disconnect: () => {
|
|
86
|
-
console.log(`Disconnecting listener ${options.listenerId}`);
|
|
87
|
-
return Promise.resolve();
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
} catch (e) {
|
|
92
|
-
console.error(
|
|
93
|
-
`Error while initializing listener ${options.listenerId} for drive ${driveId}`,
|
|
94
|
-
e,
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async registerInternalListener(module: InternalListenerModule) {
|
|
102
|
-
if (this.modules.find((m) => m.name === module.name)) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
this.modules.push(module);
|
|
106
|
-
await this.init();
|
|
107
|
-
}
|
|
108
|
-
}
|