@simitgroup/simpleapp-generator 1.0.32 → 1.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/README.md +185 -16
  3. package/buildinschemas copy/autoincreament.autoinc.jsonschema.json +39 -0
  4. package/buildinschemas copy/branch.branch.jsonschema.json +41 -0
  5. package/buildinschemas copy/docnoformat.docno.jsonschema.json +41 -0
  6. package/buildinschemas copy/organization.org.jsonschema.json +50 -0
  7. package/buildinschemas copy/permission.perm.jsonschema.json +23 -0
  8. package/buildinschemas copy/permission.perm.jsonschema.try.json +25 -0
  9. package/buildinschemas copy/tenant.tenant.jsonschema.json +21 -0
  10. package/buildinschemas copy/tenant.tenant.jsonschema.try.json +27 -0
  11. package/buildinschemas copy/user.user.jsonschema.json +31 -0
  12. package/dist/buildinschemas/autoincreament.d.ts +3 -0
  13. package/dist/buildinschemas/autoincreament.d.ts.map +1 -0
  14. package/dist/buildinschemas/autoincreament.js +36 -0
  15. package/dist/buildinschemas/autoincreament.js.map +1 -0
  16. package/dist/buildinschemas/branch.d.ts +3 -0
  17. package/dist/buildinschemas/branch.d.ts.map +1 -0
  18. package/dist/buildinschemas/branch.js +41 -0
  19. package/dist/buildinschemas/branch.js.map +1 -0
  20. package/dist/buildinschemas/docnoformat.d.ts +3 -0
  21. package/dist/buildinschemas/docnoformat.d.ts.map +1 -0
  22. package/dist/buildinschemas/docnoformat.js +60 -0
  23. package/dist/buildinschemas/docnoformat.js.map +1 -0
  24. package/dist/buildinschemas/index.d.ts +8 -0
  25. package/dist/buildinschemas/index.d.ts.map +1 -0
  26. package/dist/buildinschemas/index.js +18 -0
  27. package/dist/buildinschemas/index.js.map +1 -0
  28. package/dist/buildinschemas/organization.d.ts +3 -0
  29. package/dist/buildinschemas/organization.d.ts.map +1 -0
  30. package/dist/buildinschemas/organization.js +34 -0
  31. package/dist/buildinschemas/organization.js.map +1 -0
  32. package/dist/buildinschemas/permission.d.ts +3 -0
  33. package/dist/buildinschemas/permission.d.ts.map +1 -0
  34. package/dist/buildinschemas/permission.js +34 -0
  35. package/dist/buildinschemas/permission.js.map +1 -0
  36. package/dist/buildinschemas/tenant.d.ts +3 -0
  37. package/dist/buildinschemas/tenant.d.ts.map +1 -0
  38. package/dist/buildinschemas/tenant.js +41 -0
  39. package/dist/buildinschemas/tenant.js.map +1 -0
  40. package/dist/buildinschemas/user.d.ts +3 -0
  41. package/dist/buildinschemas/user.d.ts.map +1 -0
  42. package/dist/buildinschemas/user.js +31 -0
  43. package/dist/buildinschemas/user.js.map +1 -0
  44. package/dist/constant.d.ts +4 -0
  45. package/dist/constant.d.ts.map +1 -0
  46. package/dist/constant.js +2 -1
  47. package/dist/constant.js.map +1 -1
  48. package/dist/framework.d.ts +10 -0
  49. package/dist/framework.d.ts.map +1 -0
  50. package/dist/framework.js +120 -68
  51. package/dist/framework.js.map +1 -1
  52. package/dist/generate-allow-changebackend.js +305 -0
  53. package/dist/generate-allow-changebackend.js.map +1 -0
  54. package/dist/generate.d.ts +2 -0
  55. package/dist/generate.d.ts.map +1 -0
  56. package/dist/generate.js +310 -230
  57. package/dist/generate.js.map +1 -1
  58. package/dist/index.d.ts +3 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +84 -20
  61. package/dist/index.js.map +1 -1
  62. package/dist/libs.d.ts +2 -0
  63. package/dist/libs.d.ts.map +1 -0
  64. package/dist/processors/groupsbuilder.js +2 -0
  65. package/dist/processors/groupsbuilder.js.map +1 -0
  66. package/dist/processors/jsonschemabuilder.d.ts +4 -0
  67. package/dist/processors/jsonschemabuilder.d.ts.map +1 -0
  68. package/dist/processors/jsonschemabuilder.js +146 -172
  69. package/dist/processors/jsonschemabuilder.js.map +1 -1
  70. package/dist/schematype/baseschema.js +25 -0
  71. package/dist/schematype/baseschema.js.map +1 -0
  72. package/dist/schematype/default.js +2 -0
  73. package/dist/schematype/default.js.map +1 -0
  74. package/dist/schematype/index.js +12 -0
  75. package/dist/schematype/index.js.map +1 -0
  76. package/dist/schematype/primarymasterdata.js +38 -0
  77. package/dist/schematype/primarymasterdata.js.map +1 -0
  78. package/dist/schematype/simple.js +24 -0
  79. package/dist/schematype/simple.js.map +1 -0
  80. package/dist/schematype/simplemasterdata.js +31 -0
  81. package/dist/schematype/simplemasterdata.js.map +1 -0
  82. package/dist/schematype/transaction.js +74 -0
  83. package/dist/schematype/transaction.js.map +1 -0
  84. package/dist/storage.d.ts +3 -0
  85. package/dist/storage.d.ts.map +1 -0
  86. package/dist/storage.js +2 -2
  87. package/dist/storage.js.map +1 -1
  88. package/dist/type.d.ts +164 -0
  89. package/dist/type.d.ts.map +1 -0
  90. package/dist/type.js +16 -1
  91. package/dist/type.js.map +1 -1
  92. package/dist/validation.d.ts +1 -0
  93. package/dist/validation.d.ts.map +1 -0
  94. package/documentation/designconcept.bpmn +349 -0
  95. package/documentation/documentation.png +0 -0
  96. package/documentation/infra.drawio +141 -0
  97. package/documentation/infra.png +0 -0
  98. package/documentation/management.drawio +57 -0
  99. package/documentation/stack.drawio +106 -0
  100. package/package.json +3 -1
  101. package/src/buildinschemas/autoincreament.ts +34 -0
  102. package/src/buildinschemas/branch.ts +39 -0
  103. package/src/buildinschemas/docnoformat.ts +58 -0
  104. package/src/buildinschemas/index.ts +7 -0
  105. package/src/buildinschemas/organization.ts +31 -0
  106. package/src/buildinschemas/permission.ts +31 -0
  107. package/src/buildinschemas/tenant.ts +38 -0
  108. package/src/buildinschemas/user.ts +28 -0
  109. package/src/constant.ts +2 -1
  110. package/src/framework.ts +126 -67
  111. package/src/generate.ts +327 -266
  112. package/src/generate.ts.backup +339 -0
  113. package/src/index.ts +93 -18
  114. package/src/processors/jsonschemabuilder.ts +199 -226
  115. package/src/processors/jsonschemabuilder.ts-old +383 -0
  116. package/src/storage.ts +1 -1
  117. package/src/type.ts +94 -27
  118. package/templates/basic/nest/controller.ts.eta +255 -0
  119. package/templates/basic/nest/default.ts.eta +42 -0
  120. package/templates/basic/{model.eta → nest/model.ts.eta} +18 -5
  121. package/templates/basic/nest/processor.ts.eta +129 -0
  122. package/templates/basic/nest/service.ts.eta +64 -0
  123. package/templates/basic/{type.eta → nest/type.ts.eta} +3 -3
  124. package/templates/basic/nuxt/default.ts.eta +42 -0
  125. package/templates/basic/{pageindex.vue.eta → nuxt/pages.crud.vue.eta} +85 -22
  126. package/templates/basic/nuxt/simpleapp.doc.ts.eta +11 -0
  127. package/templates/basic/nuxt/simpleapp.generate.client.ts.eta +212 -0
  128. package/templates/nest/.env.eta +31 -0
  129. package/templates/nest/.gitignore.eta +40 -0
  130. package/templates/nest/src/app.controller.ts.eta +19 -0
  131. package/templates/nest/src/app.module.ts.eta +77 -0
  132. package/templates/nest/src/app.service.ts.eta +9 -0
  133. package/templates/nest/src/main.ts.eta +58 -0
  134. package/templates/nest/src/simpleapp/generate/apischemas/index.ts.eta +16 -0
  135. package/templates/nest/src/simpleapp/generate/commons/decorators/appuser.decorator.ts.eta +8 -0
  136. package/templates/nest/src/simpleapp/generate/commons/dicts/foreignkeys.ts.eta +1 -0
  137. package/templates/nest/src/simpleapp/generate/commons/docnogenerator.service.ts.eta +101 -0
  138. package/templates/nest/src/simpleapp/generate/commons/exceptions/SimpleAppExceptionFilter.ts.eta +39 -0
  139. package/templates/nest/src/simpleapp/generate/commons/interceptors/response.interceptor.ts.eta +38 -0
  140. package/templates/nest/src/simpleapp/generate/commons/middlewares/tenant.middleware.ts.eta +88 -0
  141. package/templates/nest/{Workflow.eta → src/simpleapp/generate/commons/providers/workflow.provider.ts.etax} +20 -14
  142. package/templates/nest/src/simpleapp/generate/commons/roles/roles.decorator.ts.eta +6 -0
  143. package/templates/nest/src/simpleapp/generate/commons/roles/roles.enum.ts.eta +28 -0
  144. package/templates/nest/src/simpleapp/generate/commons/roles/roles.group.ts.eta +10 -0
  145. package/templates/nest/src/simpleapp/generate/commons/roles/roles.guard.ts.eta +34 -0
  146. package/templates/nest/src/simpleapp/generate/commons/user.context.ts.eta +391 -0
  147. package/templates/nest/src/simpleapp/generate/controllers/simpleapp.controller.ts.eta +71 -0
  148. package/templates/nest/src/simpleapp/generate/models/perm.model.ts.eta +53 -0
  149. package/templates/nest/src/simpleapp/generate/models/tenant.model.ts.eta +45 -0
  150. package/templates/nest/src/simpleapp/generate/models/user.model.ts.eta +57 -0
  151. package/templates/nest/src/simpleapp/generate/processors/simpleapp.processor.ts.eta +624 -0
  152. package/templates/nest/src/simpleapp/generate/types/index.ts.eta +19 -0
  153. package/templates/nest/src/simpleapp/profile/profile.apischema.ts.eta +74 -0
  154. package/templates/nest/src/simpleapp/profile/profile.controller.ts.eta +110 -0
  155. package/templates/nest/src/simpleapp/profile/profile.service.ts.eta +195 -0
  156. package/templates/nest/src/simpleapp/profile/profile.types.ts.eta +18 -0
  157. package/templates/nest/src/simpleapp/services/autoinc.service.ts.eta +89 -0
  158. package/templates/nest/src/simpleapp/services/branch.service.ts.eta +66 -0
  159. package/templates/nest/src/simpleapp/services/docno.service.ts.eta +93 -0
  160. package/templates/nest/src/simpleapp/services/org.service.ts.eta +67 -0
  161. package/templates/nest/src/simpleapp/services/perm.service.ts.eta +102 -0
  162. package/templates/nest/src/simpleapp/services/tenant.service.ts.eta +69 -0
  163. package/templates/nest/src/simpleapp/services/user.service.ts.eta +66 -0
  164. package/templates/nest/src/simpleapp/simpleapp.module.ts.eta +43 -0
  165. package/templates/nuxt/.env.eta +21 -0
  166. package/templates/nuxt/.gitignore.eta +28 -0
  167. package/templates/nuxt/app.vue.eta +5 -2
  168. package/templates/nuxt/assets/css/tailwind.css.eta +35 -0
  169. package/templates/nuxt/components/CrudNestedDoc.vue.eta +164 -0
  170. package/templates/nuxt/components/CrudSimple.vue.eta +179 -0
  171. package/templates/nuxt/{components.debugdocdata.vue.eta → components/DebugDocumentData.vue.eta} +4 -1
  172. package/templates/nuxt/{components.eventmonitor.vue.eta → components/EventMonitor.vue.eta} +27 -27
  173. package/templates/nuxt/components/Invitation.vue.eta +50 -0
  174. package/templates/nuxt/components/Menus.vue.eta +58 -0
  175. package/templates/nuxt/components/PermissionInfo.vue.eta +92 -0
  176. package/templates/nuxt/components/SimpleAppAutocomplete.vue.eta +131 -0
  177. package/templates/nuxt/components/SimpleAppAutocompletemulti.vue.eta +73 -0
  178. package/templates/nuxt/components/SimpleAppCalendar.vue.eta +55 -0
  179. package/templates/nuxt/components/SimpleAppCheckbox.vue.eta +29 -0
  180. package/templates/nuxt/components/SimpleAppChip.vue.eta +28 -0
  181. package/templates/nuxt/components/SimpleAppColor.vue.eta +41 -0
  182. package/templates/nuxt/components/SimpleAppDatatable.vue.eta +20 -0
  183. package/templates/nuxt/components/SimpleAppDocumentNo.vue.eta +90 -0
  184. package/templates/nuxt/components/SimpleAppDynamicInput.vue.eta +29 -0
  185. package/templates/nuxt/components/SimpleAppEditor.vue.eta +31 -0
  186. package/templates/nuxt/components/SimpleAppForm.vue.eta +131 -0
  187. package/templates/nuxt/components/SimpleAppInputTable.vue.eta +104 -0
  188. package/templates/nuxt/components/SimpleAppList.vue.eta +38 -0
  189. package/templates/nuxt/components/SimpleAppListmulti.vue.eta +41 -0
  190. package/templates/nuxt/components/SimpleAppNumber.vue.eta +32 -0
  191. package/templates/nuxt/components/SimpleAppPassword.vue.eta +41 -0
  192. package/templates/nuxt/components/SimpleAppRadio.vue.eta +42 -0
  193. package/templates/nuxt/components/SimpleAppRating.vue.eta +41 -0
  194. package/templates/nuxt/components/SimpleAppSelect.vue.eta +38 -0
  195. package/templates/nuxt/components/SimpleAppSelectmulti.vue.eta +39 -0
  196. package/templates/nuxt/components/SimpleAppSlider.vue.eta +42 -0
  197. package/templates/nuxt/components/SimpleAppSwitch.vue.eta +30 -0
  198. package/templates/nuxt/components/SimpleAppText.vue.eta +50 -0
  199. package/templates/nuxt/components/SimpleAppTextarea.vue.eta +30 -0
  200. package/templates/nuxt/components/SimpleAppValue.vue.eta +86 -0
  201. package/templates/nuxt/components/SimpleFieldContainer.vue.eta +102 -0
  202. package/templates/nuxt/components/XorgPicker.vue.eta +66 -0
  203. package/templates/nuxt/components/helper.ts.eta +90 -0
  204. package/templates/nuxt/components/type.ts.eta +32 -0
  205. package/templates/nuxt/composables/docformat.generate.ts.eta +5 -0
  206. package/templates/nuxt/{composables.getautocomplete.ts.eta → composables/getAutocomplete.generate.ts.eta} +4 -5
  207. package/templates/nuxt/{composables.getmenus.ts.eta → composables/getMenus.generate.ts.eta} +22 -7
  208. package/templates/nuxt/composables/getOpenApi.generate.ts.eta +6 -0
  209. package/templates/nuxt/composables/getTenant.generate.ts.eta +4 -0
  210. package/templates/nuxt/composables/getUserStore.generate.ts.eta +22 -0
  211. package/templates/nuxt/composables/logout.generate.ts.eta +22 -0
  212. package/templates/nuxt/composables/roles.generate.ts.eta +48 -0
  213. package/templates/nuxt/composables/stringHelper.generate.ts.eta +5 -0
  214. package/templates/nuxt/{layouts.default.vue.eta → layouts/default.vue.eta} +2 -0
  215. package/templates/nuxt/middleware/10.acl.global.ts.eta +38 -0
  216. package/templates/nuxt/nuxt.config.ts.eta +12 -2
  217. package/templates/nuxt/pages/[xorg]/branch/index.vue.eta +102 -0
  218. package/templates/nuxt/pages/[xorg]/docnoformat/[id].vue.eta +17 -0
  219. package/templates/nuxt/pages/[xorg]/docnoformat/index.vue.eta +269 -0
  220. package/templates/nuxt/pages/[xorg]/index.vue.eta +36 -0
  221. package/templates/nuxt/pages/[xorg]/organization/index.vue.eta +148 -0
  222. package/templates/nuxt/pages/[xorg]/permission/index.vue.eta +280 -0
  223. package/templates/nuxt/pages/[xorg]/tenant/index.vue.eta +93 -0
  224. package/templates/nuxt/pages/[xorg]/user/index.vue.eta +468 -0
  225. package/templates/nuxt/pages/index.vue.eta +191 -0
  226. package/templates/nuxt/pages/login.vue.eta +21 -0
  227. package/templates/nuxt/plugins/10.simpleapp-event.ts.eta +53 -0
  228. package/templates/nuxt/plugins/20.simpleapp-userstore.ts.eta +155 -0
  229. package/templates/nuxt/plugins/50.simpleapp-client.ts.eta +23 -0
  230. package/templates/nuxt/{server.api.ts.eta → server/api/[xorg]/[...].ts.eta} +5 -2
  231. package/templates/nuxt/server/api/auth/[...].ts.eta +68 -0
  232. package/templates/nuxt/{server.api.auth.logout.ts.eta → server/api/auth/logout.ts.eta} +1 -3
  233. package/templates/nuxt/server/api/profile/[...].ts.eta +150 -0
  234. package/templates/nuxt/server/api/profile/index.ts.eta +103 -0
  235. package/templates/nuxt/simpleapp/generate/clients/SimpleAppClient.ts.eta +187 -0
  236. package/templates/nuxt/simpleapp/generate/commons/documents.ts.eta +6 -0
  237. package/templates/nuxt/simpleapp/generate/commons/events.ts.eta +5 -0
  238. package/templates/nuxt/simpleapp/generate/commons/groups.ts.eta +11 -0
  239. package/templates/nuxt/simpleapp/generate/commons/roles.ts.eta +21 -0
  240. package/templates/nuxt/types/index.ts.eta +65 -0
  241. package/templates/project/README.md +11 -0
  242. package/templates/project/build.sh.eta +17 -0
  243. package/templates/project/config.json.eta +2 -0
  244. package/templates/project/generate.ts.eta +10 -0
  245. package/templates/project/schemas/category.ts.eta +26 -0
  246. package/templates/project/schemas/index.ts.eta +5 -0
  247. package/templates/project/schemas/product.ts.eta +59 -0
  248. package/templates/project/shares/hello.ts.eta +1 -0
  249. package/templates/project/shares/index.ts.eta +2 -0
  250. package/tsconfig.json +9 -2
  251. package/tsconfig.tsbuildinfo +1 -0
  252. package/definations/category.cat.jsonschema.json +0 -55
  253. package/definations/level.lvl.jsonschema.json +0 -48
  254. package/definations/product.prd.jsonschema.json +0 -46
  255. package/templates/basic/controller.eta +0 -152
  256. package/templates/basic/module.eta +0 -22
  257. package/templates/basic/service.eta +0 -62
  258. package/templates/basic/simpleappclient.eta +0 -120
  259. package/templates/nest/SimpleAppController.eta +0 -69
  260. package/templates/nest/SimpleAppService.eta +0 -366
  261. package/templates/nest/TenantMiddleware.eta +0 -34
  262. package/templates/nest/UserProvider.eta +0 -127
  263. package/templates/nest/app.controller.eta +0 -12
  264. package/templates/nest/app.module.eta +0 -64
  265. package/templates/nest/app.service.eta +0 -8
  266. package/templates/nest/inputvalidation-exception.eta +0 -6
  267. package/templates/nest/nest.env.eta +0 -28
  268. package/templates/nest/nest.main.eta +0 -31
  269. package/templates/nuxt/components.crudsimple.vue.eta +0 -124
  270. package/templates/nuxt/components.menus.vue.eta +0 -35
  271. package/templates/nuxt/env.eta +0 -17
  272. package/templates/nuxt/pages.[xorg].index.vue.eta +0 -20
  273. package/templates/nuxt/pages.index.vue.eta +0 -72
  274. package/templates/nuxt/pages.login.vue.eta +0 -20
  275. package/templates/nuxt/plugins.simpleapp.ts.eta +0 -88
  276. package/templates/nuxt/server.api.auth[...].ts.eta +0 -233
  277. package/templates/nuxt/tailwind.css.eta +0 -49
  278. /package/templates/basic/{apischema.eta → nest/apischema.ts.eta} +0 -0
  279. /package/templates/basic/{jsonschema.eta → nest/jsonschema.ts.eta} +0 -0
  280. /package/templates/basic/{pageindexwithid.vue.eta → nuxt/pages.[id].vue.eta} +0 -0
  281. /package/templates/nest/{oauth2-redirect.eta → public_html/oauth2-redirect.html.eta} +0 -0
