UNPKG

@useorbis/db-sdk

Version:

Orbis' Typescript SDK for building open-data experiences.

273 lines (272 loc) 8.95 kB
import Ajv from "ajv/dist/2020.js"; import { StatementHistory } from "./historyProvider.js"; import { catchError } from "../../util/tryit.js"; import { OrbisError } from "../../util/results.js"; const ajv = new Ajv.Ajv2020(); export class BulkInsertStatement extends StatementHistory { #orbis; #tableName; #model; #context; #values = []; constructor(orbis, tableOrModelId) { super(); this.#orbis = orbis; if (this.#orbis.ceramic.isStreamIdString(tableOrModelId)) { this.#model = tableOrModelId; } else { this.#tableName = tableOrModelId; } } get documents() { return this.#values; } async getTableName() { if (this.#tableName) { return this.#tableName; } const { tableName } = await this.#orbis.node.getTableName(this.#model); if (!tableName) { this.#tableName = this.#model; return this.#tableName; } this.#tableName = tableName; return this.#tableName; } async getModelId() { if (this.#model) { return this.#model; } const { modelId } = await this.#orbis.node.getTableModelId(this.#tableName); if (!modelId || !this.#orbis.ceramic.isStreamIdString(modelId)) { throw "[QueryBuilder:insert] Unable to convert tableName to modelId. Use raw modelId instead."; } this.#model = modelId; return this.#model; } async validate() { const model = await this.getModelId(); const { validate } = await this.#orbis.query.fetchModel(model); const results = this.documents.map((document) => { const isValid = validate(document); if (!isValid) { return { valid: false, error: ajv.errorsText(validate.errors), document, }; } return { valid: true, document, }; }); if (results.some((result) => !result.valid)) { return { valid: false, errors: results.filter((result) => !result.valid), }; } return { valid: true, }; } value(v) { this.#values.push(v); return this; } values(values) { this.#values = [...this.#values, ...values]; return this; } context(context) { this.#context = context; return this; } async run() { if (!this.documents.length) { throw "[QueryBuilder:insert] Cannot create empty document, missing .value() or .values()"; } const timestamp = Date.now(); const model = await this.getModelId(); const { model: modelContent } = await this.#orbis.query.fetchModel(model); const accountRelation = (modelContent.accountRelation?.type || "list").toLowerCase(); const query = { documents: this.documents, context: this.#context, model, accountRelation: modelContent.accountRelation, }; const results = (await Promise.allSettled(this.documents.map(async (document) => { const [orbisDocument, error] = await catchError(() => { if (accountRelation === "single") { return this.#orbis.ceramic.createDocumentSingle({ content: document, context: this.#context, model, }); } // TODO: implementation, this is unverified if (accountRelation === "set") { const unique = modelContent.accountRelation.fields; return this.#orbis.ceramic.createDocumentSet({ content: document, context: this.#context, model, }, unique.map((key) => document[key])); } return this.#orbis.ceramic.createDocument({ content: document, context: this.#context, model, }); }); if (error) { return { error, document, }; } return orbisDocument; }))).map((result) => result.value); const success = results.filter((result) => typeof result.error === "undefined"); const errors = results.filter((result) => typeof result.error !== "undefined"); if (!errors.length) { super.storeResult({ timestamp, success: true, result: "All documents created successfully. Check .details for more.", query, details: { success, errors, }, }); } else { super.storeResult({ timestamp, success: false, error: "One or more documents failed to be created. Check .details for more.", query, details: { success, errors, }, }); } return { success, errors, }; } } export class InsertStatement extends StatementHistory { #orbis; #tableName; #model; #context; #value; constructor(orbis, tableOrModelId) { super(); this.#orbis = orbis; if (this.#orbis.ceramic.isStreamIdString(tableOrModelId)) { this.#model = tableOrModelId; } else { this.#tableName = tableOrModelId; } } get document() { return this.#value; } async getTableName() { if (this.#tableName) { return this.#tableName; } const { tableName } = await this.#orbis.node.getTableName(this.#model); if (!tableName) { this.#tableName = this.#model; return this.#tableName; } this.#tableName = tableName; return this.#tableName; } async getModelId() { if (this.#model) { return this.#model; } const { modelId } = await this.#orbis.node.getTableModelId(this.#tableName); if (!modelId || !this.#orbis.ceramic.isStreamIdString(modelId)) { throw "[QueryBuilder:insert] Unable to convert tableName to modelId. Use raw modelId instead."; } this.#model = modelId; return this.#model; } async validate() { const model = await this.getModelId(); const { validate } = await this.#orbis.query.fetchModel(model); const isValid = validate(this.#value); if (!isValid) { return { valid: false, error: ajv.errorsText(validate.errors), }; } return { valid: true, }; } value(v) { this.#value = v; return this; } context(context) { this.#context = context; return this; } async run() { if (!this.#value) { throw "[QueryBuilder:insert] Cannot create empty document, missing .value()"; } const timestamp = Date.now(); const model = await this.getModelId(); const { model: modelContent } = await this.#orbis.query.fetchModel(model); const accountRelation = (modelContent.accountRelation?.type || "list").toLowerCase(); const query = { content: this.#value, context: this.#context, model, // pass additional info, such as fields ("set") accountRelation: modelContent.accountRelation, }; const [document, error] = await catchError(() => { if (accountRelation === "single") { return this.#orbis.ceramic.createDocumentSingle(query); } // TODO: implementation, this is unverified if (accountRelation === "set") { const unique = modelContent.accountRelation.fields; return this.#orbis.ceramic.createDocumentSet(query, unique.map((key) => query.content[key])); } return this.#orbis.ceramic.createDocument(query); }); if (error) { super.storeResult({ timestamp, success: false, error, query, }); throw new OrbisError(error.message, { error, query }); } super.storeResult({ timestamp, success: true, result: document, query, }); return document; } }