@sentio/sdk 2.36.1 → 2.37.0-rc.10
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/lib/core/base-context.d.ts +4 -0
- package/lib/core/base-context.d.ts.map +1 -1
- package/lib/core/base-context.js +20 -4
- package/lib/core/base-context.js.map +1 -1
- package/lib/core/core-plugin.d.ts.map +1 -1
- package/lib/core/core-plugin.js +8 -0
- package/lib/core/core-plugin.js.map +1 -1
- package/lib/core/database-schema.d.ts +15 -0
- package/lib/core/database-schema.d.ts.map +1 -0
- package/lib/core/database-schema.js +16 -0
- package/lib/core/database-schema.js.map +1 -0
- package/lib/core/index.d.ts +1 -0
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/index.js +1 -0
- package/lib/core/index.js.map +1 -1
- package/lib/fuel/fuel-processor.d.ts.map +1 -1
- package/lib/fuel/fuel-processor.js +51 -23
- package/lib/fuel/fuel-processor.js.map +1 -1
- package/lib/store/codegen.d.ts +4 -0
- package/lib/store/codegen.d.ts.map +1 -0
- package/lib/store/codegen.js +221 -0
- package/lib/store/codegen.js.map +1 -0
- package/lib/store/context.d.ts +2 -0
- package/lib/store/context.d.ts.map +1 -0
- package/lib/store/context.js +2 -0
- package/lib/store/context.js.map +1 -0
- package/lib/store/decorators.d.ts +12 -0
- package/lib/store/decorators.d.ts.map +1 -0
- package/lib/store/decorators.js +25 -0
- package/lib/store/decorators.js.map +1 -0
- package/lib/store/entity.d.ts +17 -0
- package/lib/store/entity.d.ts.map +1 -0
- package/lib/store/entity.js +61 -0
- package/lib/store/entity.js.map +1 -0
- package/lib/store/index.d.ts +6 -0
- package/lib/store/index.d.ts.map +1 -0
- package/lib/store/index.js +6 -0
- package/lib/store/index.js.map +1 -0
- package/lib/store/run.d.ts +2 -0
- package/lib/store/run.d.ts.map +1 -0
- package/lib/store/run.js +11 -0
- package/lib/store/run.js.map +1 -0
- package/lib/store/schema.d.ts +7 -0
- package/lib/store/schema.d.ts.map +1 -0
- package/lib/store/schema.js +30 -0
- package/lib/store/schema.js.map +1 -0
- package/lib/store/store.d.ts +12 -0
- package/lib/store/store.d.ts.map +1 -0
- package/lib/store/store.js +74 -0
- package/lib/store/store.js.map +1 -0
- package/lib/store/types.d.ts +10 -0
- package/lib/store/types.d.ts.map +1 -0
- package/lib/store/types.js +2 -0
- package/lib/store/types.js.map +1 -0
- package/lib/testing/test-processor-server.d.ts +2 -5
- package/lib/testing/test-processor-server.d.ts.map +1 -1
- package/lib/testing/test-processor-server.js +2 -2
- package/lib/testing/test-processor-server.js.map +1 -1
- package/package.json +8 -5
- package/src/core/base-context.ts +22 -4
- package/src/core/core-plugin.ts +9 -0
- package/src/core/database-schema.ts +24 -0
- package/src/core/index.ts +1 -0
- package/src/fuel/fuel-processor.ts +64 -39
- package/src/store/codegen.ts +256 -0
- package/src/store/context.ts +1 -0
- package/src/store/decorators.ts +32 -0
- package/src/store/entity.ts +71 -0
- package/src/store/index.ts +5 -0
- package/src/store/run.ts +10 -0
- package/src/store/schema.ts +35 -0
- package/src/store/store.ts +80 -0
- package/src/store/types.ts +10 -0
- package/src/testing/test-processor-server.ts +13 -2
@@ -89,30 +89,42 @@ export class FuelProcessor implements FuelBaseProcessor<FuelProcessorConfig> {
|
|
89
89
|
|
90
90
|
const callHandler = {
|
91
91
|
handler: async (call: Data_FuelCall) => {
|
92
|
-
|
93
|
-
|
94
|
-
|
92
|
+
try {
|
93
|
+
const contract = new Contract(this.config.address, abi, this.provider)
|
94
|
+
const gqlTransaction = call.transaction
|
95
|
+
const tx = decodeFuelTransactionWithAbi(gqlTransaction, { [this.config.address]: abi }, this.provider)
|
95
96
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
97
|
+
const ctx = new FuelContext(
|
98
|
+
this.config.chainId,
|
99
|
+
this.config.address,
|
100
|
+
this.config.name ?? this.config.address,
|
101
|
+
tx
|
102
|
+
)
|
103
|
+
for (const op of tx.operations) {
|
104
|
+
for (const call of op.calls || []) {
|
105
|
+
if (names.has(call.functionName)) {
|
106
|
+
const fn = contract.functions[call.functionName]
|
107
|
+
const args = Object.values(call.argumentsProvided || {})
|
108
|
+
const scope = fn(...args)
|
109
|
+
const invocationResult = new FuelCall(scope, tx, false, call.argumentsProvided, tx.logs)
|
110
|
+
await handler(invocationResult, ctx)
|
111
|
+
}
|
111
112
|
}
|
112
113
|
}
|
113
|
-
}
|
114
114
|
|
115
|
-
|
115
|
+
return ctx.stopAndGetResult()
|
116
|
+
} catch (e) {
|
117
|
+
console.error(e)
|
118
|
+
return {
|
119
|
+
gauges: [],
|
120
|
+
counters: [],
|
121
|
+
events: [],
|
122
|
+
exports: [],
|
123
|
+
states: {
|
124
|
+
configUpdated: false
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}
|
116
128
|
},
|
117
129
|
fetchConfig: {
|
118
130
|
filters: Object.values(filters)
|
@@ -130,27 +142,40 @@ export class FuelProcessor implements FuelBaseProcessor<FuelProcessorConfig> {
|
|
130
142
|
|
131
143
|
const callHandler = {
|
132
144
|
handler: async (call: Data_FuelCall) => {
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
const results: ProcessResult[] = []
|
141
|
-
const logs = (tx.logs || []).filter((log) => logIds.has(log.logId))
|
142
|
-
for (const log of logs) {
|
143
|
-
const ctx = new FuelContext(
|
144
|
-
this.config.chainId,
|
145
|
-
this.config.address,
|
146
|
-
this.config.name ?? this.config.address,
|
147
|
-
tx
|
145
|
+
try {
|
146
|
+
const gqlTransaction = call.transaction
|
147
|
+
const tx = decodeFuelTransactionWithAbi(
|
148
|
+
gqlTransaction,
|
149
|
+
{ [this.config.address]: this.config.abi! },
|
150
|
+
this.provider
|
148
151
|
)
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
+
|
153
|
+
const results: ProcessResult[] = []
|
154
|
+
const logs = (tx.logs || []).filter((log) => logIds.has(log.logId))
|
155
|
+
for (const log of logs) {
|
156
|
+
const ctx = new FuelContext(
|
157
|
+
this.config.chainId,
|
158
|
+
this.config.address,
|
159
|
+
this.config.name ?? this.config.address,
|
160
|
+
tx
|
161
|
+
)
|
162
|
+
ctx.setLogIndex(log.receiptIndex)
|
163
|
+
await handler(log, ctx)
|
164
|
+
results.push(ctx.stopAndGetResult())
|
165
|
+
}
|
166
|
+
return mergeProcessResults(results)
|
167
|
+
} catch (e) {
|
168
|
+
console.error(e)
|
169
|
+
return {
|
170
|
+
gauges: [],
|
171
|
+
counters: [],
|
172
|
+
events: [],
|
173
|
+
exports: [],
|
174
|
+
states: {
|
175
|
+
configUpdated: false
|
176
|
+
}
|
177
|
+
}
|
152
178
|
}
|
153
|
-
return mergeProcessResults(results)
|
154
179
|
},
|
155
180
|
logConfig: {
|
156
181
|
logIds: Array.from(logIds)
|
@@ -0,0 +1,256 @@
|
|
1
|
+
import {
|
2
|
+
DirectiveNode,
|
3
|
+
GraphQLEnumType,
|
4
|
+
GraphQLField,
|
5
|
+
GraphQLInterfaceType,
|
6
|
+
GraphQLList,
|
7
|
+
GraphQLNonNull,
|
8
|
+
GraphQLObjectType,
|
9
|
+
GraphQLOutputType,
|
10
|
+
GraphQLScalarType,
|
11
|
+
GraphQLSchema,
|
12
|
+
Kind,
|
13
|
+
isObjectType,
|
14
|
+
isInterfaceType,
|
15
|
+
isListType
|
16
|
+
} from 'graphql'
|
17
|
+
import * as fs from 'node:fs'
|
18
|
+
import path from 'path'
|
19
|
+
import mkdirp from 'mkdirp'
|
20
|
+
import { schemaFromFile } from './schema.js'
|
21
|
+
import chalk from 'chalk'
|
22
|
+
|
23
|
+
export async function codegen(srcDir: string, outputDir: string) {
|
24
|
+
for (const file of fs.readdirSync(srcDir)) {
|
25
|
+
const f = path.join(srcDir, file)
|
26
|
+
const filePath = path.parse(f)
|
27
|
+
if (filePath.ext == '.graphql') {
|
28
|
+
const { schema, source } = schemaFromFile(f)
|
29
|
+
const target = path.join(outputDir, filePath.name + '.ts')
|
30
|
+
await codegenInternal(schema, source, target)
|
31
|
+
console.log(chalk.green(`Generated ${target}`))
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
async function codegenInternal(schema: GraphQLSchema, source: string, target: string) {
|
37
|
+
const results: string[] = [
|
38
|
+
'/* Autogenerated file. Do not edit manually. */\n',
|
39
|
+
'/* tslint:disable */',
|
40
|
+
'/* eslint-disable */',
|
41
|
+
"import {entity, derivedFrom, DateTime, Json, Bytes, ID, Entity, Store} from '@sentio/sdk/store'",
|
42
|
+
`import { DatabaseSchema, BigDecimal } from "@sentio/sdk"`
|
43
|
+
]
|
44
|
+
const entities: string[] = []
|
45
|
+
for (const t of Object.values(schema.getTypeMap())) {
|
46
|
+
if (t.name.startsWith('__')) {
|
47
|
+
continue
|
48
|
+
}
|
49
|
+
|
50
|
+
if (t instanceof GraphQLObjectType) {
|
51
|
+
const entityContent = genEntity(t)
|
52
|
+
results.push(entityContent)
|
53
|
+
entities.push(t.name)
|
54
|
+
}
|
55
|
+
if (t instanceof GraphQLEnumType) {
|
56
|
+
results.push(genEnum(t))
|
57
|
+
}
|
58
|
+
if (t instanceof GraphQLInterfaceType) {
|
59
|
+
results.push(genInterface(t))
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
const contents =
|
64
|
+
results.join('\n') +
|
65
|
+
`\n
|
66
|
+
const source = \`${source.replaceAll('`', '`')}\`
|
67
|
+
DatabaseSchema.register({
|
68
|
+
source,
|
69
|
+
entities: {
|
70
|
+
${entities.map((e) => `"${e}": ${e}`).join(',\n\t\t')}
|
71
|
+
}
|
72
|
+
})
|
73
|
+
`
|
74
|
+
await mkdirp(path.dirname(target))
|
75
|
+
|
76
|
+
fs.writeFileSync(target, contents)
|
77
|
+
}
|
78
|
+
|
79
|
+
const JsTypes: Record<string, string> = {
|
80
|
+
ID: 'string',
|
81
|
+
String: 'string',
|
82
|
+
Int: 'number',
|
83
|
+
Float: 'number',
|
84
|
+
Boolean: 'boolean',
|
85
|
+
BigInt: 'bigint',
|
86
|
+
BigDecimal: 'BigDecimal',
|
87
|
+
DateTime: 'Date',
|
88
|
+
Json: 'any',
|
89
|
+
Bytes: 'Uint8Array'
|
90
|
+
}
|
91
|
+
const graphqlTypes = Object.entries(JsTypes).reduce(
|
92
|
+
(acc, [k, v]) => {
|
93
|
+
acc[v] = k
|
94
|
+
return acc
|
95
|
+
},
|
96
|
+
{} as Record<string, string>
|
97
|
+
)
|
98
|
+
|
99
|
+
function genType(type: GraphQLOutputType): string {
|
100
|
+
if (type instanceof GraphQLNonNull) {
|
101
|
+
return genType(type.ofType)
|
102
|
+
} else if (type instanceof GraphQLScalarType) {
|
103
|
+
return type.name
|
104
|
+
} else if (type instanceof GraphQLObjectType || type instanceof GraphQLInterfaceType) {
|
105
|
+
return type.name
|
106
|
+
} else if (type instanceof GraphQLList) {
|
107
|
+
return `Array<${genType(type.ofType)}>`
|
108
|
+
} else if (type instanceof GraphQLEnumType) {
|
109
|
+
return type.name
|
110
|
+
} else {
|
111
|
+
throw new Error('Unsupported type: ' + type)
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
function isObject(type: GraphQLOutputType) {
|
116
|
+
if (type instanceof GraphQLNonNull) {
|
117
|
+
return isObject(type.ofType)
|
118
|
+
} else if (type instanceof GraphQLList) {
|
119
|
+
return isObject(type.ofType)
|
120
|
+
}
|
121
|
+
return isObjectType(type) || isInterfaceType(type)
|
122
|
+
}
|
123
|
+
|
124
|
+
function isList(type: GraphQLOutputType) {
|
125
|
+
if (type instanceof GraphQLNonNull) {
|
126
|
+
return isListType(type.ofType)
|
127
|
+
} else {
|
128
|
+
return isListType(type)
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
function getElementType(type: GraphQLOutputType) {
|
133
|
+
if (type instanceof GraphQLNonNull) {
|
134
|
+
return getElementType(type.ofType)
|
135
|
+
} else if (type instanceof GraphQLList) {
|
136
|
+
return type.ofType
|
137
|
+
}
|
138
|
+
return type
|
139
|
+
}
|
140
|
+
|
141
|
+
function genField(field: GraphQLField<any, any>) {
|
142
|
+
const isNonNull = field.type instanceof GraphQLNonNull
|
143
|
+
const directives = field.astNode?.directives?.map((d) => '\t' + directive2decorator(d) + '\n') || []
|
144
|
+
|
145
|
+
const type = genType(field.type)
|
146
|
+
const returnType = isNonNull ? type : `${type} | undefined`
|
147
|
+
|
148
|
+
if (isObject(field.type)) {
|
149
|
+
const t = getElementType(field.type)
|
150
|
+
const typeAsArg = isInterfaceType(t) ? `"${t}"` : genType(t)
|
151
|
+
|
152
|
+
if (isList(field.type)) {
|
153
|
+
return `${directives.join()}\tget ${field.name}(): Promise<${genType(t)}[]> { return this.getFieldObjectArray(${typeAsArg}, "${field.name}") as Promise<${genType(t)}[]> }
|
154
|
+
set ${field.name}(value: ${type} | ID[]) { this.set("${field.name}", value) }
|
155
|
+
get ${field.name}Ids(): ID[] { return this.get("${field.name}") }`
|
156
|
+
}
|
157
|
+
|
158
|
+
return `${directives.join()}\tget ${field.name}(): Promise<${genType(t)} | undefined> { return this.getFieldObject(${typeAsArg},"${field.name}") as Promise<${genType(t)} | undefined> }
|
159
|
+
set ${field.name}(value: ${type} | ID) { this.set("${field.name}", value) }
|
160
|
+
get ${field.name}Id(): ID | undefined { return this.get("${field.name}") }`
|
161
|
+
}
|
162
|
+
|
163
|
+
if (returnType == 'BigInt') {
|
164
|
+
return `${directives.join()}\tget ${field.name}(): ${returnType} { return BigInt(this.get("${field.name}")) }
|
165
|
+
set ${field.name}(value: ${type}) { this.set("${field.name}", value.toString()) }`
|
166
|
+
}
|
167
|
+
if (returnType == 'BigDecimal') {
|
168
|
+
return `${directives.join()}\tget ${field.name}(): ${returnType} { return new BigDecimal(this.get("${field.name}")) }
|
169
|
+
set ${field.name}(value: ${type}) { this.set("${field.name}", value.toString()) }`
|
170
|
+
}
|
171
|
+
|
172
|
+
if (returnType == 'Bytes') {
|
173
|
+
return `${directives.join()}\tget ${field.name}(): ${returnType} { return Uint8Array.from(Buffer.from(this.get<string>("${field.name}"), 'hex')) ) }
|
174
|
+
set ${field.name}(value: ${type}) { this.set("${field.name}", Buffer.from(value).toString('hex')) }`
|
175
|
+
}
|
176
|
+
|
177
|
+
return `${directives.join()}\tget ${field.name}(): ${returnType} { return this.get("${field.name}") }
|
178
|
+
set ${field.name}(value: ${type}) { this.set("${field.name}", value) }`
|
179
|
+
}
|
180
|
+
|
181
|
+
function genDataType(t: GraphQLObjectType<any, any>) {
|
182
|
+
let output = `type ${t.name}Data = `
|
183
|
+
const relationsFields = Object.values(t.getFields()).filter((f) => isObject(f.type))
|
184
|
+
if (relationsFields.length > 0) {
|
185
|
+
output += `Omit<${t.name}, ${relationsFields.map((f) => `"${f.name}"`).join(' | ')}>`
|
186
|
+
|
187
|
+
output +=
|
188
|
+
' & {' +
|
189
|
+
relationsFields
|
190
|
+
.map((f) => {
|
191
|
+
const type = genType(getElementType(f.type))
|
192
|
+
return `${f.name}?: ${isList(f.type) ? `Array<ID|${type}>` : `ID | ${type}`}`
|
193
|
+
})
|
194
|
+
.join(', ') +
|
195
|
+
'}'
|
196
|
+
} else {
|
197
|
+
output += `${t.name}`
|
198
|
+
}
|
199
|
+
return output
|
200
|
+
}
|
201
|
+
|
202
|
+
function genEntity(t: GraphQLObjectType<any, any>) {
|
203
|
+
const decorators = t.astNode?.directives?.filter((d) => d.name.value != 'entity').map(directive2decorator) || []
|
204
|
+
|
205
|
+
let impls = ''
|
206
|
+
if (t.getInterfaces().length > 0) {
|
207
|
+
impls +=
|
208
|
+
' implements ' +
|
209
|
+
t
|
210
|
+
.getInterfaces()
|
211
|
+
.map((i) => i.name)
|
212
|
+
.join(', ')
|
213
|
+
}
|
214
|
+
|
215
|
+
return `
|
216
|
+
${genDataType(t)}
|
217
|
+
${decorators.join('\n')}
|
218
|
+
@entity("${t.name}")
|
219
|
+
export class ${t.name} extends Entity${impls} {
|
220
|
+
constructor(data: Partial<${t.name}Data>) {
|
221
|
+
super(data)
|
222
|
+
}
|
223
|
+
${Object.values(t.getFields()).map(genField).join('\n')}
|
224
|
+
}`
|
225
|
+
}
|
226
|
+
|
227
|
+
function genInterface(t: GraphQLInterfaceType) {
|
228
|
+
return `
|
229
|
+
export interface ${t.name} {
|
230
|
+
${Object.values(t.getFields())
|
231
|
+
.map((f) => `\t${f.name}: ${genType(f.type)}`)
|
232
|
+
.join('\n')}
|
233
|
+
}`
|
234
|
+
}
|
235
|
+
|
236
|
+
export function directive2decorator(directive: DirectiveNode) {
|
237
|
+
let s = `@${directive.name.value}`
|
238
|
+
if (directive.arguments?.length) {
|
239
|
+
s += `(${directive.arguments
|
240
|
+
?.map((arg) => {
|
241
|
+
return arg.value.kind === Kind.STRING ? `"${arg.value.value}"` : `${arg.value}`
|
242
|
+
})
|
243
|
+
.join(', ')})`
|
244
|
+
}
|
245
|
+
return s
|
246
|
+
}
|
247
|
+
|
248
|
+
function genEnum(t: GraphQLEnumType) {
|
249
|
+
return `
|
250
|
+
export enum ${t.name} {
|
251
|
+
${t
|
252
|
+
.getValues()
|
253
|
+
.map((v) => `\t${v.name}`)
|
254
|
+
.join(', ')}
|
255
|
+
}`
|
256
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export { StoreContext } from '@sentio/runtime'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
type Constructor = { new (...args: any[]): any }
|
2
|
+
|
3
|
+
export function entity(name: string) {
|
4
|
+
return function <T extends Constructor>(constructor: T, context: any) {
|
5
|
+
constructor.prototype.entityName = name
|
6
|
+
return constructor
|
7
|
+
}
|
8
|
+
}
|
9
|
+
|
10
|
+
export function derivedFrom(field: string) {
|
11
|
+
return function (target: any, context: any) {}
|
12
|
+
}
|
13
|
+
|
14
|
+
export function unique(field: string) {
|
15
|
+
return function (target: any, context: any) {}
|
16
|
+
}
|
17
|
+
|
18
|
+
export function index(field: string[]) {
|
19
|
+
return function (target: any, context: any) {}
|
20
|
+
}
|
21
|
+
|
22
|
+
export function fulltext(query: string) {
|
23
|
+
return function (target: any, context: any) {}
|
24
|
+
}
|
25
|
+
|
26
|
+
export function cardinality(value: number) {
|
27
|
+
return function (target: any, context: any) {}
|
28
|
+
}
|
29
|
+
|
30
|
+
export function byteWeight(value: number) {
|
31
|
+
return function (target: any, context: any) {}
|
32
|
+
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import { ID } from './types.js'
|
2
|
+
import { PluginManager } from '@sentio/runtime'
|
3
|
+
import { Store } from './store.js'
|
4
|
+
|
5
|
+
export interface EntityClass<T extends Entity> {
|
6
|
+
new (data: any): T
|
7
|
+
}
|
8
|
+
|
9
|
+
export class Entity {
|
10
|
+
get id(): ID {
|
11
|
+
return this.get('id')
|
12
|
+
}
|
13
|
+
|
14
|
+
data: Record<string, any> = {}
|
15
|
+
constructor(data: any) {
|
16
|
+
Object.entries(data).forEach(([key, value]) => {
|
17
|
+
if (Array.isArray(value)) {
|
18
|
+
this.data[key] = value.map((v) => this.getIdFromEntity(v))
|
19
|
+
} else {
|
20
|
+
this.data[key] = this.getIdFromEntity(value)
|
21
|
+
}
|
22
|
+
})
|
23
|
+
}
|
24
|
+
|
25
|
+
private getIdFromEntity(entity: any): any {
|
26
|
+
if (entity instanceof Entity) {
|
27
|
+
return entity.id
|
28
|
+
} else if (typeof entity === 'object' && entity.id) {
|
29
|
+
return entity.id
|
30
|
+
}
|
31
|
+
return entity
|
32
|
+
}
|
33
|
+
|
34
|
+
private getStore() {
|
35
|
+
const dbContext = PluginManager.INSTANCE.dbContextLocalStorage.getStore()
|
36
|
+
if (dbContext) {
|
37
|
+
return new Store(dbContext)
|
38
|
+
}
|
39
|
+
return undefined
|
40
|
+
}
|
41
|
+
|
42
|
+
get<T>(field: string): T {
|
43
|
+
return this.data[field]
|
44
|
+
}
|
45
|
+
|
46
|
+
set<T>(field: string, value: T | T[] | ID | ID[]): void {
|
47
|
+
if (Array.isArray(value) && value instanceof Entity) {
|
48
|
+
this.data[field] = value.map((v) => (v as Entity).id)
|
49
|
+
} else if (value instanceof Entity) {
|
50
|
+
this.data[field] = (value as Entity).id
|
51
|
+
}
|
52
|
+
this.data[field] = value
|
53
|
+
}
|
54
|
+
|
55
|
+
protected getFieldObject<T extends Entity>(entity: EntityClass<T> | string, field: string): Promise<T | undefined> {
|
56
|
+
const id = this.data[field]
|
57
|
+
return id ? (this.getStore()?.get(entity, id) as Promise<T>) : Promise.resolve(undefined)
|
58
|
+
}
|
59
|
+
|
60
|
+
protected getFieldObjectArray<T extends Entity>(entity: EntityClass<T>, field: string): Promise<T[]> {
|
61
|
+
const ids = this.data[field]
|
62
|
+
const promises = ids.map((id: string) => this.getStore()?.get(entity, id))
|
63
|
+
return Promise.all(promises) as Promise<T[]>
|
64
|
+
}
|
65
|
+
|
66
|
+
toString(): string {
|
67
|
+
const entityName = this.constructor.prototype.entityName
|
68
|
+
const id = this.id
|
69
|
+
return `${entityName}#${id} ${JSON.stringify(this.data)}`
|
70
|
+
}
|
71
|
+
}
|
package/src/store/run.ts
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
import { buildASTSchema, DocumentNode, extendSchema, GraphQLSchema, parse, validateSchema } from 'graphql/index.js'
|
2
|
+
import * as fs from 'node:fs'
|
3
|
+
|
4
|
+
const customScalars = ['BigInt', 'BigDecimal', 'DateTime', 'JSON', 'Bytes', 'ID']
|
5
|
+
|
6
|
+
const baseSchema = buildASTSchema(
|
7
|
+
parse(`
|
8
|
+
directive @entity on OBJECT
|
9
|
+
directive @query on INTERFACE
|
10
|
+
directive @derivedFrom(field: String!) on FIELD_DEFINITION
|
11
|
+
directive @unique on FIELD_DEFINITION
|
12
|
+
directive @index(fields: [String!] unique: Boolean) repeatable on OBJECT | FIELD_DEFINITION
|
13
|
+
directive @fulltext(query: String!) on FIELD_DEFINITION
|
14
|
+
directive @cardinality(value: Int!) on OBJECT | FIELD_DEFINITION
|
15
|
+
directive @byteWeight(value: Float!) on FIELD_DEFINITION
|
16
|
+
directive @variant on OBJECT # legacy
|
17
|
+
directive @jsonField on OBJECT # legacy
|
18
|
+
${customScalars.map((name) => 'scalar ' + name).join('\n')}
|
19
|
+
`)
|
20
|
+
)
|
21
|
+
|
22
|
+
export function buildSchema(doc: DocumentNode): GraphQLSchema {
|
23
|
+
const schema = extendSchema(baseSchema, doc)
|
24
|
+
const errors = validateSchema(schema).filter((err) => !/query root/i.test(err.message))
|
25
|
+
if (errors.length > 0) {
|
26
|
+
throw errors[0]
|
27
|
+
}
|
28
|
+
return schema
|
29
|
+
}
|
30
|
+
|
31
|
+
export function schemaFromFile(filePath: string) {
|
32
|
+
const source = fs.readFileSync(filePath, 'utf-8')
|
33
|
+
const doc = parse(source)
|
34
|
+
return { schema: buildSchema(doc), source }
|
35
|
+
}
|
@@ -0,0 +1,80 @@
|
|
1
|
+
import { Entity, EntityClass } from './entity.js'
|
2
|
+
import { StoreContext } from './context.js'
|
3
|
+
import { DatabaseSchema } from '../core/index.js'
|
4
|
+
|
5
|
+
export class Store {
|
6
|
+
constructor(private readonly context: StoreContext) {}
|
7
|
+
|
8
|
+
async get<T extends Entity>(entity: EntityClass<T> | string, id: string): Promise<T | undefined> {
|
9
|
+
const promise = this.context.sendRequest({
|
10
|
+
get: {
|
11
|
+
entity: typeof entity == 'string' ? entity : entity.prototype.entityName,
|
12
|
+
id
|
13
|
+
}
|
14
|
+
})
|
15
|
+
|
16
|
+
const data = (await promise) as any
|
17
|
+
if (data?.['id'] != null) {
|
18
|
+
return this.newEntity(entity, data)
|
19
|
+
}
|
20
|
+
return undefined
|
21
|
+
}
|
22
|
+
|
23
|
+
async delete(entity: EntityClass<any>, id: string | string[]): Promise<void> {
|
24
|
+
const toBeDeleted = []
|
25
|
+
if (Array.isArray(id)) {
|
26
|
+
for (const i of id) {
|
27
|
+
toBeDeleted.push({ entity: entity.prototype.entityName, id: i })
|
28
|
+
}
|
29
|
+
} else {
|
30
|
+
toBeDeleted.push({ entity: entity.prototype.entityName, id })
|
31
|
+
}
|
32
|
+
await this.context.sendRequest({
|
33
|
+
delete: {
|
34
|
+
entity: toBeDeleted.map((e) => e.entity) as string[],
|
35
|
+
id: toBeDeleted.map((e) => e.id) as string[]
|
36
|
+
}
|
37
|
+
})
|
38
|
+
}
|
39
|
+
|
40
|
+
async upsert<T extends Entity>(entity: T | T[]): Promise<void> {
|
41
|
+
const entities = Array.isArray(entity) ? entity : [entity]
|
42
|
+
const promise = this.context.sendRequest({
|
43
|
+
upsert: {
|
44
|
+
entity: entities.map((e) => e.constructor.prototype.entityName),
|
45
|
+
data: entities.map((e) => e.data),
|
46
|
+
id: entities.map((e) => e.id)
|
47
|
+
}
|
48
|
+
})
|
49
|
+
|
50
|
+
await promise
|
51
|
+
}
|
52
|
+
|
53
|
+
async list<T extends Entity>(entity: EntityClass<T>, limit?: number, offset?: number): Promise<T[]> {
|
54
|
+
const promise = this.context.sendRequest({
|
55
|
+
list: {
|
56
|
+
entity: entity.constructor.prototype.entityName,
|
57
|
+
limit,
|
58
|
+
offset
|
59
|
+
}
|
60
|
+
})
|
61
|
+
|
62
|
+
const list = (await promise) as any[]
|
63
|
+
return list.map((data) => {
|
64
|
+
return this.newEntity(entity, data)
|
65
|
+
})
|
66
|
+
}
|
67
|
+
|
68
|
+
private newEntity<T extends Entity>(entity: EntityClass<T> | string, data: any) {
|
69
|
+
if (typeof entity == 'string') {
|
70
|
+
const en = DatabaseSchema.findEntity(entity)
|
71
|
+
if (!en) {
|
72
|
+
// it is an interface
|
73
|
+
return new Entity(data) as T
|
74
|
+
}
|
75
|
+
entity = en
|
76
|
+
}
|
77
|
+
|
78
|
+
return new (entity as EntityClass<T>)(data)
|
79
|
+
}
|
80
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
export type { BigDecimal } from '@sentio/bigdecimal'
|
2
|
+
|
3
|
+
export type ID = string
|
4
|
+
export type String = string
|
5
|
+
export type Int = number
|
6
|
+
export type Float = number
|
7
|
+
export type Boolean = boolean
|
8
|
+
export type DateTime = Date
|
9
|
+
export type Json = any
|
10
|
+
export type Bytes = Uint8Array
|
@@ -2,12 +2,16 @@ import {
|
|
2
2
|
AccountConfig,
|
3
3
|
ContractConfig,
|
4
4
|
DataBinding,
|
5
|
+
DeepPartial,
|
5
6
|
Empty,
|
6
7
|
ProcessBindingResponse,
|
7
8
|
ProcessBindingsRequest,
|
8
9
|
ProcessConfigRequest,
|
9
10
|
ProcessConfigResponse,
|
10
11
|
ProcessorServiceImplementation,
|
12
|
+
ProcessStreamRequest,
|
13
|
+
ProcessStreamResponse,
|
14
|
+
ServerStreamingMethodResult,
|
11
15
|
StartRequest
|
12
16
|
} from '@sentio/protos'
|
13
17
|
import { CallContext } from 'nice-grpc-common'
|
@@ -80,7 +84,14 @@ export class TestProcessorServer implements ProcessorServiceImplementation {
|
|
80
84
|
return this.service.processBindings({ bindings: [request] }, context)
|
81
85
|
}
|
82
86
|
|
83
|
-
processBindingsStream(
|
84
|
-
|
87
|
+
processBindingsStream(
|
88
|
+
requests: AsyncIterable<ProcessStreamRequest>,
|
89
|
+
context: CallContext
|
90
|
+
): ServerStreamingMethodResult<DeepPartial<ProcessStreamResponse>> {
|
91
|
+
throw new Error('Method not implemented.')
|
85
92
|
}
|
93
|
+
|
94
|
+
// processBindingsStream(request: AsyncIterable<ProcessStreamRequest>, context: CallContext) {
|
95
|
+
// return this.service.processBindingsStream(request, context)
|
96
|
+
// }
|
86
97
|
}
|