Model.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import { useModelStore } from "./stores/model";
  2. import { useWebsocketStore } from "./stores/websocket";
  3. export default class Model {
  4. private _permissions?: object;
  5. private _subscriptions?: { updated: string; deleted: string };
  6. private _uses: number;
  7. private _loadedRelations: string[];
  8. constructor(data: object) {
  9. this._uses = 0;
  10. this._loadedRelations = [];
  11. Object.assign(this, data);
  12. }
  13. private async _getRelations(
  14. model?: object,
  15. path?: string
  16. ): Promise<string[]> {
  17. const relationPaths = await Object.entries(model ?? this)
  18. .filter(
  19. ([key, value]) =>
  20. !key.startsWith("_") &&
  21. (typeof value === "object" || Array.isArray(value))
  22. )
  23. .reduce(async (_modelIds, [key, value]) => {
  24. const paths = await _modelIds;
  25. path = path ? `${path}.${key}` : key;
  26. if (typeof value === "object" && value._id) paths.push(path);
  27. else if (Array.isArray(value))
  28. await Promise.all(
  29. value.map(async item => {
  30. if (typeof item !== "object") return;
  31. if (item._id) paths.push(path);
  32. else
  33. paths.push(
  34. ...(await this._getRelations(item, path))
  35. );
  36. })
  37. );
  38. else paths.push(...(await this._getRelations(value, path)));
  39. return paths;
  40. }, Promise.resolve([]));
  41. return relationPaths.filter(
  42. (relationPath, index) =>
  43. relationPaths.indexOf(relationPath) === index
  44. );
  45. }
  46. private async _getRelation(key: string) {
  47. let relation = JSON.parse(JSON.stringify(this));
  48. key.split(".").forEach(property => {
  49. if (Number.isInteger(property))
  50. property = Number.parseInt(property);
  51. relation = relation[property];
  52. });
  53. return relation;
  54. }
  55. private async _loadRelation(
  56. model: object,
  57. path: string,
  58. force: boolean,
  59. pathParts?: string[]
  60. ): Promise<void> {
  61. let [head, ...rest] = path.split(".");
  62. let [next] = rest;
  63. if (Number.isInteger(head)) head = Number.parseInt(head);
  64. if (Number.isInteger(next)) next = Number.parseInt(next);
  65. pathParts ??= [];
  66. pathParts.push(head);
  67. if (Array.isArray(model[head])) {
  68. await Promise.all(
  69. model[head].map(async (item, index) => {
  70. let itemPath = `${index}`;
  71. if (rest.length > 0) itemPath += `.${rest.join(".")}`;
  72. await this._loadRelation(model[head], itemPath, force, [
  73. ...pathParts
  74. ]);
  75. })
  76. );
  77. return;
  78. }
  79. if (rest.length > 0 && model[next] === null) {
  80. await this._loadRelation(
  81. model[head],
  82. rest.join("."),
  83. force,
  84. pathParts
  85. );
  86. return;
  87. }
  88. const fullPath = pathParts.join(".");
  89. if (force || !this._loadedRelations.includes(fullPath)) {
  90. const { findById, registerModels } = useModelStore();
  91. const data = await findById(model[head]._name, model[head]._id);
  92. const [registeredModel] = await registerModels(data);
  93. model[head] = registeredModel;
  94. this._loadedRelations.push(fullPath);
  95. }
  96. if (rest.length === 0) return;
  97. await model[head].loadRelations(rest.join("."));
  98. }
  99. public async loadRelations(
  100. relations?: string | string[],
  101. force = false
  102. ): Promise<void> {
  103. if (relations)
  104. relations = Array.isArray(relations) ? relations : [relations];
  105. await Promise.all(
  106. (relations ?? []).map(async path => {
  107. await this._loadRelation(this, path, force);
  108. })
  109. );
  110. }
  111. public async unregisterRelations(): Promise<void> {
  112. const { unregisterModels } = useModelStore();
  113. const relationIds = await Promise.all(
  114. this._loadedRelations.map(async path => {
  115. const relation = await this._getRelation(path);
  116. return relation._id;
  117. })
  118. );
  119. await unregisterModels(relationIds);
  120. }
  121. public async updateData(data: object) {
  122. await this.unregisterRelations();
  123. Object.assign(this, data);
  124. await this.loadRelations(this._loadedRelations, true);
  125. await this.refreshPermissions();
  126. }
  127. public getName(): string {
  128. return this._name;
  129. }
  130. public async getPermissions(refresh = false): Promise<object> {
  131. if (refresh === false && this._permissions) return this._permissions;
  132. const { runJob } = useWebsocketStore();
  133. this._permissions = await runJob("data.users.getModelPermissions", {
  134. modelName: this._name,
  135. modelId: this._id
  136. });
  137. return this._permissions;
  138. }
  139. public async refreshPermissions(): Promise<void> {
  140. if (this._permissions) this.getPermissions(true);
  141. }
  142. public async hasPermission(permission: string): Promise<boolean> {
  143. const permissions = await this.getPermissions();
  144. return !!permissions[permission];
  145. }
  146. public getSubscriptions() {
  147. return this._subscriptions;
  148. }
  149. public setSubscriptions(updated: string, deleted: string): void {
  150. this._subscriptions = { updated, deleted };
  151. }
  152. public getUses(): number {
  153. return this._uses;
  154. }
  155. public addUse(): void {
  156. this._uses += 1;
  157. }
  158. public removeUse(): void {
  159. this._uses -= 1;
  160. }
  161. public toJSON(): object {
  162. return Object.fromEntries(
  163. Object.entries(this).filter(
  164. ([key, value]) =>
  165. (!key.startsWith("_") || key === "_id") &&
  166. typeof value !== "function"
  167. )
  168. );
  169. }
  170. public async update(query: object) {
  171. const { runJob } = useWebsocketStore();
  172. return runJob(`data.${this.getName()}.updateById`, {
  173. _id: this._id,
  174. query
  175. });
  176. }
  177. public async delete() {
  178. const { runJob } = useWebsocketStore();
  179. return runJob(`data.${this.getName()}.deleteById`, { _id: this._id });
  180. }
  181. }