Skip to content

Instantly share code, notes, and snippets.

@kasper573
Created October 31, 2018 23:15
Show Gist options
  • Save kasper573/7807580e133dd2a0ce4b3cc207d44151 to your computer and use it in GitHub Desktop.
Save kasper573/7807580e133dd2a0ce4b3cc207d44151 to your computer and use it in GitHub Desktop.
import { CrudAction } from './CrudAction';
import { CrudReaction } from './CrudReaction';
import { CrudDefinition } from './CrudDefinition';
export type CrudDefinitions<Model = any, Query = ModelQuery<Model>> = Partial<{
create: CrudDefinition<Model, Model>,
read: CrudDefinition<Query, Model[]>,
update: CrudDefinition<Model, Model>,
delete: CrudDefinition<Model, boolean>
}>;
export class CrudPerformer<Model = any, Query = ModelQuery<Model>> {
private readonly changeCallbacks: Record<string, CrudReaction<Model>> = {};
get id() {
return this.model.name;
}
constructor(
public model: Class<Model>,
public definitions: CrudDefinitions<Model, Query>
) {}
public can(action: CrudAction): boolean {
return !!this.definitions[action];
}
async create(instance: Model) {
if (!this.can('create')) {
throw new Error(`Create not supported for ${this.id}`);
}
const def = this.definitions.create!;
const instruction = def.instruction(instance);
const response = await def.send(instruction);
if (!response.error) {
await this.emitChange('create', response.data);
}
return response;
}
read(query: Query) {
if (!this.can('read')) {
throw new Error(`Read not supported for ${this.id}`);
}
const def = this.definitions.read!;
const instruction = def.instruction(query);
return def.send(instruction);
}
async readOne(query: Query) {
const { data, ...rest } = await this.read(query);
return {
data: data && data[0],
...rest
};
}
async update(instance: Model) {
if (!this.can('update')) {
throw new Error(`Update not supported for ${this.id}`);
}
const def = this.definitions.update!;
const instruction = def.instruction(instance);
const response = await def.send(instruction);
if (!response.error) {
await this.emitChange('update', response.data, instance);
}
return response;
}
async delete(instance: Model) {
if (!this.can('delete')) {
throw new Error(`Delete not supported for ${this.id}`);
}
const def = this.definitions.delete!;
const instruction = def.instruction(instance);
const response = await def.send(instruction);
if (!response.error && response.data) {
await this.emitChange('delete', instance, instance);
}
return response;
}
private emitChange(
action: CrudAction,
newInstance: Model,
oldInstance?: Model
) {
const promises: Promise<any>[] = [];
for (const id in this.changeCallbacks) {
const result = this.changeCallbacks[id](action, newInstance, oldInstance);
if (result instanceof Promise) {
promises.push(result);
}
}
return Promise.all(promises);
}
/**
* Observe changes by attaching a callback.
* Returns a function that stops the observation when called.
*/
onChange(id: string, callback: CrudReaction<Model>) {
this.changeCallbacks[id] = callback;
return () => delete this.changeCallbacks[id];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment