@powerhousedao/academy 3.3.0-dev.10 → 3.3.0-dev.12

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 CHANGED
@@ -1,3 +1,17 @@
1
+ ## 3.3.0-dev.12 (2025-07-17)
2
+
3
+ ### 🩹 Fixes
4
+
5
+ - **document-drive:** use lowercase letters when hashing relational processor namespace ([87c7944d3](https://github.com/powerhouse-inc/powerhouse/commit/87c7944d3))
6
+
7
+ ### ❤️ Thank You
8
+
9
+ - acaldas
10
+
11
+ ## 3.3.0-dev.11 (2025-07-16)
12
+
13
+ This was a version bump only for @powerhousedao/academy to align it with other projects, there were no code changes.
14
+
1
15
  ## 3.3.0-dev.10 (2025-07-15)
2
16
 
3
17
  ### 🩹 Fixes
@@ -0,0 +1,203 @@
1
+ # Build a Todo-List processor
2
+
3
+ 1. Generate the processor
4
+ 2. Define your database schema
5
+ 3. Customize the processor to your needs
6
+ 4. Test your processor
7
+ 5. Use the operational store in Frontend and Subgraph
8
+
9
+
10
+ ## Generate the Processor
11
+
12
+ In order to generate the processor you need to run the following command:
13
+ ```bash
14
+ ph generate --processor todo-processor --processor-type relational-db --document-types powerhouse/todolist
15
+ ```
16
+
17
+ With that command you create a processor named todo-processor which is of type relational db and listens on changes from documents of type powerhouse/todolist.
18
+
19
+ ## Define your database schema
20
+
21
+ As next step we need to define the db schema in the `processors/todo-processor/migration.ts` file.
22
+
23
+ The migration file has a up and a down function which gets called when either the processor was added or when the processor was removed.
24
+
25
+ Below you can find the example of a todo table.
26
+
27
+ ```ts
28
+ import { type IOperationalStore } from "document-drive/processors/types"
29
+
30
+ export async function up(db: IOperationalStore): Promise<void> {
31
+ // Create table
32
+ await db.schema
33
+ .createTable("todo")
34
+ .addColumn("name", "varchar(255)")
35
+ .addColumn("completed", "boolean")
36
+ .addPrimaryKeyConstraint("todo_pkey", ["name"])
37
+ .ifNotExists()
38
+ .execute();
39
+
40
+ const tables = await db.introspection.getTables();
41
+ console.log(tables);
42
+ }
43
+
44
+ export async function down(db: IOperationalStore): Promise<void> {
45
+ // drop table
46
+ await db.schema.dropTable("todo").execute();
47
+ }
48
+ ```
49
+
50
+ ## Generate Types
51
+
52
+ After defining your db schema its important to generate the types for typescript. This allows to create type safety queries and make use of code completion in your IDE when writing database queries.
53
+
54
+ Simply execute the following command.
55
+
56
+ ```bash
57
+ ph generate --migration-file processors/todo-indexer/migrations.js --schema-file processors/todo-indexer/schema.ts
58
+ ```
59
+
60
+ Afterwards check your `processors/todo-processor/schema.ts` file.
61
+ It will contain the types of your database.
62
+
63
+ ## Define the Filter
64
+
65
+ Checkout the `processors/todo-processor/factory.ts`.
66
+
67
+ Here you can define how the processor is being instantiated. In thise case it listens on powerhouse/todo-list document changes in the main branch and the global scope.
68
+
69
+ ```ts
70
+ export const todoProcessorProcessorFactory =
71
+ (module: IProcessorHostModule) =>
72
+ async (driveId: string): Promise<ProcessorRecord[]> => {
73
+ // Create a namespace for the processor and the provided drive id
74
+ const namespace = TodoProcessorProcessor.getNamespace(driveId);
75
+
76
+ // Create a filter for the processor
77
+ const filter: OperationalProcessorFilter = {
78
+ branch: ["main"],
79
+ documentId: ["*"],
80
+ documentType: ["powerhouse/todo-list"],
81
+ scope: ["global"],
82
+ };
83
+
84
+ // Create a namespaced store for the processor
85
+ const store = await createNamespacedDb<TodoProcessorProcessor>(
86
+ namespace,
87
+ module.operationalStore,
88
+ );
89
+
90
+ // Create the processor
91
+ const processor = new TodoProcessorProcessor(namespace, filter, store);
92
+ return [
93
+ {
94
+ processor,
95
+ filter,
96
+ },
97
+ ];
98
+ };
99
+
100
+ ```
101
+
102
+ ## Customize the logic of the processor
103
+
104
+ When you defined your db schema and the filter when your processor should receive processed operations its time to implement the actual logic.
105
+
106
+ In the following you'll find an example where we store all the created and udpated todos in a table.
107
+
108
+ ```ts
109
+ type DocumentType = ToDoListDocument;
110
+
111
+ export class TodoIndexerProcessor extends OperationalProcessor<DB> {
112
+
113
+ static override getNamespace(driveId: string): string {
114
+ // Default namespace: `${this.name}_${driveId.replaceAll("-", "_")}`
115
+ return super.getNamespace(driveId);
116
+ }
117
+
118
+ override async initAndUpgrade(): Promise<void> {
119
+ await up(this.operationalStore as IOperationalStore);
120
+ }
121
+
122
+ override async onStrands(
123
+ strands: InternalTransmitterUpdate<DocumentType>[],
124
+ ): Promise<void> {
125
+ if (strands.length === 0) {
126
+ return;
127
+ }
128
+
129
+ for (const strand of strands) {
130
+ if (strand.operations.length === 0) {
131
+ continue;
132
+ }
133
+
134
+ for (const operation of strand.operations) {
135
+ await this.operationalStore
136
+ .insertInto("todo")
137
+ .values({
138
+ task: strand.documentId,
139
+ status: true,
140
+ })
141
+ .execute();
142
+ }
143
+ }
144
+ }
145
+
146
+ async onDisconnect() {}
147
+ }
148
+
149
+ ```
150
+
151
+ ## Fetch Data through a Subgraph
152
+
153
+ ### Generate Subgraph
154
+
155
+ Simply generate a new subgraph with:
156
+ ```bash
157
+ ph generate --subgraph <subgraph-name>
158
+ ```
159
+
160
+ ### Fetch Data from Processor
161
+
162
+ open ```./subgraphs/<subgraph-name>/index.ts```
163
+
164
+
165
+
166
+ define the following:
167
+
168
+
169
+ ```ts
170
+ resolvers = {
171
+ Query: {
172
+ todoList: {
173
+ resolve: async (parent, args, context, info) => {
174
+ const todoList = await TodoProcessor.query(
175
+ args.driveId ?? "powerhouse",
176
+ this.operationalStore
177
+ )
178
+ .selectFrom("todo")
179
+ .selectAll()
180
+ .execute();
181
+ return todoList
182
+ },
183
+ },
184
+ },
185
+ };
186
+
187
+ typeDefs = gql`
188
+ type Query {
189
+ type Todo {
190
+ name: String!
191
+ completed: Boolean!
192
+ }
193
+
194
+ todoList(driveId: String): [Todo!]!
195
+ }
196
+ `;
197
+ ```
198
+
199
+
200
+ ### useOperationalStore Hook
201
+
202
+ .....
203
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powerhousedao/academy",
3
- "version": "3.3.0-dev.10",
3
+ "version": "3.3.0-dev.12",
4
4
  "homepage": "https://powerhouse.academy",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,169 +0,0 @@
1
- # Build a Todo-List processor
2
-
3
- 1. Generate the processor
4
- 2. Define your database schema
5
- 3. Customize the processor to your needs
6
- 4. Test your processor
7
- 5. Use the operational store in Frontend and Subgraph
8
-
9
-
10
- ## Generate the Processor
11
-
12
- In order to generate the processor you need to run the following command:
13
- ```bash
14
- ph generate --processor todo-processor --processor-type operational --document-types powerhouse/todolist
15
- ```
16
-
17
- ## Define your database schema
18
-
19
- in the migrations.ts file in your processor directory you can find the up and down methods which are being executed when the processor gets installed or removed.
20
-
21
- ```ts
22
- import { type IOperationalStore } from "document-drive/processors/types"
23
-
24
- export async function up(db: IOperationalStore): Promise<void> {
25
- // Create table
26
- await db.schema
27
- .createTable("todo")
28
- .addColumn("name", "varchar(255)")
29
- .addColumn("completed", "boolean")
30
- .addPrimaryKeyConstraint("todo_pkey", ["name"])
31
- .ifNotExists()
32
- .execute();
33
-
34
- const tables = await db.introspection.getTables();
35
- console.log(tables);
36
- }
37
-
38
- export async function down(db: IOperationalStore): Promise<void> {
39
- // drop table
40
- await db.schema.dropTable("todo").execute();
41
- }
42
- ```
43
-
44
- when you finished defining your database model you can generate the types for typescript from it with the following command:
45
-
46
- ```bash
47
- ph generate --migration-file processors/todo-indexer/migrations.js --schema-file processors/todo-indexer/schema.ts
48
- ```
49
-
50
-
51
- ## Customize the processor
52
-
53
- the index file contains the processor itsself with the default template:
54
-
55
- ```ts
56
- import {
57
- OperationalProcessor,
58
- type OperationalProcessorFilter,
59
- } from "document-drive/processors/operational-processor";
60
- import { type InternalTransmitterUpdate } from "document-drive/server/listener/transmitter/internal";
61
- import { up } from "./migrations.js";
62
- import { type DB } from "./schema.js";
63
- import type { ToDoListDocument } from "../../document-models/to-do-list/index.js";
64
-
65
- type DocumentType = ToDoListDocument;
66
-
67
- export class TodoIndexerProcessor extends OperationalProcessor<DB> {
68
- get filter(): OperationalProcessorFilter {
69
- return {
70
- branch: ["main"],
71
- documentId: ["*"],
72
- documentType: ["powerhouse/todolist"],
73
- scope: ["global"],
74
- };
75
- }
76
-
77
- async initAndUpgrade(): Promise<void> {
78
- await up(this.operationalStore);
79
- }
80
-
81
- async onStrands(
82
- strands: InternalTransmitterUpdate<DocumentType>[],
83
- ): Promise<void> {
84
- if (strands.length === 0) {
85
- return;
86
- }
87
-
88
- for (const strand of strands) {
89
- if (strand.operations.length === 0) {
90
- continue;
91
- }
92
-
93
- for (const operation of strand.operations) {
94
- console.log(">>> ", operation.type);
95
- await this.operationalStore
96
- .insertInto("todo")
97
- .values({
98
- task: strand.documentId,
99
- status: true,
100
- })
101
- .execute();
102
- }
103
- }
104
- }
105
-
106
- async onDisconnect() {}
107
- }
108
- ```
109
-
110
- As you can see you can define with the filter options when the processor is executed.
111
- In this case the processor is called when an operation on a powerhouse/todolist document was processed.
112
- The operations and the corresponding states will be passed to the onStrands method.
113
-
114
- Furthermore the Processor contains an initAndUpgrade function which is called when the processor is being activated. Next to the init there is also an onDisconnect function which is called when the processor is beging removed. The template of the operational database processor contains the up method of the migrations.ts file.
115
-
116
- Finally there is the onStrands method. Here you can update your operational database based upon your needs.
117
-
118
- ## test the processor
119
-
120
- .... todo
121
-
122
- ## use the operational database
123
-
124
- ### subgraph
125
-
126
- 1. generate subgraph with
127
-
128
- ```bash
129
- ph generate --subgraph <subgraph-name>
130
- ```
131
-
132
- open ```./subgraphs/<subgraph-name>/index.ts```
133
-
134
- define the following:
135
-
136
-
137
- ```
138
- resolvers = {
139
- Query: {
140
- todoList: {
141
- resolve: async (parent, args, context, info) => {
142
- const todoList = await this.operationalStore.selectFrom("todo").selectAll().execute();
143
- return todoList
144
- },
145
- },
146
- },
147
- };
148
-
149
- typeDefs = gql`
150
- type Query {
151
- type Todo {
152
- name: String!
153
- completed: Boolean!
154
- }
155
-
156
- todoList: [Todo!]!
157
- }
158
- `;
159
- ```
160
-
161
- you can simply do sql requests with the provided operationalstore in the class for example
162
- ```ts
163
- await this.operationalStore.selectFrom("todo").selectAll().execute();
164
- ```
165
-
166
- ### useOperationalStore Hook
167
-
168
- .....
169
-