@@ -0,0 +1,624 @@
1
+ import { Injectable, Logger,Inject } from '@nestjs/common';
2
+ import { InjectModel } from '@nestjs/mongoose';
3
+ import * as jsonpath from 'jsonpath'
4
+ import { uniq } from 'lodash';
5
+
6
+ import {foreignkeys} from '../commons/dicts/foreignkeys'
7
+ import { Model,Types,PipelineStage,mongo, FilterQuery, ProjectionType} from 'mongoose';
8
+ import Ajv from 'ajv';
9
+ import addFormats from 'ajv-formats';
10
+ import addErrors from 'ajv-errors';
11
+ import {
12
+ NotFoundException,
13
+ BadRequestException,
14
+ ForbiddenException,
15
+ InternalServerErrorException,
16
+ HttpExceptionOptions,
17
+ } from '@nestjs/common/exceptions';
18
+ import { UserContext } from '../commons/user.context';
19
+ import { DocNumberFormatGenerator } from '../commons/docnogenerator.service';
20
+
21
+ export enum IsolationType {
22
+ 'none' = 'none',
23
+ 'org' = 'org',
24
+ 'tenant' = 'tenant',
25
+ 'branch' = 'branch',
26
+ }
27
+ export enum HookType {
28
+ 'init' = 'init',
29
+ 'beforeSearch' = 'beforeSearch',
30
+ 'afterSearch' = 'afterSearch',
31
+ 'beforeValidation' = 'beforeValidation',
32
+ 'afterValidation' = 'afterValidation',
33
+ 'beforeCreate' = 'beforeCreate',
34
+ 'afterCreate' = 'afterCreate',
35
+ 'beforeUpdate' = 'beforeUpdate',
36
+ 'afterUpdate' = 'afterUpdate',
37
+ 'beforeDelete' = 'beforeDelete',
38
+ 'afterDelete' = 'afterDelete',
39
+ 'beforeFetchRecord' = 'beforeFetchRecord',
40
+ 'afterFetchRecord' = 'afterFetchRecord',
41
+ }
42
+ export type MoreProjectionType = {
43
+ [key: string]: string;
44
+ };
45
+ @Injectable()
46
+ export class SimpleAppService<T extends { _id?: string }> {
47
+ protected logger = new Logger();
48
+ protected strictIsolation = true
49
+ protected jsonschema = { type: 'object', properties: {}, required: [] };
50
+ protected documentIdentityCode = 'code';
51
+ protected documentIdentityLabel = 'label';
52
+ protected documentName = '-unknowndocname-';
53
+ protected documentType = '-unknowndoctype-';
54
+ protected LIMITPERPAGE = 20;
55
+ protected moreAutoCompleteField: MoreProjectionType = {};
56
+ protected isolationtype: IsolationType = IsolationType.org;
57
+ protected isolationFilter: any = {};
58
+ protected data: T = { _id: '' } as T;
59
+ protected doc: Model<T>; //set private to prevent developer break data isolation control
60
+ protected errorlist = [];
61
+ protected withDocNumberFormat=false
62
+ protected foreignkeys = {}
63
+ // protected userprovider = new UserContext() ;
64
+
65
+ constructor(
66
+ doctype: string,
67
+ docname: string,
68
+ newdoc: Model<T>,
69
+ isolationtype: IsolationType = IsolationType.org,
70
+ private docnogenerator:DocNumberFormatGenerator
71
+ ) {
72
+ // console.log("-------init simpleapp service abstract class -------userprovider=",typeof this.userprovider)
73
+ this.documentType = doctype.toUpperCase();
74
+ this.documentName = docname;
75
+ this.doc = newdoc;
76
+ this.isolationtype = isolationtype;
77
+ this.hook(undefined,HookType.init, undefined);
78
+ // this.tenantdoc = tenantdoc
79
+ }
80
+ getDocumentType = () => this.documentType;
81
+ getDocumentName = () => this.documentName;
82
+ getRecordId = (): string => this.data._id;
83
+ setSchema = (newschema) => (this.jsonschema = newschema);
84
+ getSchema = () => this.doc.schema.obj;
85
+ getData = () => {
86
+ //console.log('thisdata', this.data);
87
+ return this.data;
88
+ };
89
+ setData = (newdata: T) => {
90
+ delete newdata._id;
91
+ this.data = { ...newdata };
92
+ return this;
93
+ };
94
+ reCalculate() {}
95
+ getIsolationFilter = (appuser:UserContext,) => {
96
+ let isolationFilter = {};
97
+ switch (this.isolationtype) {
98
+ case 'none':
99
+ isolationFilter = {};
100
+ break;
101
+ case 'branch':
102
+ isolationFilter = appuser.getBranchFilter();
103
+ break;
104
+ case 'tenant':
105
+ isolationFilter = appuser.getTenantFilter();
106
+ break;
107
+ case 'org':
108
+ default:
109
+ isolationFilter = appuser.getOrgFilter();
110
+ break;
111
+ }
112
+ return isolationFilter;
113
+ };
114
+ async list(appuser:UserContext,) {
115
+ try {
116
+ //console.log("this.isolationFilter",this.getIsolationFilter())
117
+ const products = await this.doc.find(this.getIsolationFilter(appuser));
118
+ // console.log(products)
119
+ const productlist = products.map((p: T) => {
120
+ return p;
121
+ });
122
+ // console.log(products);
123
+ return productlist;
124
+ } catch (err) {
125
+ throw new InternalServerErrorException(err.message);
126
+ }
127
+ }
128
+ addAutoCompleteField = (morefield: MoreProjectionType) => {
129
+ const props = Object.getOwnPropertyNames(morefield);
130
+ // console.log('addAutoCompleteField', props);
131
+ for (let i = 0; i < props.length; i++) {
132
+ const key = props[i];
133
+ this.moreAutoCompleteField[key] = '$' + morefield[key];
134
+ }
135
+ // console.log(this.moreAutoCompleteField);
136
+ };
137
+ async getAutoComplete(appuser:UserContext, keyword: string) {
138
+ try {
139
+ const filter1 = {};
140
+ const filter2 = {};
141
+ let filters: any[] = [];
142
+ if (
143
+ this.jsonschema.properties[this.documentIdentityCode]['type'] ==
144
+ 'string'
145
+ ) {
146
+ filter1[this.documentIdentityCode] = { $regex: keyword, $options: 'i' };
147
+ filters.push(filter1);
148
+ }
149
+
150
+ filter2[this.documentIdentityLabel] = { $regex: keyword, $options: 'i' };
151
+ filters.push(filter2);
152
+ const filterobj = { $or: filters };
153
+
154
+ Object.assign(filterobj, this.getIsolationFilter(appuser));
155
+ let projections = {
156
+ id: `\$_id`,
157
+ label: `\$${this.documentIdentityLabel}`,
158
+ code: `\$${this.documentIdentityCode}`,
159
+ };
160
+ if (this.moreAutoCompleteField) {
161
+ Object.assign(projections, this.moreAutoCompleteField);
162
+ }
163
+ const products = await this.doc.find(filterobj, projections, {
164
+ limit: this.LIMITPERPAGE,
165
+ });
166
+ const productlist = products.map((p: T) => {
167
+ return p;
168
+ });
169
+ return productlist;
170
+ } catch (err) {
171
+ throw new InternalServerErrorException(err.message);
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Special search function which can by pass data isolation. reserved and try not use
177
+ * @param appuser
178
+ * @param filters
179
+ * @returns
180
+ */
181
+ private async searchNoIsolation(appuser:UserContext,filters: Object) {
182
+ try {
183
+ await this.hook(appuser,HookType.beforeSearch, filters);
184
+ const products = await this.doc.find(filters);
185
+ const productlist = products.map((p: T) => {
186
+ return p;
187
+ });
188
+ await this.hook(appuser,HookType.afterSearch, productlist);
189
+ // console.log(products);
190
+ return productlist;
191
+ } catch (err) {
192
+ throw new InternalServerErrorException(err.message);
193
+ }
194
+ // return this;
195
+ }
196
+ async aggregate(appuser:UserContext,pipeline:PipelineStage[]){
197
+
198
+ if(pipeline[0] && pipeline[0]['$match']){
199
+ try{
200
+ const isolationFilter= {... this.getIsolationFilter(appuser)}
201
+ this.polishIsolationFilter(isolationFilter)
202
+
203
+
204
+ Object.assign(pipeline[0]['$match'],isolationFilter);
205
+ console.log("final agg",pipeline)
206
+ return await this.doc.aggregate(pipeline)
207
+ }catch(err){
208
+ throw new InternalServerErrorException(err);
209
+ }
210
+
211
+ }else{
212
+ throw new InternalServerErrorException('first aggregate pipelinestage shall use $match');
213
+ }
214
+
215
+ }
216
+ async search(appuser:UserContext,filters: FilterQuery<T>,projection:ProjectionType<T>=undefined,sort:any=undefined) {
217
+ try {
218
+ const isolationFilter= {... this.getIsolationFilter(appuser)}
219
+ this.polishIsolationFilter(isolationFilter)
220
+
221
+ // console.log("initial search",filters)
222
+ const newfilters ={...filters,...isolationFilter}
223
+ await this.hook(appuser,HookType.beforeSearch, newfilters);
224
+ // console.log("before _find",newfilters)
225
+ // console.log("this.doc",this.doc)
226
+ const products = await this.doc.find(newfilters,projection).sort(sort);
227
+ // console.log("after search",products)
228
+ const productlist = products.map((p: T) => {
229
+ return p;
230
+ });
231
+ // console.log("after map",productlist)
232
+ await this.hook(appuser,HookType.afterSearch, productlist);
233
+ // console.log(products);
234
+ return productlist;
235
+ } catch (err) {
236
+ throw new BadRequestException(err.message);
237
+ }
238
+ // return this;
239
+ }
240
+ async findById(appuser:UserContext,id: string) {
241
+ await this.hook(appuser,HookType.beforeFetchRecord, id);
242
+ const data = await this.search(appuser,{ _id: id });
243
+ await this.hook(appuser,HookType.afterFetchRecord, data);
244
+ if (data.length == 1) {
245
+ // console.log('data0', data[0]);
246
+ return data[0];
247
+ } else {
248
+ return null;
249
+ }
250
+ }
251
+
252
+
253
+ async create(appuser:UserContext, data:T) {
254
+ let result;
255
+ if(!data._id){
256
+ data._id = crypto.randomUUID()
257
+ }
258
+ if(this.withDocNumberFormat && !data[this.documentIdentityCode]){
259
+ await this.genNewDocNo(appuser,data)
260
+ }
261
+ console.log("DATA for create",data)
262
+ await this.hook(appuser,HookType.beforeCreate, data);
263
+
264
+ let isolationFilter:any = {...appuser.getCreateFilter()}
265
+ isolationFilter = this.polishIsolationFilter(isolationFilter,data)
266
+
267
+ this.logger.debug("isolationFilter",'SimpleAppService')
268
+ this.logger.debug(isolationFilter,'SimpleAppService')
269
+ this.logger.debug("Create data before isolation",'SimpleAppService')
270
+ this.logger.debug(data,'SimpleAppService')
271
+ Object.assign(data, isolationFilter);
272
+
273
+ await this.validateData(appuser,data);
274
+ this.logger.debug("Create record",'SimpleAppService')
275
+ this.logger.debug(data,'SimpleAppService')
276
+
277
+ this.applyNestedDateTime(appuser,data,'create')
278
+ const newdoc = new this.doc(data);
279
+
280
+ result = await newdoc.save({session:appuser.getDBSession()})
281
+
282
+ // this.doc.create(data)
283
+ // this.doc
284
+
285
+ // result = await newdoc.save()
286
+ await this.hook(appuser,HookType.afterCreate, result);
287
+
288
+ return result as T;
289
+ }
290
+
291
+
292
+ applyNestedDateTime=(appuser:UserContext,data:any,transtype:string)=>{
293
+
294
+ const props = Object.getOwnPropertyNames(data)
295
+ for(let i=0;i<props.length;i++){
296
+ const key = props[i]
297
+ //need to apply nested
298
+ if(Array.isArray(data[key]) && data[key].length>0 && typeof data[key][0] == 'object'){
299
+ for(let j=0;j<data[key].length;j++){
300
+ this.applyNestedDateTime(appuser,data[key][j],transtype)
301
+ }
302
+ }else if(key == 'created'){
303
+ data['created'] = (transtype == 'create' || !data['created']) ? new Date() .toISOString() : data['created']
304
+ }else if(key == 'createdby'){
305
+ data['createdby'] = (transtype == 'create' || !data['createdby']) ? appuser.getUid() : data['createdby']
306
+ }else if(key == 'updated'){
307
+ data['updated'] = new Date() .toISOString()
308
+ }else if(key == 'updatedby'){
309
+ data['updatedby'] = appuser.getUid()
310
+ }
311
+ }
312
+
313
+ }
314
+ hook = async (appuser:UserContext,type: string, data?: any) => {
315
+ return true;
316
+ };
317
+ async validateData(appuser:UserContext,data: T) {
318
+ const ajv = new Ajv({ allErrors: true, useDefaults: true });
319
+ addFormats(ajv);
320
+ addErrors(ajv);
321
+
322
+ ajv.addFormat('tel', /^$|^\d{7,15}$/gm);
323
+ ajv.addFormat('text', /.*$/);
324
+ ajv.addFormat('html', /.*$/);
325
+ ajv.addFormat('documentno', /.*$/);
326
+
327
+
328
+ ajv.addKeyword({ keyword: 'x-document-no', schemaType:'boolean' });
329
+ ajv.addKeyword({ keyword: 'x-document-label',schemaType: 'boolean' });
330
+ ajv.addKeyword({ keyword: 'x-document-status', schemaType: 'array' });
331
+ ajv.addKeyword({ keyword: 'x-document-api',schemaType: 'array' });
332
+ ajv.addKeyword({ keyword: 'x-page-type',schemaType:'string' }); // undefine mean no generate page
333
+ ajv.addKeyword({ keyword: 'x-ignore-autocomplete',schemaType: 'boolean' });
334
+ ajv.addKeyword({ keyword: 'x-isolation-type', schemaType: 'string' });
335
+ ajv.addKeyword({ keyword: 'x-document-type', schemaType: 'string' });
336
+ ajv.addKeyword({ keyword: 'x-document-name', schemaType: 'string' });
337
+ ajv.addKeyword({ keyword: 'x-collection-name',schemaType: 'string' });
338
+ ajv.addKeyword({ keyword: 'x-autocomplete-field', schemaType: 'boolean' });
339
+ ajv.addKeyword({ keyword: 'x-foreignkey', schemaType: 'string' });
340
+ ajv.addKeyword({ keyword: 'x-import-lib', schemaType: 'array' });
341
+ ajv.addKeyword({ keyword: 'x-compute', schemaType: 'object' });
342
+ ajv.addKeyword({ keyword: 'x-required-role', schemaType: 'array' });
343
+
344
+ const issuccess = await this.hook(appuser,HookType.beforeValidation, data);
345
+ if (!issuccess) {
346
+ const errormsg: string[] = [];
347
+ for (let i = 0; i < this.errorlist.length; i++) {
348
+ errormsg.push(this.errorlist[i].message);
349
+ }
350
+ this.logger.log('run hook during validation');
351
+ throw new BadRequestException("Before validation hook failed",errormsg as HttpExceptionOptions);
352
+ }
353
+
354
+ let validate;
355
+ try {
356
+ validate = ajv.compile(this.jsonschema);
357
+ } catch (err) {
358
+ this.logger.error('compile error', err);
359
+ throw new ForbiddenException(err.message);
360
+ }
361
+ const valid = validate(data);
362
+ if (!valid) {
363
+ this.logger.error(JSON.stringify(validate.errors), 'validate errors:');
364
+ throw new BadRequestException("Data validation failed",validate.errors as HttpExceptionOptions);
365
+ }
366
+ await this.hook(appuser,HookType.afterValidation, data);
367
+
368
+ }
369
+
370
+ polishIsolationFilter = (filterIsolation:any,data:any={}) =>{
371
+ if(this.isolationtype == 'none'){
372
+ delete filterIsolation['branchId']
373
+ delete filterIsolation['orgId']
374
+ delete filterIsolation['tenantId']
375
+ }
376
+ if(this.isolationtype == 'tenant' && !this.strictIsolation){
377
+ // delete filterIsolation['tenantId']
378
+ if(data['tenantId']){
379
+ filterIsolation['tenantId']=data['tenantId']
380
+ }
381
+ delete filterIsolation['branchId']
382
+ delete filterIsolation['orgId']
383
+ }
384
+ if(this.isolationtype == 'org' && !this.strictIsolation){
385
+ // delete filterIsolation['tenantId']
386
+ if(data['tenantId']){
387
+ filterIsolation['tenantId']=data['tenantId']
388
+ }
389
+ if(data['orgId']){
390
+ filterIsolation['orgId']=data['orgId']
391
+ }
392
+ // delete filterIsolation['orgId']
393
+ delete filterIsolation['branchId']
394
+ }
395
+ return filterIsolation
396
+ }
397
+ async findIdThenDelete(appuser:UserContext,id: string): Promise<any> {
398
+ const deletedata = await this.findById(appuser,id);
399
+ let dependency
400
+ try {
401
+ await this.hook(appuser,HookType.beforeDelete, id);
402
+ this.logger.debug('delete record',this.documentName, id);
403
+ dependency = await this.getRelatedRecords(id);
404
+ console.log('dependency', dependency);
405
+ if (!dependency) {
406
+ let filterIsolation = this.getIsolationFilter(appuser);
407
+ this.polishIsolationFilter(filterIsolation)
408
+
409
+ filterIsolation['_id'] = id;
410
+ this.logger.debug('delete filter', filterIsolation);
411
+ const result = await this.doc.deleteOne(filterIsolation).session(appuser.getDBSession());
412
+ const deleteresult = {result:result, data: deletedata}
413
+ this.logger.debug(deleteresult, " delete result" +this.doc.collection.name,)
414
+ // this.doc.findByIdAndDelete(id)
415
+ await this.hook(appuser,HookType.afterDelete, deleteresult);
416
+
417
+ //this.doc.findByIdAndDelete(id);
418
+ return deleteresult;
419
+ } else {
420
+ console.log("reject query",dependency)
421
+
422
+ throw new ForbiddenException('Foreignkey constraint',dependency)
423
+ }
424
+ } catch (err) {
425
+
426
+ throw new InternalServerErrorException(err);
427
+ //JSON.stringify(dependency),JSON.stringify(dependency)
428
+ }
429
+ }
430
+
431
+ // updateOne = async (appuser:UserContext,data: T) => {
432
+ // this.doc.updateOne(data);
433
+ // };
434
+
435
+ findIdThenUpdate = async (appuser:UserContext,id: string, data: T,session:mongo.ClientSession=undefined) => {
436
+ const existingdata = await this.findById(appuser,id);
437
+ await this.hook(appuser,HookType.beforeUpdate, data);
438
+ // try {
439
+ Object.assign(data, appuser.getUpdateFilter());
440
+ Object.assign(existingdata, data);
441
+ delete existingdata['_id']
442
+ // existingdata['_id']=''
443
+ // console.log("newdata",data)
444
+ await this.validateData(appuser,data);
445
+
446
+ const isolationFilter = {...this.getIsolationFilter(appuser)}
447
+ this.polishIsolationFilter(isolationFilter)
448
+
449
+ isolationFilter['_id'] = id;
450
+ this.applyNestedDateTime(appuser,data,'update')
451
+ const result = await this.doc.findOneAndUpdate(isolationFilter, data).session(appuser.getDBSession());
452
+ await this.hook(appuser,HookType.afterUpdate, data);
453
+ return result;
454
+ // } catch (err) {
455
+ // throw new InternalServerErrorException(err.message);
456
+ // }
457
+ };
458
+
459
+ //find what foreign key constraint
460
+ async getRelatedRecords(id: string) {
461
+ this.logger.debug("get foreignkey for delete:",id)
462
+
463
+
464
+ if(foreignkeys === undefined){
465
+ this.logger.error("foreignkeys object undetected")
466
+ throw new InternalServerErrorException("foreignkeys object undetected")
467
+ }
468
+
469
+ const foreignkeysettings = foreignkeys[this.documentName];
470
+ if(!foreignkeysettings){
471
+ return null
472
+ }
473
+ const propkeys = Object.getOwnPropertyNames(foreignkeysettings);
474
+
475
+ if (propkeys.length > 0) {
476
+ //console.log('Have properties');
477
+ for (let i = 0; i < propkeys.length; i++) {
478
+ const collectionname = propkeys[i];
479
+ //console.log('run ', i, collectionname);
480
+ const fobjs = foreignkeysettings[propkeys[i]];
481
+ const collection = this.doc.db.collection(collectionname);
482
+ //single schema may have multiple foreign key link here, loop all
483
+ for (let j = 0; j < fobjs.length; j++) {
484
+ const fkey = fobjs[j] + '._id';
485
+ let filter = {}
486
+ filter[fkey] = id;
487
+
488
+
489
+ const result = await collection.findOne(filter);
490
+ if (result) {
491
+ this.logger.error(result,"related result found")
492
+ return result;
493
+ }
494
+ }
495
+ }
496
+ }
497
+ return null;
498
+ }
499
+
500
+ /**
501
+ * dummy ping
502
+ */
503
+ ping(...data) {
504
+ return `hello ${JSON.stringify(data)}`;
505
+ }
506
+ /**
507
+ * change property documentStatus for specific document, no workflow execution
508
+ * @param id
509
+ * @param docstatus
510
+ * @returns Promise
511
+ */
512
+ async setDocumentStatus(appuser:UserContext,id: string, docstatus: string) {
513
+ const partialdata: T = {} as T;
514
+ partialdata['documentStatus'] = docstatus;
515
+ return this.findIdThenUpdate(appuser,id, partialdata);
516
+ }
517
+
518
+ /**
519
+ * change property documentStatus for specific document, no workflow execution
520
+ * @param id
521
+ * @param docstatus
522
+ * @returns Promise
523
+ */
524
+ async executeWorkFlow(appuser:UserContext,id: string, bpmnname: string, docstatus: string) {
525
+ const data = await this.findById(appuser,id);
526
+ return "ok"
527
+ // Workflow.getInstance().executeWorkFlow(
528
+ // id,
529
+ // bpmnname,
530
+ // docstatus,
531
+ // data,
532
+ // );
533
+ }
534
+
535
+
536
+
537
+ async genNewDocNo(appuser:UserContext,data:T){
538
+ const result = await this.docnogenerator.generateNextNumberFromDocument(appuser,this.documentType,data)
539
+ data[this.documentIdentityCode]=result
540
+ }
541
+
542
+
543
+ async identifyUniqueKeys(appuser:UserContext,data:T){
544
+ /**
545
+ * 1. looping schemas identify what foreign key exists
546
+ * 2. loop through record obtain all foreign key value
547
+ * 3. get all unique key value in array {product:['xxxx','yyyy'],customer:['aaa']}
548
+ */
549
+ const schema = this.jsonschema
550
+
551
+ //get all foreign keys catalogue
552
+ const collections = Object.getOwnPropertyNames(this.foreignkeys)
553
+
554
+ //obtain exists data in according foreign key
555
+ const pipelines :PipelineStage[] =[ {$match:{_id:false}}] //exclude data from current collection
556
+ const vdata = data['_doc']
557
+ const keystore = {}
558
+ collections.forEach((collectionname)=>{
559
+ const fks:string[] = this.foreignkeys[collectionname]
560
+ let results:string[] = []
561
+ fks.forEach(fieldpath=>{
562
+ const tmp = jsonpath.query(vdata,fieldpath).filter((item:string)=>item!='')
563
+ results = results.concat(tmp)
564
+ })
565
+
566
+
567
+
568
+ if(results.length>0){
569
+ console.log(results)
570
+ if(results.length>1){
571
+ results = uniq<string>(results)
572
+ }
573
+
574
+ keystore[collectionname]= results
575
+ let addfield={$addFields:{collection:collectionname}}
576
+
577
+ const stagefilter:PipelineStage = {
578
+ $unionWith: {coll:collectionname,pipeline: [{ $match: { _id: {$in:results}} },addfield,{$project:{collection:1}}, ]}
579
+ }
580
+ pipelines.push(stagefilter)
581
+ }
582
+ })
583
+
584
+ // this.doc.db.collection(collectionname);
585
+ const unionresult = await this.doc.aggregate(pipelines)
586
+
587
+ if(!unionresult){
588
+ throw new InternalServerErrorException("Foreignkey check execution error",pipelines as HttpExceptionOptions)
589
+ }else{
590
+ let searchresult:any = {}
591
+ unionresult.forEach(item=>{
592
+ if(searchresult[item.collection]){
593
+ searchresult[item.collection].push(item._id)
594
+ }else{
595
+ searchresult[item.collection] = [item._id]
596
+ }
597
+ })
598
+
599
+ //search is it all foreign key exists in db
600
+ for(let i=0; i<collections.length; i++){
601
+ const collectionname = collections[i]
602
+ const keys:string[] = keystore[collectionname]
603
+ if(!keys){
604
+ continue
605
+ }
606
+ for(let k=0;k<keys.length; k++){
607
+ const key = keys[k]
608
+ if(!searchresult[collectionname].includes(key)){
609
+ console.log(`Foreignkey ${key} at collection ${collectionname} does not exist`)
610
+ throw new BadRequestException(`Foreignkey ${key} at collection ${collectionname} does not exist`)
611
+ }
612
+ else{
613
+ // console.log(`${collectionname} - ${key} found`)
614
+ }
615
+ }
616
+
617
+ }
618
+
619
+ }
620
+
621
+ }
622
+
623
+
624
+ }
@@ -0,0 +1,19 @@
1
+ export type ForeignKey = {
2
+ _id: string
3
+ label: string
4
+ }
5
+ export type DocNumberFormatResult = {
6
+ formatId: string
7
+ formatName: string
8
+ result: string
9
+ }
10
+
11
+
12
+ export type SearchBody = {
13
+
14
+ filter?:Object;
15
+
16
+ fields?: any[];
17
+
18
+ sorts?: any[];
19
+ }