Source

contracts/crud/serialized-crud-contract.ts

import { FabricCrudContract } from "./crud-contract";
import { Model } from "@decaf-ts/decorator-validation";
import { MangoQuery } from "@decaf-ts/for-couchdb";
import { Context as Ctx, Transaction } from "fabric-contract-api";
import { Constructor } from "@decaf-ts/decoration";

/**
 * @description CRUD contract variant that serializes/deserializes payloads
 * @summary Exposes the same CRUD operations as FabricCrudContract but takes and returns JSON strings to facilitate simple client interactions.
 * @template M - Model type handled by this contract
 * @param {string} name - The contract name
 * @param {Constructor<M>} clazz - The model constructor used to instantiate models from JSON
 * @return {void}
 * @class SerializedCrudContract
 * @example
 * const contract = new SerializedCrudContract<MyModel>('MyModelContract', MyModel);
 * // Client submits JSON string payloads and receives JSON string responses
 */
export class SerializedCrudContract<
  M extends Model,
> extends FabricCrudContract<M> {
  constructor(name: string, clazz: Constructor<M>) {
    super(name, clazz);
  }

  @Transaction()
  override async create(ctx: Ctx, model: string): Promise<string> {
    const { log } = await this.logCtx([ctx], this.create);
    log.info(`Creating model: ${model}`);

    const m = this.deserialize<M>(model);

    log.info(`Model deserialized: ${JSON.stringify(m)}`);
    return this.serialize((await super.create(ctx, m)) as M);
  }

  @Transaction(false)
  override async read(ctx: Ctx, key: string): Promise<string> {
    const { log } = await this.logCtx([ctx], this.read);
    log.info(`Reading id: ${key}`);
    return this.serialize((await super.read(ctx, key)) as M);
  }

  @Transaction()
  override async update(ctx: Ctx, model: string): Promise<string> {
    const { log } = await this.logCtx([ctx], this.update);
    log.info(`Updating model: ${model}`);
    return this.serialize((await super.update(ctx, model)) as M);
  }

  @Transaction()
  override async delete(ctx: Ctx, key: string): Promise<string> {
    const { log } = await this.logCtx([ctx], this.delete);
    log.info(`Deleting id: ${key}`);
    return this.serialize((await super.delete(ctx, key)) as M);
  }

  @Transaction()
  override async deleteAll(ctx: Ctx, keys: string): Promise<string> {
    const parsedKeys: string[] = JSON.parse(keys);
    const { log } = await this.logCtx([ctx], this.deleteAll);

    log.info(`deleting ${parsedKeys.length} entries from the table`);

    return JSON.stringify(
      ((await super.deleteAll(ctx, parsedKeys)) as M[]).map(
        (m) => this.serialize(m) as string
      )
    );
  }

  @Transaction(false)
  override async readAll(ctx: Ctx, keys: string): Promise<string> {
    const parsedKeys: string[] = JSON.parse(keys);

    const { log } = await this.logCtx([ctx], this.readAll);
    log.info(`reading ${parsedKeys.length} entries from the table`);

    return JSON.stringify(
      ((await super.readAll(ctx, parsedKeys)) as M[]).map((m) =>
        this.serialize(m)
      )
    );
  }

  @Transaction()
  override async updateAll(ctx: Ctx, models: string): Promise<string> {
    const { log } = await this.logCtx([ctx], this.updateAll);
    const list: string[] = JSON.parse(models);
    const modelList: M[] = list
      .map((m) => this.deserialize(m))
      .map((m) => new this.clazz(m));

    log.info(`Updating ${modelList.length} entries to the table`);
    return JSON.stringify(
      ((await super.updateAll(ctx, modelList)) as M[]).map(
        (m) => this.serialize(m) as string
      )
    );
  }

  @Transaction(false)
  override async raw(
    context: Ctx,
    rawInput: string,
    docsOnly: boolean
  ): Promise<any> {
    const { ctx } = await this.logCtx([context], this.raw);
    const parsedInput: MangoQuery = JSON.parse(rawInput);
    return super.raw(ctx, parsedInput, docsOnly);
  }

  @Transaction()
  override async init(ctx: Ctx): Promise<void> {
    await super.init(ctx);
  }

  @Transaction(false)
  override async healthcheck(ctx: Ctx): Promise<string> {
    //TODO: TRIM NOT WORKING CHECK LATER
    return JSON.stringify(await super.healthcheck(ctx));
  }

  @Transaction()
  override async createAll(context: Ctx, models: string): Promise<string> {
    const { log } = await this.logCtx([context], this.createAll);
    const list: string[] = JSON.parse(models);
    const modelList: M[] = list
      .map((m) => this.deserialize(m))
      .map((m) => new this.clazz(m));

    log.info(`Adding ${modelList.length} entries to the table`);
    return JSON.stringify(
      ((await super.createAll(context, modelList)) as M[]).map(
        (m) => this.serialize(m) as string
      )
    );
  }
}