philipgreat's picture
feat: explain seven TeaQL runtimes (part 2)
0ccd578 verified
Raw History Blame Contribute Delete
10.5 kB
import { CheckException, EntityKey, EntityRoot, GraphMutationSession, LoadedScalarSnapshot, MutationIntent, MutationTraceScope, ObjectLocation, SmartList, TeaQLDataService, UserContext } from '../../teaql-ts';
import { CustomerOrder } from './CustomerOrder';
export class Shipment {
private static teaqlTemporaryId = 0;
id?: string;
customerOrder?: CustomerOrder | string;
referenceCode?: string;
version?: number;
constructor(init?: Partial<Shipment>) {
Object.defineProperty(this, "_action", { value: init && init.id ? "Update" : "Create", writable: true, enumerable: false });
Object.defineProperty(this, "_comment", { value: undefined, writable: true, enumerable: false });
Object.defineProperty(this, "_fullyLoaded", { value: !init || !init.id, writable: true, enumerable: false });
Object.defineProperty(this, "_loadedFields", { value: new Set(Object.keys(init || {})), writable: true, enumerable: false });
Object.defineProperty(this, "_root", { value: new EntityRoot(), writable: true, enumerable: false });
Object.defineProperty(this, "_ledgerId", { value: init?.id ?? --Shipment.teaqlTemporaryId, writable: true, enumerable: false });
if (init) {
const fields: Record<string, unknown> = { ...(init as Record<string, unknown>) };
Object.assign(this, fields);
if ((init as any).customerOrder && typeof (init as any).customerOrder === "object") {
this.customerOrder = (init as any).customerOrder instanceof CustomerOrder
? (init as any).customerOrder
: CustomerOrder.fromRecord((init as any).customerOrder);
this.markLoaded("customerOrder");
}
}
const key = this.teaqlEntityKey();
if ((this as any)._action === "Create") (this as any)._root.markAsNew(key);
else if ((this as any).version !== undefined) (this as any)._root.setOriginalVersion(key, Number((this as any).version));
LoadedScalarSnapshot.captureFor(this, this.teaqlMutationPayload());
}
private teaqlEntityKey(): EntityKey { return { entity: "Shipment", id: (this as any)._ledgerId }; }
private teaqlAttachRoot(root: EntityRoot): this {
if ((this as any)._root !== root) {
root.mergeEntityFrom((this as any)._root, this.teaqlEntityKey());
(this as any)._root = root;
}
return this;
}
static fromRecord(record: Record<string, unknown>, root?: EntityRoot): Shipment {
const entity = new Shipment(record as Partial<Shipment>);
return root ? entity.teaqlAttachRoot(root) : entity;
}
isLoaded(field: string): boolean {
return (this as any)._fullyLoaded || (this as any)._loadedFields.has(field);
}
markLoaded(...fields: string[]): this {
for (const field of fields) (this as any)._loadedFields.add(field);
return this;
}
markLoadedOnly(...fields: string[]): this {
(this as any)._fullyLoaded = false;
(this as any)._loadedFields = new Set(fields);
return this;
}
static refer(id: string | number): Shipment {
return new Shipment({ id: String(id) } as Partial<Shipment>);
}
/** @internal Generated Runtime Module fixed-ID construction capability. */
static teaqlBootstrapNew(id: string | number): Shipment {
return new Shipment().updateId(String(id));
}
markForDeletion(): this {
(this as any)._action = "Delete";
(this as any)._root.markAsDeleted(this.teaqlEntityKey());
return this;
}
auditAs(comment: string): this {
new MutationIntent(comment);
(this as any)._comment = comment;
return this;
}
async save(context: UserContext): Promise<Shipment> {
const service = context.requireResource<TeaQLDataService>("dataService");
return service.executeGraphSave(new MutationIntent((this as any)._comment), async (graph) => {
this.teaqlPreflightGraph(context, service, graph);
return this.teaqlSaveWithinGraph(context, service, graph);
});
}
/** @internal Validates and fixes the complete graph before its first mutation. */
teaqlPreflightGraph(context: UserContext, service: TeaQLDataService, graph: GraphMutationSession, parent?: MutationTraceScope): void {
if (!parent) new MutationIntent((this as any)._comment);
const action = (this as any)._action;
const pending = action !== "Update" || (this as any)._root.hasPending(this.teaqlEntityKey());
if (action === "Update" && pending) {
const notLoaded = [{ member: "id", canonical: "id" }, { member: "customerOrder", canonical: "customer_order" }, { member: "referenceCode", canonical: "reference_code" }, { member: "version", canonical: "version" }]
.find(field => !this.isLoaded(field.member));
if (notLoaded) {
throw new CheckException([{
ruleId: "invalid_type",
location: ObjectLocation.property(notLoaded.canonical),
message: "Mutation requires a fully loaded entity",
}]);
}
}
const request = graph.request({
entity: "Shipment", action,
payload: action === "Update"
? (this as any)._root.change(this.teaqlEntityKey())
: this.teaqlMutationPayload(),
id: (this as any).id, version: (this as any).version,
comment: (this as any)._comment,
ledgerKey: this.teaqlEntityKey(), ledgerRoot: (this as any)._root,
}, parent, (this as any)._comment).withLoadedSnapshot(LoadedScalarSnapshot.forEntity(this) ?? new LoadedScalarSnapshot());
if (pending) service.preflightMutation(request);
const scope = request.scopeFor(this.teaqlEntityKey());
}
/** @internal Used by generated relation cascades inside the root graph transaction. */
async teaqlSaveWithinGraph(context: UserContext, service: TeaQLDataService, graph: GraphMutationSession, parent?: MutationTraceScope): Promise<Shipment> {
if (!parent) new MutationIntent((this as any)._comment);
const action = (this as any)._action;
const ledgerPayload = (this as any)._root.change(this.teaqlEntityKey());
const mutation = {
entity: "Shipment",
action: (this as any)._action,
payload: action === "Update" ? ledgerPayload : this.teaqlMutationPayload(),
id: (this as any).id,
version: (this as any).version,
comment: (this as any)._comment
,ledgerKey: this.teaqlEntityKey()
,ledgerRoot: (this as any)._root
};
const request = graph.request(mutation, parent, (this as any)._comment)
.withLoadedSnapshot(LoadedScalarSnapshot.forEntity(this) ?? new LoadedScalarSnapshot());
if (action === "Update" && !(this as any)._root.hasPending(this.teaqlEntityKey())) {
const scope = request.scopeFor(this.teaqlEntityKey());
return this;
}
const result = await service.executeMutation(request);
for (const [field, value] of Object.entries(mutation.payload as Record<string, unknown>)) {
if (field !== "id" && field !== "version") (this as any)._root.set(this.teaqlEntityKey(), field, value);
}
if (!result.persistedRecord) {
throw new Error("Mutation did not return the authoritative persisted record");
}
const rollbackState = {
payload: this.teaqlMutationPayload(),
ledgerId: (this as any)._ledgerId,
action: (this as any)._action,
loadedFields: new Set((this as any)._loadedFields),
fullyLoaded: (this as any)._fullyLoaded,
loadedSnapshot: LoadedScalarSnapshot.forEntity(this),
};
const oldKey = this.teaqlEntityKey();
Object.assign(this, result.persistedRecord);
LoadedScalarSnapshot.captureFor(this, this.teaqlMutationPayload());
(this as any)._ledgerId = (this as any).id ?? (this as any)._ledgerId;
const newKey = this.teaqlEntityKey();
(this as any)._root.rekey(oldKey, newKey);
service.afterGraphRollback(() => {
Object.assign(this, rollbackState.payload);
(this as any)._ledgerId = rollbackState.ledgerId;
(this as any)._action = rollbackState.action;
(this as any)._loadedFields = rollbackState.loadedFields;
(this as any)._fullyLoaded = rollbackState.fullyLoaded;
LoadedScalarSnapshot.restoreFor(this, rollbackState.loadedSnapshot);
(this as any)._root.rekey(newKey, oldKey);
});
(this as any)._loadedFields = new Set(Object.keys(result.persistedRecord));
(this as any)._fullyLoaded = false;
if (mutation.action !== "Delete") (this as any)._action = "Update";
const scope = request.scopeFor(newKey);
service.afterGraphCommit(() => {
(this as any)._root.clearEntity(newKey);
if ((this as any).version !== undefined) (this as any)._root.acceptCommittedVersion(newKey, Number((this as any).version));
});
return this;
}
private teaqlMutationPayload(): Record<string, unknown> {
return {
"id": this.id,
"customer_order": this.customerOrder,
"reference_code": this.referenceCode,
"version": this.version
};
}
updateId(value: string): this {
this.id = value;
this.markLoaded("id");
(this as any)._root.set(this.teaqlEntityKey(), "id", value);
return this;
}
updateReferenceCode(value: string): this {
this.referenceCode = value;
this.markLoaded("referenceCode");
(this as any)._root.set(this.teaqlEntityKey(), "reference_code", value);
return this;
}
updateVersion(value: number): this {
this.version = value;
this.markLoaded("version");
(this as any)._root.set(this.teaqlEntityKey(), "version", value);
return this;
}
updateCustomerOrder(value: any): this {
this.customerOrder = value?.id ?? value;
this.markLoaded("customerOrder");
(this as any)._root.set(this.teaqlEntityKey(), "customer_order", this.customerOrder);
return this;
}
}