@knowmax/genericlist-core
Version:
Knowmax Generic list with basic CRUD support without any user interface implementation.
343 lines (342 loc) • 13.4 kB
JavaScript
import { action, computed, observable, makeObservable } from 'mobx';
import { GenericList } from '.';
export class GenericCudList extends GenericList {
constructor(cudConfiguration) {
super(cudConfiguration);
Object.defineProperty(this, "cudConfiguration", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/** Set in case of Create Update Delete error. Prefer using specialized error fields per mode. */
Object.defineProperty(this, "cudError", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/** Might be set in case custom validation method returned error message. */
Object.defineProperty(this, "validationError", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/** Set to editable item when in create or update mode. */
Object.defineProperty(this, "editableItem", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/** Set to original editable item when starting update mode. */
Object.defineProperty(this, "editableItemUnmodified", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/** Set to item when in delete mode. */
Object.defineProperty(this, "deleteItem", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/** Set with data of just deleted item. */
Object.defineProperty(this, "deletedItem", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/** Set with data of newly created item. */
Object.defineProperty(this, "createdItem", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/** Set to item when in restore mode. */
Object.defineProperty(this, "restoreItem", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/** Set with data of just restored item. */
Object.defineProperty(this, "restoredItem", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/** Start create mode for item. */
Object.defineProperty(this, "create", {
enumerable: true,
configurable: true,
writable: true,
value: (template) => {
this.deleteItem = undefined;
this.restoreItem = undefined;
this.editableItem = template ?? this.cudConfiguration.defaultItem();
this.editableItemUnmodified = undefined;
this.cudError = undefined;
this.validationError = undefined;
}
});
/** Cancel any mode we are currently in (create, update, delete, restore). Using lambda syntax here so we can directly hook this method to onClick/onChange like events of user interface. */
Object.defineProperty(this, "cancel", {
enumerable: true,
configurable: true,
writable: true,
value: () => {
this.editableItem = undefined;
this.editableItemUnmodified = undefined;
this.deleteItem = undefined;
this.restoreItem = undefined;
this.cudError = undefined;
this.validationError = undefined;
}
});
makeObservable(this, {
cudError: observable,
setCudError: action,
validationError: observable,
setValidationError: action,
hasCudOrValidationError: computed,
createError: computed,
updateError: computed,
deleteError: computed,
restoreError: computed,
editableItem: observable,
setEditableItem: action,
editableItemUnmodified: observable,
setEditableItemUnmodified: action,
deleteItem: observable,
setDeleteItem: action,
deletedItem: observable,
setDeletedItem: action,
createdItem: observable,
setCreatedItem: action,
restoreItem: observable,
setRestoreItem: action,
restoredItem: observable,
setRestoredItem: action,
create: action,
update: action,
delete: action,
restore: action,
cancel: action,
isCreate: computed,
isUpdate: computed,
isDelete: computed,
isRestore: computed,
isModified: computed,
isValidated: computed,
save: action,
deleteConfirmed: action,
restoreConfirmed: action,
});
this.cudConfiguration = cudConfiguration;
}
setCudError(value) {
this.cudError = value;
}
setValidationError(value) {
this.validationError = value;
}
/** Set in case of CUD error or validation error (latter only when in update mode). */
get hasCudOrValidationError() {
return ((this.isCreate || this.isUpdate) && (this.cudError !== undefined)) ||
(this.isUpdate && this.validationError !== undefined);
}
/** Set after error in create mode. */
get createError() {
return (this.isCreate && this.cudError !== undefined) ? this.cudError : undefined;
}
/** Set after error in update mode. */
get updateError() {
return (this.isUpdate && this.cudError !== undefined) ? this.cudError : undefined;
}
/** Set after error in delete mode. */
get deleteError() {
return (this.isDelete && this.cudError !== undefined) ? this.cudError : undefined;
}
/** Set after error in restore mode. */
get restoreError() {
return (this.isRestore && this.cudError !== undefined) ? this.cudError : undefined;
}
setEditableItem(value) {
this.editableItem = value;
const validationerror = this.cudConfiguration.validateItem && this.cudConfiguration.validateItem(value);
if (typeof validationerror === 'string') {
this.setValidationError(validationerror);
}
else {
this.setValidationError(undefined);
}
}
setEditableItemUnmodified(value) {
this.editableItemUnmodified = value;
}
setDeleteItem(value) {
this.deleteItem = value;
}
setDeletedItem(value) {
this.deletedItem = value;
}
setCreatedItem(value) {
this.createdItem = value;
}
setRestoreItem(value) {
this.restoreItem = value;
}
setRestoredItem(value) {
this.restoredItem = value;
}
/** Start update mode for given item. */
update(entity) {
this.deleteItem = undefined;
this.restoreItem = undefined;
this.editableItem = { ...entity };
this.editableItemUnmodified = { ...entity };
this.cudError = undefined;
this.validationError = undefined;
}
/** Start delete mode for given item. */
delete(entity) {
this.editableItem = undefined;
this.editableItemUnmodified = undefined;
this.deleteItem = entity;
this.restoreItem = undefined;
this.cudError = undefined;
this.validationError = undefined;
}
/** Start restore mode for given item. */
restore(entity) {
this.editableItem = undefined;
this.editableItemUnmodified = undefined;
this.deleteItem = undefined;
this.restoreItem = entity;
this.cudError = undefined;
this.validationError = undefined;
}
/** True if in create mode. */
get isCreate() {
return this.editableItem !== undefined && this.idForItem(this.editableItem) === undefined;
}
/** True if in update mode. */
get isUpdate() {
return this.editableItem !== undefined && this.idForItem(this.editableItem) !== undefined;
}
/** True if in delete mode. */
get isDelete() {
return this.deleteItem !== undefined;
}
/** True if in restore mode. */
get isRestore() {
return this.restoreItem !== undefined;
}
/** True is editable data was changed while in update mode. */
get isModified() {
return this.editableItemUnmodified === undefined || JSON.stringify(this.editableItemUnmodified) !== JSON.stringify(this.editableItem);
}
/** True if editable data contains valid data while in create or update mode. */
get isValidated() {
return this.editableItem && (!this.cudConfiguration.validateItem || this.cudConfiguration.validateItem(this.editableItem) === true);
}
/** Perform actual create or update operation of item set in create or update mode. */
async save() {
this.setCudError(undefined);
this.setCreatedItem(undefined);
if (this.settings.token && this.editableItem) {
try {
// Construct item specifically for server to prevent overposting of data not used by server
const serveritem = this.cudConfiguration.serverItem ?
this.cudConfiguration.serverItem(this.editableItem) :
{ ...this.editableItem };
if (this.isCreate) {
const created = await this.settings.onFetch(this.cudConfiguration.endpointPost ?? this.configuration.endpoint, {
method: 'post',
headers: this.settings.onGetHeaders(this.settings.token, this.settings.language, undefined, this.id),
json: serveritem
}, this.id);
this.cancel();
this.setCreatedItem(created);
return true;
}
else if (this.isUpdate && this.idForItem(this.editableItem) !== undefined) {
await this.settings.onFetch(`${this.cudConfiguration.endpointPut ?? this.configuration.endpoint}/${this.idForItem(this.editableItem)}`, {
method: 'put',
headers: this.settings.onGetHeaders(this.settings.token, this.settings.language, undefined, this.id),
json: serveritem
}, this.id);
this.setEditableItemUnmodified({ ...this.editableItem });
return true;
}
}
catch (e) {
if (e instanceof Error) {
this.setCudError(e);
}
}
}
return false;
}
/** Perform actual delete operation of item previously set in delete mode. */
async deleteConfirmed() {
this.setCudError(undefined);
if (this.isDelete && this.deleteItem && this.settings.token) {
try {
await this.settings.onFetch(`${this.cudConfiguration.endpointDelete ?? this.configuration.endpoint}/${this.idForItem(this.deleteItem)}`, {
method: 'delete',
headers: this.settings.onGetHeaders(this.settings.token, this.settings.language, undefined, this.id),
// TODO: reintroduce retry
//retry: 0
}, this.id);
this.setDeletedItem(this.deleteItem);
this.cancel();
return true;
}
catch (e) {
if (e instanceof Error) {
this.setCudError(e);
}
}
}
return false;
}
/** Perform actual restore operation of item previously set in restore mode. */
async restoreConfirmed() {
this.setCudError(undefined);
if (this.isRestore && this.restoreItem && this.settings.token) {
try {
await this.settings.onFetch(`${this.cudConfiguration.endpointRestore ?? this.configuration.endpoint}/restore/${this.idForItem(this.restoreItem)}`, {
method: 'post',
headers: this.settings.onGetHeaders(this.settings.token, this.settings.language, undefined, this.id),
// TODO: reintroduce retry
//retry: 0
}, this.id);
this.setRestoredItem(this.restoreItem);
this.cancel();
return true;
}
catch (e) {
if (e instanceof Error) {
this.setCudError(e);
}
}
}
return false;
}
/** Return id of given item using settings provided in provided @see IGenericCudListConfiguration. */
idForItem(item) {
return item[this.cudConfiguration.idField ?? 'id'];
}
/** Return name of given item using settings provided in provided @see IGenericCudListConfiguration. */
nameForItem(item) {
return item[this.cudConfiguration.nameField ?? 'name'];
}
}