@simitgroup/simpleapp-generator 1.0.29 → 1.0.32

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 (39) hide show
  1. package/README.md +136 -16
  2. package/dist/framework.js +8 -3
  3. package/dist/framework.js.map +1 -1
  4. package/dist/generate.js +14 -3
  5. package/dist/generate.js.map +1 -1
  6. package/dist/processors/jsonschemabuilder.js +109 -16
  7. package/dist/processors/jsonschemabuilder.js.map +1 -1
  8. package/dist/type.js.map +1 -1
  9. package/package.json +1 -1
  10. package/src/framework.ts +8 -3
  11. package/src/generate.ts +18 -5
  12. package/src/processors/jsonschemabuilder.ts +129 -20
  13. package/src/type.ts +45 -1
  14. package/templates/basic/controller.eta +34 -17
  15. package/templates/basic/model.eta +4 -1
  16. package/templates/basic/pageindex.vue.eta +60 -5
  17. package/templates/basic/service.eta +29 -3
  18. package/templates/basic/simpleappclient.eta +54 -1
  19. package/templates/nest/SimpleAppService.eta +136 -39
  20. package/templates/nest/TenantMiddleware.eta +8 -13
  21. package/templates/nest/UserProvider.eta +127 -0
  22. package/templates/nest/Workflow.eta +75 -0
  23. package/templates/nest/app.controller.eta +12 -0
  24. package/templates/nest/app.module.eta +6 -2
  25. package/templates/nest/app.service.eta +8 -0
  26. package/templates/nest/nest.env.eta +7 -0
  27. package/templates/nest/nest.main.eta +2 -2
  28. package/templates/nuxt/components.crudsimple.vue.eta +2 -2
  29. package/templates/nuxt/components.debugdocdata.vue.eta +12 -6
  30. package/templates/nuxt/components.menus.vue.eta +22 -7
  31. package/templates/nuxt/composables.getautocomplete.ts.eta +3 -5
  32. package/templates/nuxt/composables.getmenus.ts.eta +12 -2
  33. package/templates/nuxt/pages.[xorg].index.vue.eta +9 -8
  34. package/templates/nuxt/pages.index.vue.eta +61 -8
  35. package/templates/nuxt/plugins.simpleapp.ts.eta +7 -0
  36. package/templates/nuxt/server.api.ts.eta +2 -1
  37. package/templates/nuxt/tailwind.css.eta +22 -1
  38. package/tsconfig.json +4 -1
  39. package/templates/nest/User.eta +0 -115
@@ -6,13 +6,16 @@
6
6
  * Author: Ks Tan
7
7
  */
8
8
  import { SimpleAppClient } from "@simitgroup/simpleapp-vue-component/src/SimpleAppClient";
9
+ import {AxiosResponse} from 'axios'
9
10
 
10
11
  // import { JSONSchema7 } from 'json-schema';
11
12
  import { Configuration,
12
13
  <%= it.doctype.toUpperCase()%>Api,
13
14
  <%= it.typename%> ,
14
15
  <%Object.keys(it.schema).forEach(function(key){%>
15
- <% if(typeof it.schema[key]=='string'){%><%= it.schema[key] %>,<%}%>
16
+ <% if(typeof it.schema[key]=='string' || (Array.isArray(it.schema[key]) && typeof it.schema[key][0]=='string')){%>
17
+ <%= it.schema[key] %>,
18
+ <%}%>
16
19
  <%})%>
17
20
 
18
21
  } from '../openapi';
@@ -24,6 +27,9 @@ export class <%= it.typename%>Doc extends SimpleAppClient<<%= it.typename%>,<%=
24
27
  protected documentIdentityCode='<%~ it.autocompletecode %>'
25
28
  protected documentIdentityName='<%~ it.autocompletename %>'
26
29
  constructor(xorg:string,event:any,listen?:any) {
30
+ if(!xorg){
31
+ xorg='MC0wLTA' //0-0-0
32
+ }
27
33
  const apiconfig = new Configuration({ basePath: `${useRuntimeConfig().public.APP_URL}/api/${xorg}` });
28
34
  const apiobj = new <%= it.doctype.toUpperCase()%>Api(apiconfig)
29
35
  super(apiobj,'<%= it.doctype %>','<%=it.name %>')
@@ -57,6 +63,53 @@ export class <%= it.typename%>Doc extends SimpleAppClient<<%= it.typename%>,<%=
57
63
  this.setData(newdata)
58
64
  }
