complexqa_frontend_core 1.0.0

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.
@@ -0,0 +1,2991 @@
1
+ /**
2
+ * Прототип идеи, пока не уверен, что именно так надо
3
+ *
4
+ * @version v.0.1 (25/06/2024)
5
+ */
6
+ class App
7
+ {
8
+
9
+ init_options = false;
10
+
11
+ /**
12
+ *
13
+ * @version v.0.1 (25/06/2024)
14
+ */
15
+ constructor(init_options)
16
+ {
17
+
18
+ if (this.constructor._instance)
19
+ {
20
+ return this.constructor._instance;
21
+ }
22
+
23
+ this.init_options = init_options;
24
+ this.constructor._instance = this;
25
+
26
+ this.#bootstrap();
27
+
28
+ return this.constructor._instance;
29
+ }
30
+
31
+ /**
32
+ *
33
+ * @version v.0.1 (25/06/2024)
34
+ */
35
+ #bootstrap()
36
+ {
37
+ this.service = {};
38
+ this.service.translate = new serviceTranslate();
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Общие методы для всех типизированных элементов
44
+ *
45
+ * @version v.2.0 (24/11/2024)
46
+ */
47
+ class ApiAbstractElementClass
48
+ {
49
+
50
+ init_options;
51
+ element_class_instance;
52
+ dev_stage_config;
53
+ parent_app;
54
+ host_api;
55
+ stage = 'production';
56
+ module_prefix = false;
57
+ api_prefix = 'web_api';
58
+ method;
59
+ headers;
60
+
61
+
62
+ /**
63
+ *
64
+ * @version v.0.1 (27/06/2024)
65
+ * @param options
66
+ */
67
+ constructor(options)
68
+ {
69
+ if (options?.init_options)
70
+ {
71
+ this.init_options = options?.init_options;
72
+ }
73
+ if (options?.stage)
74
+ {
75
+ this.stage = options?.stage;
76
+ }
77
+ if (options?.dev_stage_config)
78
+ {
79
+ this.dev_stage_config = options?.dev_stage_config;
80
+ }
81
+ if (options?.parent_app)
82
+ {
83
+ this.parent_app = options?.parent_app;
84
+ }
85
+ if (options?.host_api)
86
+ {
87
+ this.host_api = options?.host_api;
88
+ }
89
+ }
90
+
91
+
92
+ set_element_class_instance(element_class_instance)
93
+ {
94
+ this.element_class_instance = element_class_instance;
95
+ }
96
+
97
+
98
+ /**
99
+ *
100
+ * @version v.0.1 (26/05/2024)
101
+ * @param {object|typeFOR} payload
102
+ * @param {array} payload.filter
103
+ * @param {?object|undefined} payload.object
104
+ * @param {?object|undefined} payload.response
105
+ */
106
+ async search(payload)
107
+ {
108
+ if (!this.module_prefix)
109
+ {
110
+ throw new Error('Error #10-1152 in ApiAbstractClass');
111
+ }
112
+
113
+ if (this.stage === 'development')
114
+ {
115
+ let response = await this.#mock_search(payload);
116
+ return {
117
+ response: response,
118
+ payload : payload
119
+ };
120
+ }
121
+
122
+ let url = `/${ this.api_prefix }/${ this.module_prefix }/search`;
123
+
124
+ payload = this.#handle_request_payload(payload);
125
+ let response = this.#api_request(url, payload);
126
+ response = this.#handle_response(response);
127
+
128
+ return {
129
+ response: response,
130
+ payload : payload
131
+ };
132
+ }
133
+
134
+
135
+ /**
136
+ *
137
+ * @version v.0.1 (27/06/2024)
138
+ * @param payload
139
+ * @returns {Promise<array>}
140
+ */
141
+ async #mock_search(payload)
142
+ {
143
+ // @todo - дописать
144
+ if (this.dev_stage_config.api_response_search_element_count > 0 && this.dev_stage_config.api_response_search_element_count < 999)
145
+ {
146
+
147
+ }
148
+ else
149
+ {
150
+ throw new Error('Error #100-154');
151
+ }
152
+
153
+ let model = new this.element_class_instance();
154
+ let response = model.get_random_demo_data(this.dev_stage_config.api_response_search_element_count)
155
+
156
+ return response;
157
+
158
+ }
159
+
160
+
161
+ /**
162
+ *
163
+ * @version v.0.1 (26/05/2024)
164
+ * @param payload
165
+ */
166
+ async find(payload)
167
+ {
168
+ if (!this.module_prefix)
169
+ {
170
+ throw new Error('Error #10-1152 in ApiAbstractClass');
171
+ }
172
+ }
173
+
174
+
175
+ /**
176
+ *
177
+ * @version v.0.1 (26/05/2024)
178
+ * @param payload
179
+ */
180
+ async create(payload)
181
+ {
182
+ if (!this.module_prefix)
183
+ {
184
+ throw new Error('Error #10-1152 in ApiAbstractClass');
185
+ }
186
+
187
+ if (this.stage === 'development')
188
+ {
189
+ return {
190
+ response: payload,
191
+ payload : payload
192
+ };
193
+ }
194
+
195
+ let url = `/${ this.api_prefix }/${ this.module_prefix }/create`;
196
+
197
+ payload = this.#handle_request_payload(payload);
198
+ let response = this.#api_request(url, payload);
199
+ response = this.#handle_response(response);
200
+
201
+ return {
202
+ response: response,
203
+ payload : payload
204
+ };
205
+ }
206
+
207
+
208
+ /**
209
+ *
210
+ * @version v.0.1 (26/05/2024)
211
+ * @param payload
212
+ */
213
+ async update(payload)
214
+ {
215
+ if (!this.module_prefix)
216
+ {
217
+ throw new Error('Error #10-1152 in ApiAbstractClass');
218
+ }
219
+
220
+ }
221
+
222
+
223
+ /**
224
+ *
225
+ * @version v.0.1 (26/05/2024)
226
+ * @param payload
227
+ */
228
+ async update_property(payload)
229
+ {
230
+ if (!this.module_prefix)
231
+ {
232
+ throw new Error('Error #10-1152 in ApiAbstractClass');
233
+ }
234
+
235
+ }
236
+
237
+
238
+ /**
239
+ *
240
+ * @version v.0.1 (26/05/2024)
241
+ * @param payload
242
+ */
243
+ async delete(payload)
244
+ {
245
+ if (!this.module_prefix)
246
+ {
247
+ throw new Error('Error #10-1152 in ApiAbstractClass');
248
+ }
249
+
250
+ }
251
+
252
+
253
+ /******************************************************************************************/
254
+ /******************************************************************************************/
255
+
256
+ /******************************************************************************************/
257
+
258
+ #handle_request_payload(payload)
259
+ {
260
+ return this.#object_replace_null_recursively(payload);
261
+ }
262
+
263
+ #object_replace_null_recursively(object)
264
+ {
265
+ let result = clone_object(object);
266
+ for (let key in result)
267
+ {
268
+ if (result[ key ] === null)
269
+ {
270
+ result[ key ] = 'NULL';
271
+ }
272
+ else if (( result[ key ]?.constructor === Object ) || ( result[ key ]?.constructor === Array ))
273
+ {
274
+ result[ key ] = this.#object_replace_null_recursively(result[ key ]);
275
+ }
276
+ }
277
+
278
+ return result;
279
+ }
280
+
281
+
282
+ /**
283
+ *
284
+ * @param url
285
+ * @param payload
286
+ * @param method
287
+ * @returns {Promise<Promise<axios.AxiosResponse<any>> | *>}
288
+ * @version v.0.2 (26/01/2025)
289
+ */
290
+ async #api_request(url, payload, method = 'POST')
291
+ {
292
+ let request_params;
293
+ if (!payload)
294
+ {
295
+ request_params = {};
296
+ }
297
+ else
298
+ {
299
+ request_params = clone_object(payload)
300
+ }
301
+
302
+ request_params = this.#mixin_service_params(request_params);
303
+
304
+ console.log({payload, request_params});
305
+
306
+
307
+ if (this.host_api)
308
+ {
309
+ url = 'https://' + this.host_api + url;
310
+ }
311
+
312
+ let request_options = {
313
+ method : method,
314
+ headers: this.headers,
315
+ url : url
316
+ };
317
+
318
+ if (this.method === 'get')
319
+ {
320
+ request_options.paramsSerializer = this.object_to_string;
321
+ request_options.params = request_params;
322
+ }
323
+ else
324
+ {
325
+ request_options.data = (request_params);
326
+ }
327
+
328
+ let config = {};
329
+
330
+ return axios(request_options, config);
331
+ }
332
+
333
+
334
+ /**
335
+ *
336
+ * @param object
337
+ * @returns {string}
338
+ */
339
+ object_to_string(object)
340
+ {
341
+ let result = qs.stringify(object, { array_format: 'indices' });
342
+
343
+ return result;
344
+ }
345
+
346
+
347
+ #mixin_service_params(params)
348
+ {
349
+ params.client = 'webapp';
350
+ return params;
351
+ }
352
+
353
+
354
+ /**
355
+ *
356
+ * @param {} response
357
+ * @returns {Promise<any>}
358
+ * @version v.0.1 (01/06/2024)
359
+ */
360
+ #handle_response(response)
361
+ {
362
+ response = response.then((api_response) =>
363
+ {
364
+ let error_payload = {
365
+ api_response: api_response?.data?.api_response,
366
+ api_code : api_response?.data?.api_code,
367
+ api_result : api_response?.data?.api_result,
368
+ api_error : api_response?.data?.api_error
369
+ };
370
+
371
+ if (api_response?.data?.api_error?.adapted_message)
372
+ {
373
+ error_payload.message = api_response.data.api_error.adapted_message
374
+ throw new ApiException(error_payload);
375
+ }
376
+ else if (api_response?.data?.api_code)
377
+ {
378
+ let api_code = api_response.data.api_code;
379
+
380
+ if (( api_code >= 200 ) && ( api_code <= 299 ))
381
+ {
382
+ if (is_array(api_response?.data) && this.element_class_instance)
383
+ {
384
+ let result = api_response?.data.map((row) =>
385
+ {
386
+ return new this.element_class_instance(row);
387
+ });
388
+
389
+ return result;
390
+ }
391
+ else
392
+ {
393
+ return api_response?.data;
394
+ }
395
+ }
396
+ else if (api_response?.data?.api_result === false)
397
+ {
398
+ throw new ApiException(error_payload);
399
+ }
400
+ else
401
+ {
402
+ throw new ApiException(error_payload);
403
+ }
404
+ }
405
+ else
406
+ {
407
+ return api_response?.data;
408
+ }
409
+ })
410
+ .catch((error) =>
411
+ {
412
+
413
+ echo({ app: this });
414
+ console.error(error);
415
+ if (error instanceof ApiException)
416
+ {
417
+ throw error;
418
+ }
419
+
420
+
421
+ throw new ApiException({ error: error });
422
+ });
423
+
424
+ return response;
425
+ }
426
+ }
427
+
428
+ /**
429
+ *
430
+ * @version v.0.1 (24/11/2024)
431
+ */
432
+ class FunctionalApi extends ApiAbstractElementClass
433
+ {
434
+ module_prefix = 'functional';
435
+
436
+ constructor(options)
437
+ {
438
+ super(options);
439
+ this.set_element_class_instance(typeFunctional);
440
+ }
441
+ }
442
+ /**
443
+ * Обертка над axios
444
+ * @version v.0.3 (26/01/2025)
445
+ */
446
+ class Api
447
+ {
448
+ /**
449
+ * Режим работы
450
+ *
451
+ * @type {string} development || production || tests
452
+ */
453
+ stage = 'development';
454
+ dev_stage_config = {
455
+ generate_error : false,
456
+ api_response_delay : 3500,
457
+ api_response_search_element_count: 8,
458
+ };
459
+
460
+ init_options;
461
+
462
+ test_plan;
463
+ /**
464
+ * {ProjectApi} project
465
+ */
466
+ project;
467
+ service;
468
+
469
+ host_api = false;
470
+
471
+
472
+ /**
473
+ *
474
+ * @version v.0.3 (26/01/2025)
475
+ */
476
+ constructor(init_options)
477
+ {
478
+
479
+ if (this.constructor._instance)
480
+ {
481
+ return this.constructor._instance;
482
+ }
483
+
484
+
485
+ if (init_options.host_api)
486
+ {
487
+ // @todo validate
488
+ this.host_api = init_options.host_api;
489
+ }
490
+ else
491
+ {
492
+ // подумать
493
+ this.host_api = 'complexqa.localhost';
494
+ }
495
+
496
+ if (init_options.stage)
497
+ {
498
+ // @todo validate
499
+ this.stage = init_options.stage;
500
+ }
501
+
502
+ if (init_options.dev_stage_config)
503
+ {
504
+ // @todo validate
505
+ this.dev_stage_config = init_options.dev_stage_config;
506
+ }
507
+
508
+ this.init_options = init_options;
509
+ this.constructor._instance = this;
510
+
511
+
512
+ this.#bootstrap();
513
+ return this.constructor._instance;
514
+ }
515
+
516
+
517
+ /**
518
+ *
519
+ * @param stage
520
+ */
521
+ set_stage(stage)
522
+ {
523
+ this.stage = stage;
524
+ }
525
+
526
+
527
+ /**
528
+ *
529
+ * @version v.0.1 (25/06/2024)
530
+ */
531
+ #bootstrap()
532
+ {
533
+ let payload = {
534
+ init_options : this.init_options,
535
+ stage : this.stage,
536
+ dev_stage_config: this.dev_stage_config,
537
+ host_api: this.host_api,
538
+ parent_app : this
539
+ };
540
+
541
+ //echo({payload});
542
+
543
+ this.test_plan = new TestPlanApi(payload);
544
+ this.project = new ProjectApi(payload);
545
+ this.service = new ServiceApi(payload);
546
+ }
547
+ }
548
+ /**
549
+ *
550
+ * @version v.0.1 (15/06/2024)
551
+ */
552
+ class ProjectApi extends ApiAbstractElementClass
553
+ {
554
+ module_prefix = 'project';
555
+
556
+ constructor(options)
557
+ {
558
+ super(options);
559
+ this.set_element_class_instance(typeProject);
560
+ }
561
+ }
562
+
563
+ // тут нужен свой класс, не для типов
564
+ class ServiceApi
565
+ {
566
+ module_prefix = '--';
567
+
568
+ constructor(options)
569
+ {
570
+ }
571
+
572
+
573
+ /**
574
+ *
575
+ * @returns {Promise<*[]>}
576
+ */
577
+ async translate_get_dictionaries()
578
+ {
579
+ return [];
580
+ }
581
+ }
582
+
583
+ /**
584
+ *
585
+ * @version v.0.1 (24/11/2024)
586
+ */
587
+ class TeamMemberApi extends ApiAbstractElementClass
588
+ {
589
+ module_prefix = 'team_member';
590
+
591
+ constructor(options)
592
+ {
593
+ super(options);
594
+ this.set_element_class_instance(typeTeamMember);
595
+ }
596
+ }
597
+
598
+ /**
599
+ *
600
+ * @version v.0.1 (24/11/2024)
601
+ */
602
+ class TestCaseApi extends ApiAbstractElementClass
603
+ {
604
+ module_prefix = 'test_case';
605
+
606
+ constructor(options)
607
+ {
608
+ super(options);
609
+ this.set_element_class_instance(typeTestCase);
610
+ }
611
+ }
612
+
613
+ /**
614
+ *
615
+ * @version v.0.1 (24/11/2024)
616
+ */
617
+ class TestCaseStepApi extends ApiAbstractElementClass
618
+ {
619
+ module_prefix = 'test_case_step';
620
+
621
+ constructor(options)
622
+ {
623
+ super(options);
624
+ this.set_element_class_instance(typeTestCaseStep);
625
+ }
626
+ }
627
+
628
+ /**
629
+ *
630
+ * @version v.0.1 (26/05/2024)
631
+ */
632
+ class TestPlanApi extends ApiAbstractElementClass
633
+ {
634
+ module_prefix = 'test_plan';
635
+
636
+ constructor(options)
637
+ {
638
+ super(options);
639
+ this.set_element_class_instance(typeTestPlan);
640
+ }
641
+ }
642
+ class TestRunApi extends ApiAbstractElementClass
643
+ {
644
+ module_prefix = 'test_run';
645
+
646
+ constructor(options)
647
+ {
648
+ super(options);
649
+ this.set_element_class_instance(typeTestRun);
650
+ }
651
+ }
652
+
653
+ /**
654
+ *
655
+ * @version v.0.1 (24/11/2024)
656
+ */
657
+ class TestRunResultApi extends ApiAbstractElementClass
658
+ {
659
+ module_prefix = 'test_run_result';
660
+
661
+ constructor(options)
662
+ {
663
+ super(options);
664
+ this.set_element_class_instance(typeTestRunResult);
665
+ }
666
+ }
667
+
668
+ /**
669
+ *
670
+ * @version v.0.1 (24/11/2024)
671
+ */
672
+ class TestSuiteApi extends ApiAbstractElementClass
673
+ {
674
+ module_prefix = 'test_suite';
675
+
676
+ constructor(options)
677
+ {
678
+ super(options);
679
+ this.set_element_class_instance(typeTestSuite);
680
+ }
681
+ }
682
+ class ApiException extends Error
683
+ {
684
+ /**
685
+ * Сообщение об ошибке
686
+ * @type {string}
687
+ */
688
+ #message = '';
689
+ /**
690
+ * Нативная ошибка
691
+ * @type {null|Error}
692
+ */
693
+ #error = null;
694
+
695
+
696
+ api_response = null;
697
+ api_code = null;
698
+ api_result = null;
699
+ api_error = null;
700
+
701
+
702
+ constructor(options)
703
+ {
704
+ super();
705
+
706
+ if (options.message)
707
+ {
708
+ this.#message = options.message;
709
+ }
710
+
711
+ if (options.error instanceof Error)
712
+ {
713
+ this.#error = options.error;
714
+ }
715
+
716
+
717
+ if (options?.api_response)
718
+ {
719
+ this.api_response = options?.api_response;
720
+ }
721
+
722
+ if (options?.api_code)
723
+ {
724
+ this.api_code = options?.api_code;
725
+ }
726
+
727
+ if (options?.api_result)
728
+ {
729
+ this.api_result = options?.api_result;
730
+ }
731
+
732
+ if (options?.api_error)
733
+ {
734
+ this.api_error = options?.api_error;
735
+ }
736
+ }
737
+
738
+
739
+ get_message()
740
+ {
741
+ if (this.#error instanceof Error)
742
+ {
743
+ return this.#error.message;
744
+ }
745
+ else
746
+ {
747
+ return this.#message;
748
+ }
749
+ }
750
+ }
751
+ /**
752
+ *
753
+ * @version v.0.1 (25/06/2024)
754
+ */
755
+ class abstractService
756
+ {
757
+ init_options = false;
758
+
759
+
760
+ /**
761
+ *
762
+ * @param init_options
763
+ */
764
+ constructor(init_options = false)
765
+ {
766
+ /*if (this.constructor._instance)
767
+ {
768
+ return this.constructor._instance;
769
+ }
770
+
771
+ this.init_options = init_options;
772
+
773
+ this.constructor._instance = this;*/
774
+ }
775
+
776
+ /*#bootstrap()
777
+ {
778
+
779
+ }*/
780
+ }
781
+ /**
782
+ *
783
+ * @version v.0.1 (25/06/2024)
784
+ */
785
+ class serviceTranslate extends abstractService
786
+ {
787
+ #language = 'en';
788
+ #available_language = [ 'ru', 'en' ];
789
+ #dictionary = false; // может сделать сторадж отдельно и static ?
790
+ #vendor_instance;
791
+
792
+
793
+ /**
794
+ *
795
+ * @version v.0.1 (25/06/2024)
796
+ * @param {?object} init_options
797
+ * @returns {serviceTranslate}
798
+ */
799
+ constructor(init_options = false)
800
+ {
801
+ super(init_options);
802
+
803
+ if (init_options?.language)
804
+ {
805
+ if (in_array(init_options?.language, this.#available_language))
806
+ {
807
+ this.#language = init_options?.language;
808
+ }
809
+ else
810
+ {
811
+ echo({ init_options });
812
+ throw new Error('Unsupported language');
813
+ }
814
+ }
815
+
816
+ if (this.constructor._instance)
817
+ {
818
+ return this.constructor._instance;
819
+ }
820
+
821
+ this.init_options = init_options;
822
+ this.constructor._instance = this;
823
+
824
+ this.#bootstrap();
825
+
826
+ return this.constructor._instance;
827
+ }
828
+
829
+
830
+ /**
831
+ *
832
+ * @version v.0.1 (25/06/2024)
833
+ * @param api_response
834
+ */
835
+ #api_response_parser(api_response)
836
+ {
837
+
838
+ // заготовка
839
+ return api_response;
840
+ }
841
+
842
+
843
+ /**
844
+ *
845
+ * @version v.0.1 (25/06/2024)
846
+ */
847
+ async #bootstrap()
848
+ {
849
+ this.#dictionary = await Api.service.translate_get_dictionaries();
850
+
851
+ let normalize_response = this.#api_response_parser(this.#dictionary);
852
+
853
+
854
+ await i18next.init({
855
+ lng : this.#language,
856
+ debug : false,
857
+ resources: normalize_response
858
+ });
859
+
860
+ this.#vendor_instance = i18next;
861
+ }
862
+
863
+
864
+ /**
865
+ *
866
+ * @version v.0.1 (25/06/2024)
867
+ * @param group
868
+ * @param key
869
+ */
870
+ get(group, key)
871
+ {
872
+ return this.#vendor_instance.t(group + '.' + key);
873
+ }
874
+ }
875
+ /*
876
+ /!**
877
+ *
878
+ * @version v.1.0 (18/11/2024)
879
+ *!/
880
+ class UserService extends abstractService
881
+ {
882
+
883
+ static #current_user_id = '-1';
884
+ static #user_name = '';
885
+ static #current_team_id = '';
886
+ static #user_teams = [ -1 ];
887
+ static #roles = [ 'user' ];
888
+ static #current_language = 'en';
889
+
890
+
891
+ /!**
892
+ *
893
+ *
894
+ * @version v.1.0 (18/11/2024)
895
+ *
896
+ * @param init_options
897
+ * @returns {*}
898
+ *!/
899
+ /!**
900
+ *
901
+ *
902
+ * @version v.1.0 (18/11/2024)
903
+ *
904
+ * @param init_options
905
+ * @returns {*}
906
+ *!/
907
+ constructor(init_options = false)
908
+ {
909
+ super();
910
+ if (this.constructor._instance)
911
+ {
912
+ return this.constructor._instance;
913
+ }
914
+
915
+ this.init_options = init_options;
916
+
917
+ this.constructor._instance = this;
918
+ super();
919
+ }
920
+
921
+ static get_current_user_id()
922
+ {
923
+ return UserService.#current_user_id;
924
+ }
925
+
926
+ static get_user_name()
927
+ {
928
+ return UserService.#user_name;
929
+ }
930
+
931
+ static get_current_team_id()
932
+ {
933
+ return UserService.#current_team_id;
934
+ }
935
+
936
+ static get_user_teams()
937
+ {
938
+ return UserService.#user_teams;
939
+ }
940
+
941
+ static get_roles()
942
+ {
943
+ return UserService.#roles;
944
+ }
945
+
946
+ static get_current_language()
947
+ {
948
+ return UserService.#current_language;
949
+ }
950
+ }*/
951
+
952
+ /**
953
+ *
954
+ * @param {object} object
955
+ * @returns {object}
956
+ */
957
+ function clone_object(object)
958
+ {
959
+ // @todo use deep clone
960
+ // @todo add validation
961
+ return JSON.parse(JSON.stringify(object));
962
+ }
963
+
964
+ /**
965
+ *
966
+ * @param {string|number|array|object} text
967
+ */
968
+ function echo(text)
969
+ {
970
+ console.log(text);
971
+ }
972
+
973
+
974
+ /**
975
+ *
976
+ * @param {string|number|array|object} data
977
+ */
978
+ function echo_table(...data)
979
+ {
980
+ if (is_array(data))
981
+ {
982
+ data.map((row) =>
983
+ {
984
+ console.table(row);
985
+ });
986
+ }
987
+ else
988
+ {
989
+ echo(data);
990
+ }
991
+ }
992
+
993
+ /**
994
+ *
995
+ *
996
+ * @param {*} data
997
+ * @param {boolean} strict_mode
998
+ * @returns {boolean}
999
+ */
1000
+ function is_array(data, strict_mode = false)
1001
+ {
1002
+ if (strict_mode)
1003
+ {
1004
+ if (data instanceof Array)
1005
+ {
1006
+ return true;
1007
+ }
1008
+ else
1009
+ {
1010
+ return false;
1011
+ }
1012
+ }
1013
+ else
1014
+ {
1015
+ if (data instanceof Array && data?.length > 0)
1016
+ {
1017
+ return true;
1018
+ }
1019
+ else
1020
+ {
1021
+ return false;
1022
+ }
1023
+ }
1024
+ }
1025
+
1026
+
1027
+ /**
1028
+ *
1029
+ *
1030
+ * @param {*} value
1031
+ * @returns {boolean}
1032
+ */
1033
+ function is_object(value)
1034
+ {
1035
+ return _.isObject(value);
1036
+ }
1037
+
1038
+
1039
+ /**
1040
+ *
1041
+ *
1042
+ * @param {*} value
1043
+ * @returns {boolean}
1044
+ */
1045
+ function is_string(value)
1046
+ {
1047
+ let response = false;
1048
+ if (typeof value === 'string')
1049
+ {
1050
+ response = true;
1051
+ }
1052
+
1053
+ return response;
1054
+ }
1055
+
1056
+
1057
+ /**
1058
+ * Проверяем на число, не строго (!)
1059
+ * @param {*} value
1060
+ * @returns {boolean}
1061
+ */
1062
+ function is_number(value)
1063
+ {
1064
+ let response = false;
1065
+
1066
+ if (typeof ( value ) === 'number')
1067
+ {
1068
+ response = true;
1069
+ }
1070
+ else if (value === false)
1071
+ {
1072
+ response = false;
1073
+ }
1074
+ else if (typeof ( value ) === "string" && (!isNaN( value )))
1075
+ {
1076
+ response = true;
1077
+ }
1078
+
1079
+ return response
1080
+ }
1081
+
1082
+
1083
+ /**
1084
+ * Возвращает количество элементов массива или false, если передан не массив
1085
+ *
1086
+ * @param {array} array массив
1087
+ * @returns {number|false} количество элементов массива
1088
+ */
1089
+ function count(array)
1090
+ {
1091
+ let response = false;
1092
+ if (array instanceof Array)
1093
+ {
1094
+ response = array?.length;
1095
+ }
1096
+ else
1097
+ {
1098
+ response = false;
1099
+ }
1100
+
1101
+ return response;
1102
+ }
1103
+
1104
+
1105
+ /**
1106
+ *
1107
+ * @param {string|number} value
1108
+ * @param {array} array
1109
+ * @param {boolean} strict
1110
+ * @returns {boolean}
1111
+ */
1112
+ function in_array(value, array, strict = false)
1113
+ {
1114
+ if (!is_array(array))
1115
+ {
1116
+ return false;
1117
+ }
1118
+ let response = false;
1119
+
1120
+ if (strict)
1121
+ {
1122
+ response = array.find(element =>
1123
+ {
1124
+ return element === value;
1125
+ });
1126
+ }
1127
+ else
1128
+ {
1129
+ response = array.find(element =>
1130
+ {
1131
+ return element == value;
1132
+ });
1133
+ }
1134
+
1135
+ return Boolean(response);
1136
+ }
1137
+
1138
+ function cls()
1139
+ {
1140
+ console.clear();
1141
+ }
1142
+ /**
1143
+ * Базовый абстрактный класс для всех типов
1144
+ *
1145
+ * @version v.0.1 (26/05/2024)
1146
+ */
1147
+ class familyGeneralElement
1148
+ {
1149
+ primary_key = false;
1150
+ create_scheme = false;
1151
+ structure_scheme = false;
1152
+ available_enum_values = false;
1153
+
1154
+
1155
+ /***********************************************/
1156
+ /***********************************************/
1157
+
1158
+ /***********************************************/
1159
+
1160
+ /**
1161
+ *
1162
+ * @version v.0.1 (26/05/2024)
1163
+ * @return {string|false}
1164
+ */
1165
+ get_primary_key()
1166
+ {
1167
+ if (this.primary_key)
1168
+ {
1169
+ return this.primary_key;
1170
+ }
1171
+ else
1172
+ {
1173
+ throw new Error('familyGeneralElement has no primary_key. Error #01-01');
1174
+ }
1175
+ }
1176
+
1177
+
1178
+ /**
1179
+ *
1180
+ * @version v.0.1 (27/06/2024)
1181
+ * @returns {array<familyGeneralElement>}
1182
+ * @param {number} count
1183
+ */
1184
+ get_random_demo_data(count)
1185
+ {
1186
+ // @todo validations
1187
+ let response = [];
1188
+ for (let i = 0; i < count; i++)
1189
+ {
1190
+ response.push(this.get_demo_row());
1191
+ }
1192
+ // получаем массив сгенерированных элементов,
1193
+ // в том числе - с незаполненными данными (корректная обработка)
1194
+
1195
+ // контроль пересечения primary key
1196
+ // прототип идеи
1197
+ // @todo - дописать
1198
+ return response;
1199
+ }
1200
+
1201
+ /**
1202
+ *
1203
+ * @returns {familyGeneralElement}
1204
+ */
1205
+ get_demo_row()
1206
+ {
1207
+ return this;
1208
+ }
1209
+
1210
+ /**
1211
+ *
1212
+ * @version v.0.2 (15/06/2024)
1213
+ * @param {string} attribute
1214
+ */
1215
+ get_available_enum_values(attribute)
1216
+ {
1217
+ let response = false;
1218
+ if (this.structure_scheme?.[ attribute ])
1219
+ {
1220
+ // выбрасывать исключения?
1221
+ return false;
1222
+ }
1223
+
1224
+ let rules = this.get_attribute_structure_scheme(attribute);
1225
+ if (!rules)
1226
+ {
1227
+ return false;
1228
+ }
1229
+ if (!in_array('enum', rules))
1230
+ {
1231
+ return false;
1232
+ }
1233
+
1234
+ if (this.available_enum_values?.[ attribute ])
1235
+ {
1236
+ response = this.available_enum_values?.[ attribute ];
1237
+ }
1238
+
1239
+ return response;
1240
+ }
1241
+
1242
+
1243
+ /**
1244
+ *
1245
+ * @param attribute
1246
+ * @returns {array|false}
1247
+ */
1248
+ get_attribute_structure_scheme(attribute)
1249
+ {
1250
+ let response = false;
1251
+ if (this.structure_scheme[ attribute ])
1252
+ {
1253
+ let scheme = clone_object(this.structure_scheme[ attribute ]);
1254
+
1255
+ if (scheme)
1256
+ {
1257
+ scheme = scheme.split('|');
1258
+ if (is_array(scheme))
1259
+ {
1260
+ response = [];
1261
+ scheme.map((row) =>
1262
+ {
1263
+ response.push(row.trim());
1264
+ });
1265
+ }
1266
+ }
1267
+ }
1268
+
1269
+ return response;
1270
+ }
1271
+
1272
+
1273
+ /**
1274
+ *
1275
+ * @version v.0.1 (26/05/2024)
1276
+ */
1277
+ get_data()
1278
+ {
1279
+ return this.get_pure_data();
1280
+ }
1281
+
1282
+
1283
+ /**
1284
+ *
1285
+ * @version v.0.1 (26/05/2024)
1286
+ */
1287
+ get_pure_data()
1288
+ {
1289
+ let response = false;
1290
+ if (this.structure_scheme)
1291
+ {
1292
+ response = {};
1293
+ _.mapObject(this.structure_scheme, (value, attribute) =>
1294
+ {
1295
+ if (this[ attribute ])
1296
+ {
1297
+ response [ attribute ] = this[ attribute ];
1298
+ }
1299
+ });
1300
+ }
1301
+
1302
+ return response;
1303
+ }
1304
+
1305
+
1306
+ /**
1307
+ * @todo - сделать нормализация по create scheme с валидацией
1308
+ *
1309
+ * @version v.0.1 (26/01/2025)
1310
+ *
1311
+ * @param payload
1312
+ * @returns {*}
1313
+ */
1314
+ normalize_payload(payload)
1315
+ {
1316
+
1317
+ return payload;
1318
+ }
1319
+
1320
+
1321
+ get_editable_attributes() {}
1322
+
1323
+ is_editable_attribute(attribute) {}
1324
+
1325
+ /**
1326
+ *
1327
+ * @version v.0.1 (26/05/2024)
1328
+ * @param {string} attribute_name
1329
+ * @return {string}
1330
+ */
1331
+ get_attribute_name_translate(attribute_name)
1332
+ {
1333
+ return attribute_name;
1334
+ }
1335
+
1336
+
1337
+ /***********************************************/
1338
+ /***********************************************/
1339
+
1340
+ /***********************************************/
1341
+
1342
+ /**
1343
+ *
1344
+ * @version v.0.1 (26/05/2024)
1345
+ * @param id
1346
+ */
1347
+ async find(id = false)
1348
+ {
1349
+ // или передаем ID, или используем вложенный
1350
+ // возвращаем инстанс себя же
1351
+ alert('Error #267-001 Not implemented method');
1352
+ }
1353
+
1354
+ /**
1355
+ *
1356
+ * @version v.0.1 (26/05/2024)
1357
+ */
1358
+ async create()
1359
+ {}
1360
+
1361
+
1362
+ /**
1363
+ *
1364
+ * @version v.0.1 (26/05/2024)
1365
+ */
1366
+ async update()
1367
+ {
1368
+ alert('Error #272-001 Not implemented method');
1369
+ }
1370
+
1371
+
1372
+ /**
1373
+ *
1374
+ * @version v.0.1 (26/05/2024)
1375
+ */
1376
+ async update_property(key, value, callback)
1377
+ {
1378
+ alert('Error #272-001 Not implemented method');
1379
+ }
1380
+
1381
+
1382
+ /**
1383
+ *
1384
+ * @version v.0.1 (26/05/2024)
1385
+ */
1386
+ async update_properties()
1387
+ {
1388
+ alert('Error #272-001 Not implemented method');
1389
+ }
1390
+
1391
+
1392
+ /**
1393
+ *
1394
+ * @version v.0.1 (26/05/2024)
1395
+ * @param element
1396
+ * @param callback
1397
+ * @returns {Promise<boolean>}
1398
+ */
1399
+ async delete(element, callback)
1400
+ {
1401
+ alert('Error #267-001 Not implemented method');
1402
+ return false;
1403
+ }
1404
+ }
1405
+ /**
1406
+ * Заготовка - логи действий
1407
+ *
1408
+ * @version v.0.1 (15/10/2024)
1409
+ */
1410
+ class typeActionLog
1411
+ {
1412
+
1413
+ }
1414
+
1415
+ /**
1416
+ *
1417
+ * @version v.0.1 (23/06/2024)
1418
+ */
1419
+ class typeFunctional extends familyGeneralElement
1420
+ {
1421
+ functional_id;
1422
+ project_id; // foreign key
1423
+ functional_group_id; // foreign key
1424
+ team_id; // foreign key
1425
+ functional_name;
1426
+ functional_description;
1427
+ reference_document_ids; // IDS документов по функционалу (к примеру confluence)
1428
+ created_at;
1429
+ updated_at;
1430
+
1431
+ primary_key = 'functional_id';
1432
+
1433
+ structure_scheme = {
1434
+ functional_id : 'integer | require',
1435
+ project_id : 'integer | require',
1436
+ functional_group_id : 'integer | require',
1437
+ team_id : 'integer | require',
1438
+ functional_name : 'string | max:90 | require',
1439
+ functional_description: 'string | max:2500 | optional',
1440
+ reference_document_ids: 'array | reference | optional',
1441
+ created_at : 'timestamp | require',
1442
+ updated_at : 'timestamp | optional',
1443
+ };
1444
+
1445
+ available_enum_values = {
1446
+ functional_status: [ 'не идей. нужен ли тут статус' ]
1447
+ };
1448
+
1449
+
1450
+ /**
1451
+ *
1452
+ * @param {object|false|undefined} data
1453
+ */
1454
+ constructor(data = false)
1455
+ {
1456
+ super();
1457
+
1458
+ if (data && is_object(data))
1459
+ {
1460
+ _.mapObject(data, (value, key) =>
1461
+ {
1462
+ if (this.hasOwnProperty(key))
1463
+ {
1464
+ this [ key ] = value;
1465
+ }
1466
+ });
1467
+ }
1468
+
1469
+ return this;
1470
+ }
1471
+
1472
+
1473
+ /******************************************************************************************/
1474
+ /*********************************** general **********************************************/
1475
+
1476
+ /******************************************************************************************/
1477
+
1478
+ /**
1479
+ *
1480
+ * @version v.0.1 (07/07/2024)
1481
+ */
1482
+ async create()
1483
+ {}
1484
+
1485
+
1486
+ /**
1487
+ *
1488
+ * @version v.0.1 (07/07/2024)
1489
+ */
1490
+ async update()
1491
+ {
1492
+
1493
+ }
1494
+
1495
+
1496
+ /**
1497
+ *
1498
+ * @version v.0.1 (07/07/2024)
1499
+ */
1500
+ async delete()
1501
+ {
1502
+
1503
+ }
1504
+
1505
+
1506
+ }
1507
+
1508
+ /**
1509
+ * @version v.0.2 (25/01/2025)
1510
+ */
1511
+ class typeFunctionalGroup extends familyGeneralElement
1512
+ {
1513
+ functional_group_id;
1514
+ project_id; // foreign key
1515
+ team_id; // foreign key
1516
+ parent_id; // для организации вложенности
1517
+ functional_group_name;
1518
+ functional_group_description;
1519
+ //functional_group_status; // enum (active + archive ??)
1520
+ created_at;
1521
+ updated_at;
1522
+
1523
+ primary_key = 'functional_group_id';
1524
+
1525
+ structure_scheme = {
1526
+ functional_group_id : 'integer | require',
1527
+ functional_group_name : 'string | max:90 | require',
1528
+ functional_group_description: 'string | max:250 | optional',
1529
+ project_id : 'integer | require',
1530
+ team_id : 'integer | require',
1531
+ parent_id : 'integer | optional',
1532
+ functional_group_status : 'integer | enum | require',
1533
+ created_at : 'timestamp | require',
1534
+ updated_at : 'timestamp | optional',
1535
+ };
1536
+
1537
+ available_enum_values = {
1538
+ functional_group_status: [ 'не идей. нужен ли тут статус' ]
1539
+ };
1540
+
1541
+
1542
+ /**
1543
+ *
1544
+ * @param {object|false|undefined} data
1545
+ */
1546
+ constructor(data = false)
1547
+ {
1548
+ super();
1549
+
1550
+ if (data && is_object(data))
1551
+ {
1552
+ _.mapObject(data, (value, key) =>
1553
+ {
1554
+ if (this.hasOwnProperty(key))
1555
+ {
1556
+ this [ key ] = value;
1557
+ }
1558
+ });
1559
+ }
1560
+
1561
+ return this;
1562
+ }
1563
+
1564
+
1565
+ /******************************************************************************************/
1566
+ /*********************************** general **********************************************/
1567
+
1568
+ /******************************************************************************************/
1569
+
1570
+ /**
1571
+ *
1572
+ * @version v.0.1 (07/07/2024)
1573
+ */
1574
+ async create()
1575
+ {}
1576
+
1577
+
1578
+ /**
1579
+ *
1580
+ * @version v.0.1 (07/07/2024)
1581
+ */
1582
+ async update()
1583
+ {
1584
+
1585
+ }
1586
+
1587
+
1588
+ /**
1589
+ *
1590
+ * @version v.0.1 (07/07/2024)
1591
+ */
1592
+ async delete()
1593
+ {
1594
+
1595
+ }
1596
+
1597
+
1598
+ }
1599
+ /**
1600
+ *
1601
+ * @version v.0.2 (15/06/2024)
1602
+ */
1603
+ class typeMilestone extends familyGeneralElement
1604
+ {
1605
+ milestone_id; // primary AI
1606
+ milestone_name;
1607
+ milestone_description;
1608
+ milestone_status; // enum
1609
+ milestone_start_date;
1610
+ milestone_end_date;
1611
+ team_id; // foreign key
1612
+ project_id; // foreign key
1613
+
1614
+
1615
+ primary_key = 'milestone_id';
1616
+
1617
+ structure_scheme = {
1618
+ milestone_id : 'integer | require',
1619
+ milestone_name : 'string | require',
1620
+ milestone_description: 'string | optional',
1621
+ milestone_status : 'string | enum | require',
1622
+ company_id : 'integer | require',
1623
+ milestone_start_date : 'date | require',
1624
+ milestone_end_date : 'date | optional',
1625
+ project_id : 'integer | require',
1626
+ };
1627
+
1628
+ available_enum_values = {
1629
+ milestone_status: [ 'ACTUAL', 'COMPLETED', 'PLANNED' ]
1630
+ };
1631
+
1632
+ /**
1633
+ *
1634
+ * @param {object|false|undefined} data
1635
+ */
1636
+ constructor(data = false)
1637
+ {
1638
+ super();
1639
+
1640
+ if (data && is_object(data))
1641
+ {
1642
+ _.mapObject(data, (value, key) =>
1643
+ {
1644
+ if (this.hasOwnProperty(key))
1645
+ {
1646
+ this [ key ] = value;
1647
+ }
1648
+ });
1649
+ }
1650
+
1651
+ return this;
1652
+ }
1653
+
1654
+
1655
+ /******************************************************************************************/
1656
+ /*********************************** general **********************************************/
1657
+
1658
+ /******************************************************************************************/
1659
+
1660
+ /**
1661
+ *
1662
+ * @version v.0.1 (07/07/2024)
1663
+ */
1664
+ async create()
1665
+ {}
1666
+
1667
+
1668
+ /**
1669
+ *
1670
+ * @version v.0.1 (07/07/2024)
1671
+ */
1672
+ async update()
1673
+ {
1674
+
1675
+ }
1676
+
1677
+
1678
+ /**
1679
+ *
1680
+ * @version v.0.1 (07/07/2024)
1681
+ */
1682
+ async delete()
1683
+ {
1684
+
1685
+ }
1686
+
1687
+
1688
+ }
1689
+
1690
+ /**
1691
+ *
1692
+ * @version v.0.2 (15/06/2024)
1693
+ */
1694
+ class typeProject extends familyGeneralElement
1695
+ {
1696
+ project_id; // primary
1697
+ project_name;
1698
+ project_description; // some comment
1699
+ project_status; // enum
1700
+ team_id; // foreign key + owner
1701
+ reference_document_ids; // IDS документов по функционалу (к примеру confluence)
1702
+ // хосты/домены будут отдельно храниться, их может быть множество
1703
+
1704
+ primary_key = 'project_id';
1705
+
1706
+ structure_scheme = {
1707
+ project_id : 'integer | require',
1708
+ project_name : 'string | require',
1709
+ project_description : 'string | optional',
1710
+ project_status : 'string | enum | require',
1711
+ company_id : 'integer | require',
1712
+ reference_document_ids: 'array | reference | optional',
1713
+ };
1714
+
1715
+ available_enum_values = {
1716
+ project_status: [ 'OPEN', 'CLOSED', 'PLANNED' ]
1717
+ };
1718
+
1719
+
1720
+ /**
1721
+ *
1722
+ * @param {object|false|undefined} data
1723
+ */
1724
+ constructor(data = false)
1725
+ {
1726
+ super();
1727
+
1728
+ if (data && is_object(data))
1729
+ {
1730
+ _.mapObject(data, (value, key) =>
1731
+ {
1732
+ if (this.hasOwnProperty(key))
1733
+ {
1734
+ this [ key ] = value;
1735
+ }
1736
+ });
1737
+ }
1738
+
1739
+ return this;
1740
+ }
1741
+
1742
+
1743
+ /**
1744
+ *
1745
+ * @version v.0.1 (02/07/2024)
1746
+ * @returns {array<typeProject>}
1747
+ * @param {number} count
1748
+ */
1749
+ get_random_demo_data(count)
1750
+ {
1751
+ // @todo validations
1752
+ let response = [];
1753
+
1754
+ for (let i = 0; i < count; i++)
1755
+ {
1756
+ response.push(new MockDataTypeProject());
1757
+ }
1758
+
1759
+
1760
+ return response;
1761
+ }
1762
+
1763
+
1764
+ /******************************************************************************************/
1765
+ /*********************************** general **********************************************/
1766
+ /******************************************************************************************/
1767
+
1768
+ /**
1769
+ *
1770
+ * @param {familyGeneralElement|object} payload
1771
+ * @param {typeFunctionCallback|object|false} callback
1772
+ *
1773
+ * @param {?function} callback.before
1774
+ * @param {?function} callback.success
1775
+ * @param {?function} callback.error
1776
+ * @param {?function} callback.final
1777
+ *
1778
+ * @version v.0.1 (26/01/2025)
1779
+ */
1780
+ async create(payload, callback)
1781
+ {
1782
+ if (typeof callback?.before === 'function')
1783
+ {
1784
+ callback?.before({ payload });
1785
+ }
1786
+
1787
+ payload = this.normalize_payload(payload);
1788
+
1789
+
1790
+ /**
1791
+ * {Api} ApiService
1792
+ */
1793
+ ApiService.project.create(payload).then((response) =>
1794
+ {
1795
+ if (typeof callback?.success === 'function')
1796
+ {
1797
+ callback?.success({ response, payload })
1798
+ }
1799
+ }).catch((error) =>
1800
+ {
1801
+ echo({ error });
1802
+ if (typeof callback?.error === 'function')
1803
+ {
1804
+ callback?.error({ error, payload })
1805
+ }
1806
+ }).finally((response) =>
1807
+ {
1808
+ if (typeof callback?.final === 'function')
1809
+ {
1810
+ callback?.final({ response, payload })
1811
+ }
1812
+ });
1813
+ }
1814
+
1815
+
1816
+ /**
1817
+ *
1818
+ * @version v.0.1 (07/07/2024)
1819
+ */
1820
+ async update()
1821
+ {
1822
+
1823
+ }
1824
+
1825
+
1826
+ /**
1827
+ *
1828
+ * @version v.0.1 (07/07/2024)
1829
+ */
1830
+ async delete()
1831
+ {
1832
+
1833
+ }
1834
+
1835
+
1836
+ /******************************************************************************************/
1837
+ /**************************** работа с участниками проекта ********************************/
1838
+ /******************************************************************************************/
1839
+
1840
+ // member = user + role
1841
+
1842
+ member_add() {}
1843
+
1844
+ member_update() {}
1845
+
1846
+ member_delete() {}
1847
+
1848
+ member_get() {}
1849
+
1850
+
1851
+ }
1852
+ /**
1853
+ * (идея) - у проекта должна быть тестовая документация
1854
+ *
1855
+ * @see https://vladislaveremeev.gitbook.io/qa_bible/testovaya-dokumentaciya-i-artefakty-test-deliverablestest-artifacts/vidy-testovoi-dokumentacii
1856
+ *
1857
+ * @version v.0.2 (07/07/2024)
1858
+ */
1859
+ class typeProjectDocument extends familyGeneralElement
1860
+ {
1861
+ project_document_id;
1862
+ project_id;
1863
+ project_document_type;
1864
+ // где и как храним файлы?
1865
+ // nextcloud integration?
1866
+
1867
+
1868
+
1869
+ available_enum_values = {
1870
+ project_document_type: [ 'QUALITY_POLICY', 'TEST_POLICY', 'PROJECT_DESCRIPTION', 'TEST_PLAN', 'DOCUMENT' ],
1871
+ };
1872
+
1873
+
1874
+ /**
1875
+ *
1876
+ * @param {object|false|undefined} data
1877
+ */
1878
+ constructor(data = false)
1879
+ {
1880
+ super();
1881
+
1882
+ if (data && is_object(data))
1883
+ {
1884
+ _.mapObject(data, (value, key) =>
1885
+ {
1886
+ if (this.hasOwnProperty(key))
1887
+ {
1888
+ this [ key ] = value;
1889
+ }
1890
+ });
1891
+ }
1892
+
1893
+ return this;
1894
+ }
1895
+
1896
+
1897
+ /******************************************************************************************/
1898
+ /*********************************** general **********************************************/
1899
+
1900
+ /******************************************************************************************/
1901
+
1902
+ /**
1903
+ *
1904
+ * @version v.0.1 (07/07/2024)
1905
+ */
1906
+ async create()
1907
+ {}
1908
+
1909
+
1910
+ /**
1911
+ *
1912
+ * @version v.0.1 (07/07/2024)
1913
+ */
1914
+ async update()
1915
+ {
1916
+
1917
+ }
1918
+
1919
+
1920
+ /**
1921
+ *
1922
+ * @version v.0.1 (07/07/2024)
1923
+ */
1924
+ async delete()
1925
+ {
1926
+
1927
+ }
1928
+ }
1929
+ /**
1930
+ * Тестовые аккаунты
1931
+ *
1932
+ * У каждого проекта свои коллекции
1933
+ * (идея)
1934
+ *
1935
+ * @version v.0.1 (30/06/2024)
1936
+ */
1937
+ class typeProjectTestAccount extends familyGeneralElement
1938
+ {
1939
+ test_account_id;
1940
+ project_id;
1941
+ site;
1942
+ authentication_type;
1943
+ login;
1944
+ password; // как будем хранить?
1945
+ comment;
1946
+
1947
+ primary_key = 'test_account_id';
1948
+
1949
+ structure_scheme = {
1950
+ test_account_id : 'integer | require',
1951
+ project_id : 'integer | require',
1952
+ authentication_type: 'enum | require',
1953
+ site : 'string | require',
1954
+ login : 'string | require',
1955
+ password : 'string | optional',
1956
+ comment : 'string | optional',
1957
+ };
1958
+
1959
+ available_enum_values = {
1960
+ authentication_type: [ 'FORMS' ] // другие пока нет автоматизации не особо нужны
1961
+ };
1962
+
1963
+ /**
1964
+ *
1965
+ * @param {object|false|undefined} data
1966
+ */
1967
+ constructor(data = false)
1968
+ {
1969
+ super();
1970
+
1971
+ if (data && is_object(data))
1972
+ {
1973
+ _.mapObject(data, (value, key) =>
1974
+ {
1975
+ if (this.hasOwnProperty(key))
1976
+ {
1977
+ this [ key ] = value;
1978
+ }
1979
+ });
1980
+ }
1981
+
1982
+ return this;
1983
+ }
1984
+ }
1985
+ /**
1986
+ * Тестовые данные
1987
+ * Какая-то коллекция, созданная для тестирования и ввода
1988
+ *
1989
+ * У каждого проекта свои коллекции
1990
+ *
1991
+ * (идея)
1992
+ * @version v.0.1 (30/06/2024)
1993
+ */
1994
+ class typeProjectTestData extends familyGeneralElement
1995
+ {
1996
+ // как храним? json или какие-то структуры? [key->value] ?
1997
+ test_data_id;
1998
+ project_id;
1999
+
2000
+
2001
+ primary_key = 'test_data_id';
2002
+
2003
+ /**
2004
+ *
2005
+ * @param {object|false|undefined} data
2006
+ */
2007
+ constructor(data = false)
2008
+ {
2009
+ super();
2010
+
2011
+ if (data && is_object(data))
2012
+ {
2013
+ _.mapObject(data, (value, key) =>
2014
+ {
2015
+ if (this.hasOwnProperty(key))
2016
+ {
2017
+ this [ key ] = value;
2018
+ }
2019
+ });
2020
+ }
2021
+
2022
+ return this;
2023
+ }
2024
+ }
2025
+ /**
2026
+ * Роли в проекте
2027
+ * @version v.0.1 (07/07/2024)
2028
+ */
2029
+ class typeProjectUserRole extends familyGeneralElement
2030
+ {
2031
+ // строго определенный список
2032
+ // без возможности создания иных
2033
+ // если роль не указана - то ADMINISTRATOR
2034
+
2035
+ role_slug;
2036
+ role_description;
2037
+ project_id;
2038
+ user_id;
2039
+
2040
+ primary_key = 'role_slug';
2041
+
2042
+ structure_scheme = {
2043
+ role_slug : 'string | require',
2044
+ role_description: 'string | optional',
2045
+ project_id : 'integer | require',
2046
+ user_id : 'integer | require',
2047
+ };
2048
+
2049
+ available_enum_values = {
2050
+ role_slug: [ 'ROOT', 'ADMINISTRATOR', 'LEAD', 'QA_ENGINEER' ]
2051
+ // ?? Системные администраторы, Разработчики, Тестировщики
2052
+ };
2053
+
2054
+
2055
+ /**
2056
+ *
2057
+ * @param {object|false|undefined} data
2058
+ */
2059
+ constructor(data = false)
2060
+ {
2061
+ super();
2062
+
2063
+ if (data && is_object(data))
2064
+ {
2065
+ _.mapObject(data, (value, key) =>
2066
+ {
2067
+ if (this.hasOwnProperty(key))
2068
+ {
2069
+ this [ key ] = value;
2070
+ }
2071
+ });
2072
+ }
2073
+
2074
+ return this;
2075
+ }
2076
+ }
2077
+
2078
+ /**
2079
+ * @version v.2.0 (07/07/2024)
2080
+ */
2081
+ class typeTask extends familyGeneralElement
2082
+ {
2083
+ task_id;
2084
+ team_id; // foreign key
2085
+ task_performer_id; // foreign key // @todo переименовать с общепринятой практикой
2086
+ task_initiator_id; // foreign key // @todo переименовать с общепринятой практикой
2087
+ task_type; // enum => сделать тест, выполнить тест, итд (!не дублирует предмет задания - тут тип + действие)
2088
+ task_status; // enum
2089
+ project_id; // foreign key (денормализация) (а надо ли)
2090
+ task_subject_element_type; // предмет задания
2091
+ task_subject_id; // ID предмета задания (изначально его может не быть)
2092
+
2093
+
2094
+ primary_key = 'task_id';
2095
+
2096
+ structure_scheme = {
2097
+ task_id : 'integer | require',
2098
+ team_id : 'integer | require',
2099
+ task_performer_id : 'integer | require',
2100
+ task_initiator_id : 'integer | require',
2101
+ task_type : 'string | enum | require',
2102
+ task_status : 'string | enum | require',
2103
+ project_id : 'integer | require',
2104
+ task_subject_element_type: 'string | enum | require',
2105
+ task_subject_id : 'integer | optional',
2106
+ };
2107
+
2108
+ available_enum_values = {
2109
+ task_status: [ 'OPEN', 'IN_PROGRESS', 'DONE', 'CANCELLED' ],
2110
+ // тут будет много, все надо перечислить, чтобы просто по каждой описать свой код
2111
+ // и не все комбинации action + element доступны и могут существовать
2112
+ task_type : [
2113
+ 'CREATE_PROJECT_DOCUMENT',
2114
+ 'CREATE_FUNCTIONAL',
2115
+ 'CREATE_TESTCASE',
2116
+ 'CREATE_TEST_RUN',
2117
+ 'RUN_TEST_RUN'
2118
+ ],
2119
+ task_subject_element_type: [
2120
+ 'PROJECT',
2121
+ 'PROJECT_DOCUMENT',
2122
+ 'TEST_SUITE',
2123
+ 'TEST_CASE',
2124
+ 'TEST_RUN',
2125
+ 'TEST_RESULT',
2126
+ ],
2127
+ };
2128
+
2129
+
2130
+ /**
2131
+ *
2132
+ * @param {object|false|undefined} data
2133
+ */
2134
+ constructor(data = false)
2135
+ {
2136
+ super();
2137
+
2138
+ if (data && is_object(data))
2139
+ {
2140
+ _.mapObject(data, (value, key) =>
2141
+ {
2142
+ if (this.hasOwnProperty(key))
2143
+ {
2144
+ this [ key ] = value;
2145
+ }
2146
+ });
2147
+ }
2148
+
2149
+ return this;
2150
+ }
2151
+ }
2152
+
2153
+ /**
2154
+ * @version v.0.1 (23/06/2024)
2155
+ */
2156
+ class typeTeam extends familyGeneralElement
2157
+ {
2158
+ team_id; //
2159
+ team_status; // enum
2160
+ team_type; // enum
2161
+ team_country_code; // (закон о перс данных и иные) как минимум статистика
2162
+ team_name;
2163
+ _team_price_id // foreign key (тариф)
2164
+ team_root_user_id; // foreign key
2165
+ created_at;
2166
+ updated_at;
2167
+ // тут где-то могут быть тарифы
2168
+
2169
+
2170
+ primary_key = 'team_id';
2171
+
2172
+ structure_scheme = {
2173
+ team_id : 'integer | require',
2174
+ team_status : 'string | enum | require',
2175
+ team_type : 'string | enum | require',
2176
+ team_country_code: 'string | country_code | require',
2177
+ team_name : 'string | require',
2178
+ team_price_id : 'integer | require',
2179
+ team_root_user_id: 'integer | require',
2180
+ created_at : 'timestamp | require',
2181
+ updated_at : 'timestamp | optional',
2182
+ };
2183
+
2184
+
2185
+ available_enum_values = {
2186
+ team_status: [ 'PENDING', 'APPROVED', 'BANNED', 'FROZEN', 'TRIAL' ],
2187
+ team_type : [ 'PERSONAL', 'ORGANIZATION', 'SCHOOL' ],
2188
+ };
2189
+
2190
+
2191
+ /**
2192
+ *
2193
+ * @param {object|false|undefined} data
2194
+ */
2195
+ constructor(data = false)
2196
+ {
2197
+ super();
2198
+
2199
+ if (data && is_object(data))
2200
+ {
2201
+ _.mapObject(data, (value, key) =>
2202
+ {
2203
+ if (this.hasOwnProperty(key))
2204
+ {
2205
+ this [ key ] = value;
2206
+ }
2207
+ });
2208
+ }
2209
+
2210
+ return this;
2211
+ }
2212
+
2213
+
2214
+ /******************************************************************************************/
2215
+ /*********************************** general **********************************************/
2216
+
2217
+ /******************************************************************************************/
2218
+
2219
+ /**
2220
+ *
2221
+ * @version v.0.1 (07/07/2024)
2222
+ */
2223
+ async create()
2224
+ {}
2225
+
2226
+
2227
+ /******************************************************************************************/
2228
+ /*********************** работа с администраторами компании *******************************/
2229
+
2230
+ /******************************************************************************************/
2231
+
2232
+ adminstator_add() {}
2233
+
2234
+ adminstator_update() {}
2235
+
2236
+ adminstator_delete() {}
2237
+
2238
+ adminstators_get() {}
2239
+
2240
+
2241
+ /******************************************************************************************/
2242
+ /************************* работа с сотрудниками компании *********************************/
2243
+
2244
+ /******************************************************************************************/
2245
+
2246
+ user_add() {}
2247
+
2248
+ user_update() {}
2249
+
2250
+ user_delete() {}
2251
+
2252
+ users_get() {}
2253
+ }
2254
+
2255
+ /**
2256
+ * @version v.0.2 (11/11/2024)
2257
+ */
2258
+ class typeTeamMember extends familyGeneralElement
2259
+ {
2260
+ team_member_id;
2261
+ team_id;
2262
+ user_id;
2263
+ team_member_status;
2264
+ member_role;
2265
+ created_at;
2266
+ updated_at;
2267
+
2268
+
2269
+ primary_key = 'team_member_id';
2270
+
2271
+ structure_scheme = {
2272
+ team_member_id : 'integer | require',
2273
+ team_id : 'integer | require',
2274
+ user_id : 'integer | require',
2275
+ team_member_status: 'string | enum | require',
2276
+ member_role: 'string | enum | require',
2277
+ created_at : 'timestamp | require',
2278
+ updated_at : 'timestamp | optional',
2279
+ };
2280
+
2281
+ available_enum_values = {
2282
+ team_member_status: [ 'PENDING', 'ACCEPTED', 'BANNED', 'REJECTED' ],
2283
+ member_role : [ 'ROOT', 'ADMINISTRATOR', 'MEMBER' ]
2284
+ };
2285
+
2286
+
2287
+ /**
2288
+ *
2289
+ * @param {object|false|undefined} data
2290
+ */
2291
+ constructor(data = false)
2292
+ {
2293
+ super();
2294
+
2295
+ if (data && is_object(data))
2296
+ {
2297
+ _.mapObject(data, (value, key) =>
2298
+ {
2299
+ if (this.hasOwnProperty(key))
2300
+ {
2301
+ this [ key ] = value;
2302
+ }
2303
+ });
2304
+ }
2305
+
2306
+ return this;
2307
+ }
2308
+ }
2309
+ /**
2310
+ * Роль пользователя в команде
2311
+ * Должно стать часть team management
2312
+ *
2313
+ * Один пользователь может иметь много ролей (разработчик, тестировщик, ...)
2314
+ *
2315
+ * @version v.0.1 (07/07/2024)
2316
+ */
2317
+ class typeTeamUserRole extends familyGeneralElement
2318
+ {
2319
+ // @todo - все не актуально, поменять
2320
+ role_slug;
2321
+ role_description;
2322
+ team_id;
2323
+ user_id;
2324
+
2325
+ primary_key = 'role_slug';
2326
+
2327
+ structure_scheme = {
2328
+ role_slug : 'string | require',
2329
+ role_description: 'string | optional',
2330
+ team_id : 'integer | require',
2331
+ user_id : 'integer | require',
2332
+ };
2333
+
2334
+ /*available_enum_values = {
2335
+ // эти роли вынесены
2336
+ role_slug: [ 'ROOT', 'ADMINISTRATOR', 'MEMBER' ]
2337
+ };
2338
+ */
2339
+
2340
+ /**
2341
+ *
2342
+ * @param {object|false|undefined} data
2343
+ */
2344
+ constructor(data = false)
2345
+ {
2346
+ super();
2347
+
2348
+ if (data && is_object(data))
2349
+ {
2350
+ _.mapObject(data, (value, key) =>
2351
+ {
2352
+ if (this.hasOwnProperty(key))
2353
+ {
2354
+ this [ key ] = value;
2355
+ }
2356
+ });
2357
+ }
2358
+
2359
+ return this;
2360
+ }
2361
+ }
2362
+
2363
+ /**
2364
+ *
2365
+ * @version v.0.2 (10/12/2024)
2366
+ */
2367
+ class typeTestCase extends familyGeneralElement
2368
+ {
2369
+ test_case_id;
2370
+ test_suite_id; // foreign key
2371
+ project_id; // foreign key
2372
+ team_id; // foreign key // secure - access
2373
+ functional_id; // foreign key
2374
+ test_case_title; // text 90
2375
+ test_case_preconditions; // text (взято у рельсов)
2376
+ test_case_mission; // text (взято у рельсов)
2377
+ test_case_goals; // text (взято у рельсов)
2378
+ test_case_description; // text 250
2379
+ test_case_time_to_execute; // время выполнения (в секундах) (оценочное) (для планирования работ)
2380
+ test_case_complexity; // enum (для планирования работ)
2381
+ test_case_importance; // enum (для планирования работ)
2382
+ test_case_steps; // pseudo // foreign key - relations
2383
+ test_case_presteps; // pseudo // foreign key - relations
2384
+ _attachments; // typeAttachments - <relations>
2385
+ preconditions; // text
2386
+ excepted_result; // text // ?? <goals>
2387
+ created_at;
2388
+ updated_at;
2389
+
2390
+
2391
+ primary_key = 'test_case_id';
2392
+
2393
+ // @todo - довнести
2394
+ structure_scheme = {
2395
+ test_case_id : 'integer | require',
2396
+ test_suite_id : 'integer | require',
2397
+ project_id : 'integer | require',
2398
+ team_id : 'integer | require',
2399
+ functional_id : 'integer | optional',
2400
+ test_case_title : 'string | require',
2401
+ test_case_descriptions : 'string | require',
2402
+ test_case_time_to_execute: 'integer | optional',
2403
+ test_case_complexity : 'string | enum | optional',
2404
+ test_case_importance : 'string | enum | optional',
2405
+ created_at : 'timestamp | require',
2406
+ updated_at : 'timestamp | optional',
2407
+ };
2408
+
2409
+ available_enum_values = {
2410
+ test_case_complexity: [ 'LOW', 'NORMAL', 'HIGH' ],
2411
+ test_case_importance: [ 'LOW', 'NORMAL', 'HIGH' ],
2412
+ };
2413
+
2414
+
2415
+ /**
2416
+ *
2417
+ * @param {object|false|undefined} data
2418
+ */
2419
+ constructor(data = false)
2420
+ {
2421
+ super();
2422
+
2423
+ if (data && is_object(data))
2424
+ {
2425
+ _.mapObject(data, (value, key) =>
2426
+ {
2427
+ if (this.hasOwnProperty(key))
2428
+ {
2429
+ this [ key ] = value;
2430
+ }
2431
+ });
2432
+ }
2433
+
2434
+ return this;
2435
+ }
2436
+
2437
+
2438
+ /******************************************************************************************/
2439
+ /*********************************** general **********************************************/
2440
+
2441
+ /******************************************************************************************/
2442
+
2443
+ /**
2444
+ *
2445
+ * @version v.0.1 (07/07/2024)
2446
+ */
2447
+ async create()
2448
+ {}
2449
+
2450
+
2451
+ /**
2452
+ *
2453
+ * @version v.0.1 (07/07/2024)
2454
+ */
2455
+ async update()
2456
+ {
2457
+
2458
+ }
2459
+
2460
+
2461
+ /**
2462
+ *
2463
+ * @version v.0.1 (07/07/2024)
2464
+ */
2465
+ async delete()
2466
+ {
2467
+
2468
+ }
2469
+
2470
+
2471
+ }
2472
+
2473
+ /**
2474
+ *
2475
+ * @version v.0.1 (26/05/2024)
2476
+ */
2477
+ class typeTestCaseStep extends familyGeneralElement
2478
+ {
2479
+ test_case_step_id;
2480
+ test_case_step_type;
2481
+ team_id; // foreign key
2482
+ test_case_id; // relations
2483
+ step_text;
2484
+
2485
+ primary_key = 'test_case_step_id';
2486
+
2487
+ available_enum_values = {
2488
+ test_case_step_type: [ 'TEST_CASE_STEP', 'TEST_CASE_PRESTEP' ],
2489
+ };
2490
+ }
2491
+
2492
+ /**
2493
+ * Нужен ли этот тип?
2494
+ *
2495
+ * @version v.0.1 (26/05/2024)
2496
+ */
2497
+ class typeTestPlan extends familyGeneralElement
2498
+ {
2499
+
2500
+
2501
+ /**
2502
+ *
2503
+ * @param {object|false|undefined} data
2504
+ */
2505
+ constructor(data = false)
2506
+ {
2507
+ super();
2508
+
2509
+ if (data && is_object(data))
2510
+ {
2511
+ _.mapObject(data, (value, key) =>
2512
+ {
2513
+ if (this.hasOwnProperty(key))
2514
+ {
2515
+ this [ key ] = value;
2516
+ }
2517
+ });
2518
+ }
2519
+
2520
+ return this;
2521
+ }
2522
+ }
2523
+ /**
2524
+ * Коллекция тестов для работы, она же parent для typeTestRunResult
2525
+ *
2526
+ * Содержит ссылки на коллекцию тест кейсов
2527
+ *
2528
+ * @version v.0.2 (21/01/2025)
2529
+ */
2530
+ class typeTestRun extends familyGeneralElement
2531
+ {
2532
+ test_run_id;
2533
+ test_run_name;
2534
+ test_run_description;
2535
+ milestone_id; /// ??
2536
+ assigned_to;
2537
+ test_case_ids; // массив тестов на запуск
2538
+ test_run_status;
2539
+ test_case_summary_statistic; // массив [key->value] по статусам вложенных тест-кейсов для визуализации
2540
+ // typeTestRunResult.test_result_status : 'PASSED', 'FAILED', 'SKIPPED', 'BLOCKED', 'RETEST'
2541
+
2542
+
2543
+ created_at;
2544
+ updated_at;
2545
+ completed_at;
2546
+
2547
+ // логи запуска, окончания - отдельно
2548
+ primary_key = 'test_run_id';
2549
+
2550
+ available_enum_values = {
2551
+ test_run_status: [ 'PENDING', 'IN_PROGRESS', 'COMPLETED', 'CLOSED' ],
2552
+ };
2553
+
2554
+ /**
2555
+ *
2556
+ * @param {object|false|undefined} data
2557
+ */
2558
+ constructor(data = false)
2559
+ {
2560
+ super();
2561
+
2562
+ if (data && is_object(data))
2563
+ {
2564
+ _.mapObject(data, (value, key) =>
2565
+ {
2566
+ if (this.hasOwnProperty(key))
2567
+ {
2568
+ this [ key ] = value;
2569
+ }
2570
+ });
2571
+ }
2572
+
2573
+ return this;
2574
+ }
2575
+ }
2576
+ /**
2577
+ * 1 результат по каждому тест кейсу
2578
+ *
2579
+ * отчет смотрим по testRuns
2580
+ *
2581
+ * @version v.0.4 (10/12/2024)
2582
+ */
2583
+ class typeTestRunResult extends familyGeneralElement
2584
+ {
2585
+ test_run_result_id;
2586
+ test_case_id;
2587
+ test_run_id;
2588
+ test_result_status;
2589
+ task_id; // надо ли?
2590
+ project_id;
2591
+ functional_id; // надо ли?
2592
+ milestone_id;
2593
+ elapsed_time; // фактическое время выполнения
2594
+ reference_defects_ids; // list of IDs in user bug tracker / include sentry ids
2595
+
2596
+ created_at;
2597
+ updated_at;
2598
+ completed_at;
2599
+
2600
+
2601
+ // тут нужен ещё контекст выполнения (банально список браузеров)
2602
+
2603
+ primary_key = 'test_run_result_id';
2604
+
2605
+ structure_scheme = {
2606
+ test_result_id : 'integer | require',
2607
+ test_case_id : 'integer | require',
2608
+ test_run_id : 'integer | require',
2609
+ test_result_status : 'string | enum | require',
2610
+ task_id : 'integer | require',
2611
+ project_id : 'integer | require',
2612
+ functional_id : 'integer | optional',
2613
+ milestone_id : 'integer | optional',
2614
+ test_run_result_to_execute: 'time | optional',
2615
+ reference_defects_ids : 'array | reference | optional',
2616
+ };
2617
+
2618
+ available_enum_values = {
2619
+ test_result_status: [ 'PASSED', 'FAILED', 'SKIPPED', 'BLOCKED', 'RETEST' ]
2620
+ };
2621
+
2622
+
2623
+ /**
2624
+ *
2625
+ * @param {object|false|undefined} data
2626
+ */
2627
+ constructor(data = false)
2628
+ {
2629
+ super();
2630
+
2631
+ if (data && is_object(data))
2632
+ {
2633
+ _.mapObject(data, (value, key) =>
2634
+ {
2635
+ if (this.hasOwnProperty(key))
2636
+ {
2637
+ this [ key ] = value;
2638
+ }
2639
+ });
2640
+ }
2641
+
2642
+ return this;
2643
+ }
2644
+ }
2645
+ /**
2646
+ * Структурная коллекция тестов
2647
+ *
2648
+ * @version v.0.1 (23/06/2024)
2649
+ */
2650
+ class typeTestSuite extends familyGeneralElement
2651
+ {
2652
+ test_suite_id;
2653
+ project_id;
2654
+ functional_id;
2655
+ test_suite_name;
2656
+ test_suite_description;
2657
+ team_id;
2658
+
2659
+ primary_key = 'test_suite_id';
2660
+
2661
+ structure_scheme = {
2662
+ test_suite_id : 'integer | require',
2663
+ project_id : 'integer | require',
2664
+ functional_id : 'integer | optional',
2665
+ test_suite_name : 'string | protected | require',
2666
+ test_suite_description: 'string | require',
2667
+ team_id : 'integer | require',
2668
+ };
2669
+
2670
+
2671
+ /**
2672
+ *
2673
+ * @param {object|false|undefined} data
2674
+ */
2675
+ constructor(data = false)
2676
+ {
2677
+ super();
2678
+
2679
+ if (data && is_object(data))
2680
+ {
2681
+ _.mapObject(data, (value, key) =>
2682
+ {
2683
+ if (this.hasOwnProperty(key))
2684
+ {
2685
+ this [ key ] = value;
2686
+ }
2687
+ });
2688
+ }
2689
+
2690
+ return this;
2691
+ }
2692
+
2693
+
2694
+ /******************************************************************************************/
2695
+ /*********************************** general **********************************************/
2696
+
2697
+ /******************************************************************************************/
2698
+
2699
+ /**
2700
+ *
2701
+ * @version v.0.1 (07/07/2024)
2702
+ */
2703
+ async create()
2704
+ {}
2705
+
2706
+
2707
+ /**
2708
+ *
2709
+ * @version v.0.1 (07/07/2024)
2710
+ */
2711
+ async update()
2712
+ {
2713
+
2714
+ }
2715
+
2716
+
2717
+ /**
2718
+ *
2719
+ * @version v.0.1 (07/07/2024)
2720
+ */
2721
+ async delete()
2722
+ {
2723
+
2724
+ }
2725
+
2726
+
2727
+ }
2728
+
2729
+ /**
2730
+ * @version v.0.1 (23/06/2024)
2731
+ */
2732
+ class typeUser extends familyGeneralElement
2733
+ {
2734
+ user_id;
2735
+ country;
2736
+ user_name;
2737
+ user_password; // не доступно на фронте
2738
+ user_status;
2739
+ user_email;
2740
+ user_phone;
2741
+
2742
+ primary_key = 'user_id';
2743
+
2744
+ structure_scheme = {
2745
+ user_id : 'integer | require',
2746
+ country : 'string | enum | require',
2747
+ user_name : 'string | require',
2748
+ user_password : 'string | protected | require',
2749
+ user_status : 'string | enum| require',
2750
+ user_email : 'string | email | require',
2751
+ user_phone_number: 'string | phone_number | optional',
2752
+ };
2753
+
2754
+ available_enum_values = {
2755
+ user_status: [ 'PENDING', 'APPROVED', 'BANNED', 'FROZEN', 'BLOCKED' ]
2756
+ };
2757
+
2758
+
2759
+ /**
2760
+ *
2761
+ * @param {object|false|undefined} data
2762
+ */
2763
+ constructor(data = false)
2764
+ {
2765
+ super();
2766
+
2767
+ if (data && is_object(data))
2768
+ {
2769
+ _.mapObject(data, (value, key) =>
2770
+ {
2771
+ if (this.hasOwnProperty(key))
2772
+ {
2773
+ this [ key ] = value;
2774
+ }
2775
+ });
2776
+ }
2777
+
2778
+ return this;
2779
+ }
2780
+ }
2781
+ /**
2782
+ * базовый тип для всех служебных типов
2783
+ * @version v.0.1 (26/05/2024)
2784
+ */
2785
+ class familyService
2786
+ {
2787
+ //
2788
+ }
2789
+ /**
2790
+ *
2791
+ * @version v.0.1 (26/01/2025)
2792
+ */
2793
+ class typeFOR extends familyService
2794
+ {
2795
+ filter;
2796
+ object;
2797
+ response;
2798
+
2799
+ /**
2800
+ *
2801
+ * @version v.0.1 (26/01/2025)
2802
+ * @param data
2803
+ * @returns {typeFOR}
2804
+ */
2805
+ constructor(data = false)
2806
+ {
2807
+ super();
2808
+
2809
+ if (data && typeof data === 'object')
2810
+ {
2811
+ _.mapObject(data, (value, key) =>
2812
+ {
2813
+ if (this.hasOwnProperty(key))
2814
+ {
2815
+ this [ key ] = value;
2816
+ }
2817
+ });
2818
+ }
2819
+
2820
+ return this;
2821
+ }
2822
+ }
2823
+ /**
2824
+ * Описание стандартного колбека типового для методов
2825
+ *
2826
+ * @version v.0.1 (26/05/2024)
2827
+ */
2828
+ class typeFunctionCallback extends familyService
2829
+ {
2830
+ before;
2831
+ success;
2832
+ error;
2833
+ final;
2834
+ destroy;
2835
+
2836
+
2837
+ /**
2838
+ *
2839
+ * @version v.0.1 (26/05/2024)
2840
+ * @param {object|false} data
2841
+ * @return {typeFunctionCallback}
2842
+ */
2843
+ constructor(data = false)
2844
+ {
2845
+ super();
2846
+
2847
+ if (data && typeof data === 'object')
2848
+ {
2849
+ _.mapObject(data, (value, key) =>
2850
+ {
2851
+ if (this.hasOwnProperty(key))
2852
+ {
2853
+ this [ key ] = value;
2854
+ }
2855
+ });
2856
+ }
2857
+
2858
+ return this;
2859
+ }
2860
+ }
2861
+ /**
2862
+ * Уведомления
2863
+ * Как будто это абстрактный интерфейс
2864
+ * А внутри уже разные настроенные пользователем методы
2865
+ *
2866
+ */
2867
+ class typeNotification extends familyService
2868
+ {
2869
+
2870
+ // пока не понятно
2871
+
2872
+ /**
2873
+ *
2874
+ * @param {object|false|undefined} data
2875
+ */
2876
+ constructor(data = false)
2877
+ {
2878
+ super();
2879
+
2880
+ if (data && is_object(data))
2881
+ {
2882
+ _.mapObject(data, (value, key) =>
2883
+ {
2884
+ if (this.hasOwnProperty(key))
2885
+ {
2886
+ this [ key ] = value;
2887
+ }
2888
+ });
2889
+ }
2890
+
2891
+ return this;
2892
+ }
2893
+ }
2894
+ /**
2895
+ *
2896
+ * @version v.0.1 (02/07/2024)
2897
+ */
2898
+ class abstractMockData
2899
+ {
2900
+ mock_data_type = 'correct';
2901
+
2902
+ constructor(mock_data_type = 'correct')
2903
+ {
2904
+ this.mock_data_type = mock_data_type;
2905
+ }
2906
+ }
2907
+
2908
+ /**
2909
+ * @version v.0.1 (02/07/2024)
2910
+ */
2911
+ class MockDataTypeProject extends abstractMockData
2912
+ {
2913
+ correct = [ {
2914
+ project_id : 11,
2915
+ project_name : 'Demo Project #11',
2916
+ project_description: 'Description – это мета тег, представляющий собой описание. Он находится под заголовком (title) и ссылкой на сайт. Целью тега является вывод страницы в топ. Это в свою очередь привлечет больше целевых пользователей. Можно составить description самостоятельно.',
2917
+ project_status : 'OPEN',
2918
+ team_id : 0,
2919
+ }, {
2920
+ project_id : 22,
2921
+ project_name : 'Demo Project #22',
2922
+ project_description: 'Однако есть риск допустить ошибку. Если использовать не те фразы и длину предложения, то успеха не достичь.',
2923
+ project_status : 'OPEN',
2924
+ team_id : 0,
2925
+ }, {
2926
+ project_id : 33,
2927
+ project_name : 'Perspective project № 1',
2928
+ project_description: 'Чтобы сделать онлайн контент план, нужно ввести тематику блога. На основании данных генератор подберет темы.',
2929
+ project_status : 'PLANNED',
2930
+ team_id : 0,
2931
+ }, {
2932
+ project_id : 44,
2933
+ project_name : 'Perspective project № 2',
2934
+ project_description: 'Укажите любые детали вашей веб-страницы: вставьте сам текст или укажите тему/ключевые слова;',
2935
+ project_status : 'PLANNED',
2936
+ team_id : 0,
2937
+ }, {
2938
+ project_id : 55,
2939
+ project_name : 'Closed project № 1',
2940
+ project_description: 'Протестируйте бесплатно',
2941
+ project_status : 'PLANNED',
2942
+ team_id : 0,
2943
+ }, {
2944
+ project_id : 66,
2945
+ project_name : 'Closed project № 1',
2946
+ project_description: 'Создать теги онлайн можно бесплатно. Для этого вставьте всю информацию в специальную форму. Генератор быстро сформирует результат. Вы сможете убедиться в его скорости и эффективности. Создатель тегов – быстрый и удобный сервис.',
2947
+ project_status : 'PLANNED',
2948
+ team_id : 0,
2949
+ },
2950
+ ];
2951
+
2952
+ static last_index = {
2953
+ correct: 0
2954
+ };
2955
+
2956
+
2957
+ /**
2958
+ *
2959
+ * @version v.0.1 (02/07/2024)
2960
+ * @param mock_data_type
2961
+ */
2962
+ constructor(mock_data_type = 'correct')
2963
+ {
2964
+ super();
2965
+
2966
+ let response = false;
2967
+ if (mock_data_type === 'correct')
2968
+ {
2969
+ if (is_object(this.correct[ MockDataTypeProject.last_index[ mock_data_type ] ]))
2970
+ {
2971
+ response = this.correct[ MockDataTypeProject.last_index[ mock_data_type ] ];
2972
+ }
2973
+ else
2974
+ {
2975
+ MockDataTypeProject.last_index[ mock_data_type ] = 0;
2976
+ response = this.correct[ MockDataTypeProject.last_index[ mock_data_type ] ];
2977
+ }
2978
+ }
2979
+
2980
+ if (count(this[ mock_data_type ]) >= MockDataTypeProject.last_index[ mock_data_type ])
2981
+ {
2982
+ MockDataTypeProject.last_index[ mock_data_type ]++;
2983
+ }
2984
+ else
2985
+ {
2986
+ MockDataTypeProject.last_index[ mock_data_type ] = 0;
2987
+ }
2988
+
2989
+ return response;
2990
+ }
2991
+ }