@useorbis/db-sdk
Version:
Orbis' Typescript SDK for building open-data experiences.
273 lines (272 loc) • 8.95 kB
JavaScript
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;
}
}