59
65
 
66
+ <%Object.keys(it.jsonschema.properties).forEach(function(key) { %>
67
+ <% let obj=it.jsonschema.properties[key] %>
68
+
69
+ <% if(obj.type=='array' && obj.items.type=='object' && obj.items['properties']){ %>
70
+ <%let tablefields = Object.keys(obj.items.properties) %>
71
+ add<%=key%> = () => {
72
+ const tmp:<%=it.typename%><%=capitalizeFirstLetter(key)%> = {
73
+ <% for(let a=0;a<tablefields.length;a++){%>
74
+ <% const skey = tablefields[a] %>
75
+ <% const sobj = obj.items.properties[skey] %>
76
+ <%if(sobj.type=='object' && typeof sobj['x-foreignkey']!='undefined'){%>
77
+ <%=skey%>: <%~ sobj.default?JSON.stringify(sobj.default): '{_id:"",label:""}'%>,
78
+ <%}else if(sobj.type=='boolean'){%>
79
+ <%=skey%>: <%=sobj.default??false%>,
80
+ <%}else if(sobj.type=='number'){%>
81
+ <%=skey%>: <%=sobj.default??0.00%>,
82
+ <%}else if(sobj.type=='integer'){%>
83
+ <%=skey%>: <%=sobj.default??0%>,
84
+ <%}else if(sobj.type=='string'){%>
85
+ <%=skey%>: '<%=sobj.default??""%>',
86
+ <%}%>
87
+
88
+ <%}%>
89
+ }
90
+ this.getReactiveData().value.<%= key %>.push(tmp)
91
+ }
92
+
93
+
94
+ <%}%>
95
+ <%})%>
96
+
97
+
98
+ <%for(let i=0;i<it.apiSettings.length;i++){%>
99
+ <% let api = it.apiSettings[i] %>
100
+ async exec<%=capitalizeFirstLetter(api.action)%>(){
101
+ const recordid: string = this.data.value._id ?? '';
102
+ return await this.docapi.exec<%=capitalizeFirstLetter(api.action)%>(recordid)
103
+ .then((res: AxiosResponse) => {
104
+ if(this.event){this.event('info:<%=capitalizeFirstLetter(api.action)%>',res.data)}
105
+ return res;
106
+ }).catch((res:any)=>{
107
+ if(this.event){this.event('error:<%=capitalizeFirstLetter(api.action)%>',res)}
108
+ return Promise.reject(res)
109
+ });
110
+ }
111
+ <%}%>
112
+
60
113
  /*****************************customized frontend + backend code*****************************************/
61
114
 
62
115
  <%~ it.bothEndCode %>
@@ -1,41 +1,67 @@
1
- import { Injectable } from '@nestjs/common';
1
+ import { Injectable, Logger } from '@nestjs/common';
2
2
  import { InjectModel } from '@nestjs/mongoose';
3
3
  import { Model } from 'mongoose';
4
4
  import Ajv from 'ajv';
5
5
  import addFormats from 'ajv-formats';
6
+ import addErrors from 'ajv-errors';
6
7
  import foreignkeys from '../dicts/foreignkeys.json';
7
-
8
+ import { Workflow } from './Workflow';
8
9
  import {
9
10
  NotFoundException,
10
11
  InternalServerErrorException,
11
12
  } from '@nestjs/common/exceptions';
12
- import { User } from './User';
13
+ import { UserProvider } from './UserProvider';
13
14
 
14
15
  export enum IsolationType {
16
+ 'none' = 'none',
15
17
  'org' = 'org',
16
18
  'tenant' = 'tenant',
17
19
  'branch' = 'branch',
18
20
  }
21
+ export enum HookType {
22
+ 'init' = 'init',
23
+ 'beforeSearch' = 'beforeSearch',
24
+ 'afterSearch' = 'afterSearch',
25
+ 'beforeValidation' = 'beforeValidation',
26
+ 'afterValidation' = 'afterValidation',
27
+ 'beforeCreate' = 'beforeCreate',
28
+ 'afterCreate' = 'afterCreate',
29
+ 'beforeUpdate' = 'beforeUpdate',
30
+ 'afterUpdate' = 'afterUpdate',
31
+ 'beforeDelete' = 'beforeDelete',
32
+ 'afterDelete' = 'afterDelete',
33
+ 'beforeFetchRecord' = 'beforeFetchRecord',
34
+ 'afterFetchRecord' = 'afterFetchRecord',
35
+ }
36
+ export type MoreProjectionType = {
37
+ [key: string]: string;
38
+ };
19
39
  @Injectable()
20
40
  export class SimpleAppService<T extends { _id?: string }> {
41
+ protected logger = new Logger();
21
42
  protected jsonschema = { type: 'object', properties: {}, required: [] };
22
43
  protected documentIdentityCode = 'code';
23
44
  protected documentIdentityName = 'label';
24
- protected documentName = 'category';
25
- protected documentType = 'CAT';
45
+ protected documentName = '-unknowndocname-';
46
+ protected documentType = '-unknowndoctype-';
26
47
  protected LIMITPERPAGE = 20;
48
+ protected moreAutoCompleteField: MoreProjectionType = {};
27
49
  protected isolationtype: IsolationType = IsolationType.org;
28
50
  protected isolationFilter: any = {};
29
51
  protected data: T = { _id: '' } as T;
30
52
  private doc: Model<T>; //set private to prevent developer break data isolation control
31
53
  protected errorlist = [];
32
54
  constructor(
55
+ doctype: string,
56
+ docname: string,
33
57
  newdoc: Model<T>,
34
58
  isolationtype: IsolationType = IsolationType.org,
35
59
  ) {
60
+ this.documentType = doctype;
61
+ this.documentName = docname;
36
62
  this.doc = newdoc;
37
63
  this.isolationtype = isolationtype;
38
-
64
+ this.hook(HookType.init, this.documentName);
39
65
  // this.tenantdoc = tenantdoc
40
66
  }
41
67
  getRecordId = (): string => this.data._id;
@@ -53,15 +79,18 @@ export class SimpleAppService<T extends { _id?: string }> {
53
79
  getIsolationFilter = () => {
54
80
  let isolationFilter = {};
55
81
  switch (this.isolationtype) {
82
+ case 'none':
83
+ isolationFilter = {};
84
+ break;
56
85
  case 'branch':
57
- isolationFilter = User.getInstance().getBranchFilter();
86
+ isolationFilter = UserProvider.getInstance().getBranchFilter();
58
87
  break;
59
88
  case 'tenant':
60
- isolationFilter = User.getInstance().getTenantFilter();
89
+ isolationFilter = UserProvider.getInstance().getTenantFilter();
61
90
  break;
62
91
  case 'org':
63
92
  default:
64
- isolationFilter = User.getInstance().getOrgFilter();
93
+ isolationFilter = UserProvider.getInstance().getOrgFilter();
65
94
  break;
66
95
  }
67
96
  return isolationFilter;
@@ -80,20 +109,32 @@ export class SimpleAppService<T extends { _id?: string }> {
80
109
  throw new InternalServerErrorException(err.message);
81
110
  }
82
111
  }
112
+ addAutoCompleteField = (morefield: MoreProjectionType) => {
113
+ const props = Object.getOwnPropertyNames(morefield);
114
+ console.log('addAutoCompleteField', props);
115
+ for (let i = 0; i < props.length; i++) {
116
+ const key = props[i];
117
+ this.moreAutoCompleteField[key] = '$' + morefield[key];
118
+ }
119
+ console.log(this.moreAutoCompleteField);
120
+ };
83
121
  async getAutoComplete(keyword: string) {
84
122
  try {
85
123
  const filter1 = {};
86
124
  const filter2 = {};
87
- filter1[this.documentIdentityCode] = { $regex: keyword };
88
- filter2[this.documentIdentityName] = { $regex: keyword };
125
+ filter1[this.documentIdentityCode] = { $regex: keyword, $options: 'i' };
126
+ filter2[this.documentIdentityName] = { $regex: keyword, $options: 'i' };
89
127
  const filterobj = { $or: [filter1, filter2] };
90
128
 
91
129
  Object.assign(filterobj, this.getIsolationFilter());
92
- const projections = {
130
+ let projections = {
93
131
  id: `\$_id`,
94
132
  label: `\$${this.documentIdentityCode}`,
95
133
  name: `\$${this.documentIdentityName}`,
96
134
  };
135
+ if (this.moreAutoCompleteField) {
136
+ Object.assign(projections, this.moreAutoCompleteField);
137
+ }
97
138
  const products = await this.doc.find(filterobj, projections, {
98
139
  limit: this.LIMITPERPAGE,
99
140
  });
@@ -108,10 +149,12 @@ export class SimpleAppService<T extends { _id?: string }> {
108
149
  async search(filters: Object) {
109
150
  try {
110
151
  Object.assign(filters, this.getIsolationFilter());
152
+ await this.hook(HookType.beforeSearch, filters);
111
153
  const products = await this.doc.find(filters);
112
154
  const productlist = products.map((p: T) => {
113
155
  return p;
114
156
  });
157
+ await this.hook(HookType.afterSearch, productlist);
115
158
  // console.log(products);
116
159
  return productlist;
117
160
  } catch (err) {
@@ -120,7 +163,9 @@ export class SimpleAppService<T extends { _id?: string }> {
120
163
  // return this;
121
164
  }
122
165
  async findById(id: string) {
166
+ await this.hook(HookType.beforeFetchRecord, id);
123
167
  const data = await this.search({ _id: id });
168
+ await this.hook(HookType.afterFetchRecord, data);
124
169
  if (data.length == 1) {
125
170
  // console.log('data0', data[0]);
126
171
  return data[0];
@@ -133,11 +178,13 @@ export class SimpleAppService<T extends { _id?: string }> {
133
178
  let result;
134
179
 
135
180
  try {
136
- Object.assign(this.data, User.getInstance().getCreateFilter());
181
+ await this.hook(HookType.beforeCreate, this.data);
182
+ Object.assign(this.data, UserProvider.getInstance().getCreateFilter());
137
183
  delete this.data._id;
138
- this.validateData(this.data);
184
+ await this.validateData(this.data);
139
185
  const newdoc = new this.doc(this.data);
140
186
  result = await newdoc.save();
187
+ await this.hook(HookType.afterCreate, result);
141
188
  } catch (err) {
142
189
  throw new InternalServerErrorException(err.message);
143
190
  }
@@ -145,8 +192,8 @@ export class SimpleAppService<T extends { _id?: string }> {
145
192
  }
146
193
  async update() {
147
194
  const id: string = this.getRecordId();
148
- Object.assign(this.data, User.getInstance().getUpdateFilter());
149
- this.findIdThenUpdate(id, this.data);
195
+ Object.assign(this.data, UserProvider.getInstance().getUpdateFilter());
196
+ await this.findIdThenUpdate(id, this.data);
150
197
  }
151
198
  async delete() {
152
199
  const id: string = this.getRecordId();
@@ -157,47 +204,65 @@ export class SimpleAppService<T extends { _id?: string }> {
157
204
  return dependency;
158
205
  }
159
206
  }
160
- hook(type: string, data: T) {
207
+ hook = async (type: string, data?: any) => {
208
+ // console.log('Default hook', data);
161
209
  return true;
162
- }
163
- validateData(data: T) {
210
+ };
211
+ async validateData(data: T) {
164
212
  const ajv = new Ajv({ allErrors: true });
165
213
  addFormats(ajv);
214
+ addErrors(ajv);
215
+
166
216
  ajv.addFormat('x-document-no', /.*$/);
167
- ajv.addFormat('x-document-name', /.*$/);
217
+ ajv.addFormat('x-document-label', /.*$/);
218
+ ajv.addFormat('tel', /^$|^\d{7,15}$/gm);
168
219
  ajv.addFormat('x-text', /.*$/);
169
220
  ajv.addFormat('x-html', /.*$/);
170
- ajv.addKeyword({
171
- keyword: 'x-foreignkey',
172
- type: 'string',
173
- });
174
221
 
175
- if (!this.hook('pre-validation', data)) {
222
+ ajv.addKeyword({ keyword: 'x-document-status', type: 'array' });
223
+ ajv.addKeyword({ keyword: 'x-document-api', type: 'array' });
224
+ ajv.addKeyword({ keyword: 'x-ignore-autocomplete', type: 'boolean' });
225
+ ajv.addKeyword({ keyword: 'x-isolation-type', type: 'string' });
226
+ ajv.addKeyword({ keyword: 'x-document-type', type: 'string' });
227
+ ajv.addKeyword({ keyword: 'x-document-name', type: 'string' });
228
+ ajv.addKeyword({ keyword: 'x-collection-name', type: 'string' });
229
+ ajv.addKeyword({ keyword: 'x-autocomplete-field', type: 'boolean' });
230
+ ajv.addKeyword({ keyword: 'x-foreignkey', type: 'string' });
231
+
232
+ const issuccess = await this.hook(HookType.beforeValidation, data);
233
+ if (!issuccess) {
176
234
  const erromsg: string[] = [];
177
235
  for (let i = 0; i < this.errorlist.length; i++) {
178
236
  erromsg.push(this.errorlist[i].message);
179
237
  }
180
- console.log("pre-validation:",erromsg)
238
+ this.logger.log('run hook during validation');
181
239
  throw new InternalServerErrorException(erromsg.join('\n'));
182
240
  }
183
- const validate = ajv.compile(this.jsonschema);
241
+
242
+ let validate;
243
+ try {
244
+ validate = ajv.compile(this.jsonschema);
245
+ } catch (err) {
246
+ this.logger.error('compile error', err);
247
+ }
248
+
184
249
  const valid = validate(data);
185
- //console.log(validate.errors);
186
250
  if (!valid) {
187
- // console.log(validate.errors);
251
+ this.logger.error(validate.errors);
188
252
  const erromsg: string[] = [];
189
- for (let i = 0; i < validate.errors.length; i++) {
190
- erromsg.push(validate.errors[i].message);
253
+ for (let i = 0; i < ajv.errors.length; i++) {
254
+ erromsg.push(ajv.errors[i].message);
191
255
  }
192
- console.log("ajv erromsg: ",erromsg)
256
+ this.logger.error('ajv erromsg: ', erromsg);
193
257
  throw new InternalServerErrorException(erromsg.join('\n'));
194
258
  }
195
- this.hook('post-validation', data);
259
+ await this.hook(HookType.afterValidation, data);
196
260
  }
197
261
 
198
262
  async findIdThenDelete(id: string): Promise<any> {
199
263
  // const data = await this.findById(id);
200
264
  try {
265
+ await this.hook(HookType.beforeDelete, id);
201
266
  //console.log('deletedeletedeletedelete');
202
267
  const dependency = await this.getRelatedRecords(id);
203
268
  if (!dependency) {
@@ -205,7 +270,7 @@ export class SimpleAppService<T extends { _id?: string }> {
205
270
  filter['_id'] = id;
206
271
  const deleteresult = await this.doc.deleteOne(filter);
207
272
  // this.doc.findByIdAndDelete(id)
208
-
273
+ await this.hook(HookType.afterDelete, deleteresult);
209
274
  //this.doc.findByIdAndDelete(id);
210
275
  return deleteresult;
211
276
  } else {
@@ -222,15 +287,15 @@ export class SimpleAppService<T extends { _id?: string }> {
222
287
 
223
288
  findIdThenUpdate = async (id: string, data: T) => {
224
289
  const existingdata = await this.findById(id);
225
-
290
+ await this.hook(HookType.beforeUpdate, data);
226
291
  try {
227
- Object.assign(data, User.getInstance().getUpdateFilter());
292
+ Object.assign(data, UserProvider.getInstance().getUpdateFilter());
228
293
  Object.assign(existingdata, data);
229
- this.validateData(data);
294
+ await this.validateData(data);
230
295
  let filter = this.getIsolationFilter();
231
296
  filter['_id'] = id;
232
297
  const result = await this.doc.findOneAndUpdate(filter, data);
233
-
298
+ await this.hook(HookType.afterUpdate, data);
234
299
  return result;
235
300
  } catch (err) {
236
301
  throw new InternalServerErrorException(err.message);
@@ -252,7 +317,7 @@ export class SimpleAppService<T extends { _id?: string }> {
252
317
  const collection = this.doc.db.collection(collectionname);
253
318
  //single schema may have multiple foreign key link here, loop all
254
319
  for (let j = 0; j < fobjs.length; j++) {
255
- const fkey = fobjs[j];
320
+ const fkey = fobjs[j] + '._id';
256
321
  const filter = {};
257
322
  filter[fkey] = id;
258
323
  //console.log('getRelatedRecords get filter', collectionname, filter);
@@ -266,4 +331,36 @@ export class SimpleAppService<T extends { _id?: string }> {
266
331
  }
267
332
  return Promise.resolve(null);
268
333
  }
334
+
335
+ /**
336
+ * change property documentStatus for specific document, no workflow execution
337
+ * @param id
338
+ * @param docstatus
339
+ * @returns Promise
340
+ */
341
+ async setDocumentStatus(id: string, docstatus: string) {
342
+ const partialdata: T = {} as T;
343
+ partialdata['documentStatus'] = docstatus;
344
+ return this.findIdThenUpdate(id, partialdata);
345
+ }
346
+
347
+ /**
348
+ * change property documentStatus for specific document, no workflow execution
349
+ * @param id
350
+ * @param docstatus
351
+ * @returns Promise
352
+ */
353
+ async executeWorkFlow(id: string, bpmnname: string, docstatus: string) {
354
+ const data = await this.findById(id);
355
+ return Workflow.getInstance().executeWorkFlow(
356
+ id,
357
+ bpmnname,
358
+ docstatus,
359
+ data,
360
+ );
361
+ }
362
+
363
+ async getMyUserTask() {
364
+ return Workflow.getInstance().getMyUserTask();
365
+ }
269
366
  }
@@ -2,38 +2,33 @@ import { Injectable, NestMiddleware, Scope } from '@nestjs/common';
2
2
  import { Request, Response, NextFunction } from 'express';
3
3
  // import * as jwt from 'nestjs-jwt'
4
4
 
5
-
6
- import { User } from './User';
5
+ import { UserProvider } from './UserProvider';
7
6
  // import {KeycloakConfigService} from "../keycloak/keycloak.service"
8
7
  @Injectable({
9
8
  scope: Scope.REQUEST,
10
9
  })
11
-
12
10
  export class TenantMiddleware implements NestMiddleware {
13
11
  use(req: Request, res: Response, next: NextFunction) {
14
12
  if (req.baseUrl == '/oauth2-redirect.html') {
15
13
  next();
16
- return ;
14
+ return;
17
15
  }
18
- if(!req.headers['authorization']){
16
+ if (!req.headers['authorization']) {
19
17
  return res.status(401).send('Undefine bearer token');
20
18
  }
21
- if (!req.headers['x-org']){
19
+ if (!req.headers['x-org']) {
22
20
  return res.status(401).send('undefine header string x-org');
23
21
  }
24
- const u = User.getInstance();
22
+ const u = UserProvider.getInstance();
25
23
 
26
24
  try {
27
25
  u.setXorg(req.headers['x-org'].toString());
28
- let tokenstr:string = req.headers['authorization']
29
- tokenstr = tokenstr.replace("Bearer ",'')
30
- u.setUserToken(tokenstr);
26
+ let tokenstr: string = req.headers['authorization'];
27
+ tokenstr = tokenstr.replace('Bearer ', '');
28
+ u.setUserToken(tokenstr);
31
29
  next();
32
30
  } catch {
33
31
  return res.status(401).send('Invalid x-org or user info');
34
32
  }
35
-
36
-
37
-
38
33
  }
39
34
  }
@@ -0,0 +1,127 @@
1
+ import { Injectable, Scope } from '@nestjs/common';
2
+ import Base64URL from '@darkwolf/base64url';
3
+ import * as jwt from 'jsonwebtoken';
4
+
5
+ @Injectable({
6
+ scope: Scope.REQUEST,
7
+ })
8
+ export class UserProvider {
9
+ private static instance: UserProvider;
10
+ protected uid: string = '';
11
+ protected uname: string = '';
12
+ protected email: string = '';
13
+ protected fullname: string = '';
14
+ protected xOrg: string = '';
15
+ protected tenantId: number = 0;
16
+ protected orgId: number = 0;
17
+ protected branchId: number = 0;
18
+ protected accessrights: any = {};
19
+ protected token: string = '';
20
+ protected refreshtoken: string = '';
21
+ protected groups: string[] = [];
22
+ constructor() {}
23
+ public static getInstance(): UserProvider {
24
+ if (!UserProvider.instance) {
25
+ UserProvider.instance = new UserProvider();
26
+ }
27
+ return UserProvider.instance;
28
+ }
29
+ getUid = () => this.uid;
30
+ getUname = () => this.uname;
31
+ getFullname = () => this.fullname;
32
+ getTenantId = () => this.tenantId;
33
+ getOrgId = () => this.orgId;
34
+ getBranchId = () => this.branchId;
35
+ getEmail = () => this.email;
36
+ getGroups = () => this.groups;
37
+ setUserToken = (tokenstr: string) => {
38
+ const tokeninfo = jwt.decode(tokenstr);
39
+ // realm_access: {
40
+ // roles: [
41
+ // 'default-roles-simitdeveloper',
42
+ // 'offline_access',
43
+ // 'uma_authorization'
44
+ // ]
45
+ // },
46
+ // resource_access: { account: { roles: [Array] } },
47
+ // scope: 'openid email profile',
48
+ // sid: '53192f53-d4af-413b-b8d7-1e186419fe53',
49
+ // email_verified: false,
50
+ // name: 'kstan kstan',
51
+ // preferred_username: 'kstan',
52
+ // given_name: 'kstan',
53
+ // family_name: 'kstan',
54
+ // email: 'kstan@simitgroup.com'
55
+
56
+ const u = UserProvider.getInstance();
57
+ u.token = tokenstr;
58
+ u.uid = tokeninfo.sub;
59
+ u.email = tokeninfo.email;
60
+ u.uname = tokeninfo.preferred_username;
61
+ u.fullname = tokeninfo.name;
62
+ u.groups = ['superuser', 'accountant', 'directors'];
63
+ u.accessrights = tokeninfo.resource_access;
64
+ };
65
+ getInfo = () => {
66
+ return UserProvider.getInstance();
67
+ };
68
+ getBranchFilter = () => {
69
+ return {
70
+ tenantId: UserProvider.getInstance().tenantId,
71
+ orgId: UserProvider.getInstance().orgId,
72
+ branchId: UserProvider.getInstance().branchId,
73
+ };
74
+ };
75
+ getTenantFilter = () => {
76
+ return { tenantId: UserProvider.getInstance().tenantId };
77
+ };
78
+ getOrgFilter = () => {
79
+ return {
80
+ tenantId: UserProvider.getInstance().tenantId,
81
+ orgId: UserProvider.getInstance().orgId,
82
+ };
83
+ };
84
+ getWorkflowTaskFilter() {
85
+ return {
86
+ 'data.tenantId': UserProvider.getInstance().tenantId,
87
+ 'assignments.assignee': UserProvider.getInstance().getUid(),
88
+ // 'assignments.assignee': User.getInstance().getUid(),
89
+ };
90
+ }
91
+ getCreateFilter = () => {
92
+ const u = UserProvider.getInstance();
93
+ return {
94
+ tenantId: u.tenantId,
95
+ orgId: u.orgId,
96
+ branchId: u.branchId,
97
+ createdby: u.uid,
98
+ updatedby: u.uid,
99
+ created: new Date().getTime().toString(),
100
+ updated: new Date().getTime().toString(),
101
+ };
102
+ };
103
+ getUpdateFilter = () => {
104
+ const u = UserProvider.getInstance();
105
+ return {
106
+ updatedby: u.uid,
107
+ updated: new Date().getTime().toString(),
108
+ };
109
+ };
110
+ setXorg = (xorg) => {
111
+ try {
112
+ const decodedText: string = Base64URL.decodeText(xorg);
113
+ const arrXorg = decodedText.split('-');
114
+
115
+ if (arrXorg.length == 3) {
116
+ const u = UserProvider.getInstance();
117
+ u.tenantId = Number(arrXorg[0]);
118
+ u.orgId = Number(arrXorg[1]);
119
+ u.branchId = Number(arrXorg[2]);
120
+ } else {
121
+ throw 'invalid x-org';
122
+ }
123
+ } catch (err) {
124
+ throw err;
125
+ }
126
+ };
127
+ }
@@ -0,0 +1,75 @@
1
+ import { BPMNClient } from "bpmn-client";
2
+ import { UserProvider } from './UserProvider';
3
+ import { Injectable } from '@nestjs/common';
4
+
5
+ export type UserTaskActorOptions={
6
+ userId:string
7
+ actGroups?:string[]
8
+ actUsers?:string[]
9
+ }
10
+ @Injectable()
11
+ export class Workflow{
12
+ private static instance: Workflow;
13
+ server:BPMNClient
14
+
15
+ constructor(){
16
+ this.server= new BPMNClient(process.env.BPMN_HOST, process.env.BPMN_PORT, process.env.BPMN_API_KEY);
17
+ }
18
+ public static getInstance(): Workflow {
19
+ if (!Workflow.instance) {
20
+ Workflow.instance = new Workflow();
21
+ }
22
+ return Workflow.instance;
23
+ }
24
+
25
+ async executeWorkFlow(id: string, bpmnname: string, docstatus: string,data:any) {
26
+ const workflowactoroptions : UserTaskActorOptions ={
27
+ userId:UserProvider.getInstance().getUid(),
28
+ actGroups:[],
29
+ actUsers:[]
30
+ }
31
+ var instance = await this.server.engine.start(bpmnname, data,null,workflowactoroptions);
32
+ const res = {instanceId:instance.id,data:instance.data,name:instance.name,status:instance.status}
33
+ return Promise.resolve(res);
34
+ }
35
+ async getMyUserTask(){
36
+ const uid = UserProvider.getInstance().getUid()
37
+ const groups = UserProvider.getInstance().getGroups()
38
+ // 'assignments.candidateUsers': User.getInstance().getUid(),
39
+ // 'assignments.candidateGroups': User.getInstance().getGroups
40
+
41
+ let anyof:any = [
42
+ {'items.assignments.assignee':uid},
43
+ ]
44
+ let usersfilter={}
45
+ usersfilter[uid]={$in: 'items.assignments.candidateUsers'}
46
+ anyof.push(usersfilter)
47
+ //{'data.tenantId': 1,'$or':[{
48
+ // 'items.assignments.assignee': 'b2a49a8f-a943-4814-8087-60b1ef2f304f'
49
+ // }]}
50
+ //any of the group
51
+ for(let i=0;i<groups.length;i++){
52
+ const gname = groups[i]
53
+ const tmp = {}
54
+ tmp[gname]={ $in: 'items.assignments.candidateGroups'}
55
+ anyof.push(tmp)
56
+ }
57
+ const filters = {
58
+ 'data.tenantId': UserProvider.getInstance().getTenantId(),
59
+ '$or':anyof
60
+ }
61
+ console.dir(filters)
62
+
63
+
64
+ // 'assignments.assignee': User.getInstance().getUid(),
65
+ const client = new BPMNClient(process.env.BPMN_HOST, process.env.BPMN_PORT, process.env.BPMN_API_KEY);
66
+ const items = await client.datastore.findItems(filters)
67
+ let data=[]
68
+ for(let i=0;i<items.length;i++){
69
+ if(items[i].status=='wait'){
70
+ data.push(items[i])
71
+ }
72
+ }
73
+ return Promise.resolve(data);
74
+ }
75
+ }
@@ -0,0 +1,12 @@
1
+ import { Controller, Get } from '@nestjs/common';
2
+ import { AppService } from './app.service';
3
+
4
+ @Controller()
5
+ export class AppController {
6
+ constructor(private readonly appService: AppService) {}
7
+
8
+ @Get('myworkflowtask')
9
+ async getHello() {
10
+ return await this.appService.getMyTask();
11
+ }
12
+ }