@adaas/a-concept 0.1.52 → 0.1.54
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.
- package/LICENSE +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +51 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/global/A-Context/A-Context.class.ts +23 -16
- package/src/global/A-Feature/A-Feature.class.ts +60 -0
- package/src/helpers/A_Common.helper.ts +62 -0
- package/tests/A-Feature.test.ts +85 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaas/a-concept",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.54",
|
|
4
4
|
"description": "A-Concept is a framework to build new Applications within or outside the ADAAS ecosystem. This framework is designed to be modular structure regardless environment and program goal.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -709,26 +709,33 @@ export class A_Context {
|
|
|
709
709
|
if (!A_TypeGuards.isAllowedForFeatureDefinition(component))
|
|
710
710
|
throw new A_ContextError(A_ContextError.InvalidFeatureExtensionParameterError, `Unable to get feature template. Component of type ${componentName} is not allowed for feature definition.`);
|
|
711
711
|
|
|
712
|
-
|
|
712
|
+
|
|
713
|
+
const callNames = A_CommonHelper.getClassInheritanceChain(component)
|
|
714
|
+
.filter(c => c !== A_Component && c !== A_Container && c !== A_Entity)
|
|
715
|
+
.map(c => `${c.name}.${name}`);
|
|
716
|
+
|
|
717
|
+
// const callName = `${component.constructor.name}.${name}`;
|
|
713
718
|
|
|
714
719
|
const steps: A_TYPES__A_StageStep[] = [];
|
|
715
720
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
721
|
+
for (const callName of callNames) {
|
|
722
|
+
// We need to get all components that has extensions for the feature in component
|
|
723
|
+
for (const [cmp, meta] of instance._metaStorage) {
|
|
724
|
+
// Just try to make sure that component not only Indexed but also presented in scope
|
|
725
|
+
if (scope.has(cmp) && (
|
|
726
|
+
A_TypeGuards.isComponentMetaInstance(meta)
|
|
727
|
+
|| A_TypeGuards.isContainerMetaInstance(meta)
|
|
728
|
+
)) {
|
|
729
|
+
// Get all extensions for the feature
|
|
730
|
+
meta
|
|
731
|
+
.extensions(callName)
|
|
732
|
+
.forEach((declaration) => {
|
|
733
|
+
steps.push({
|
|
734
|
+
component: cmp,
|
|
735
|
+
...declaration
|
|
736
|
+
});
|
|
730
737
|
});
|
|
731
|
-
|
|
738
|
+
}
|
|
732
739
|
}
|
|
733
740
|
}
|
|
734
741
|
|
|
@@ -474,6 +474,66 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
474
474
|
}
|
|
475
475
|
|
|
476
476
|
|
|
477
|
+
/**
|
|
478
|
+
* Allows to chain the feature to another feature.
|
|
479
|
+
* In this case the parent feature scope (if new not provided), stages, caller will be used.
|
|
480
|
+
*
|
|
481
|
+
* [!] Note: Chained feature will use the same caller as the parent feature.
|
|
482
|
+
*
|
|
483
|
+
* @param feature
|
|
484
|
+
*/
|
|
485
|
+
chain(
|
|
486
|
+
/**
|
|
487
|
+
* A Feature to be chained
|
|
488
|
+
*/
|
|
489
|
+
feature: A_Feature,
|
|
490
|
+
/**
|
|
491
|
+
* Optional scope to be used for the chained feature.
|
|
492
|
+
*/
|
|
493
|
+
scope?: A_Scope
|
|
494
|
+
)
|
|
495
|
+
chain<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__FeatureAvailableComponents>(
|
|
496
|
+
/**
|
|
497
|
+
* Component whose feature should be chained
|
|
498
|
+
*/
|
|
499
|
+
component: A_TYPES__FeatureAvailableComponents,
|
|
500
|
+
/**
|
|
501
|
+
* A Feature Name to be chained
|
|
502
|
+
*/
|
|
503
|
+
feature: string,
|
|
504
|
+
/**
|
|
505
|
+
* Optional scope to be used for the chained feature.
|
|
506
|
+
*/
|
|
507
|
+
scope?: A_Scope
|
|
508
|
+
)
|
|
509
|
+
chain<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__FeatureAvailableComponents>(
|
|
510
|
+
param1: A_TYPES__FeatureAvailableComponents | A_Feature,
|
|
511
|
+
param2?: string | A_Scope,
|
|
512
|
+
param3?: A_Scope
|
|
513
|
+
) {
|
|
514
|
+
let feature: A_Feature;
|
|
515
|
+
let scope: A_Scope | undefined;
|
|
516
|
+
|
|
517
|
+
if (param1 instanceof A_Feature) {
|
|
518
|
+
feature = param1;
|
|
519
|
+
scope = param2 instanceof A_Scope ? param2 : undefined;
|
|
520
|
+
} else {
|
|
521
|
+
feature = new A_Feature({
|
|
522
|
+
name: param2 as string,
|
|
523
|
+
component: param1 as T
|
|
524
|
+
});
|
|
525
|
+
scope = param3 instanceof A_Scope ? param3 : undefined;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const featureScope = scope || this.scope;
|
|
529
|
+
|
|
530
|
+
// create new caller for the chained feature
|
|
531
|
+
feature._caller = this._caller;
|
|
532
|
+
|
|
533
|
+
return feature.process(featureScope);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
|
|
477
537
|
|
|
478
538
|
|
|
479
539
|
toString(): string {
|
|
@@ -30,6 +30,68 @@ export class A_CommonHelper {
|
|
|
30
30
|
return false;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get all parent classes of a given class
|
|
36
|
+
*
|
|
37
|
+
* @param childClass
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
static getParentClasses(childClass: any): any[] {
|
|
41
|
+
|
|
42
|
+
// first we need to check is that a constructor or instance
|
|
43
|
+
let current = typeof childClass === 'function'
|
|
44
|
+
? Object.getPrototypeOf(childClass)
|
|
45
|
+
: Object.getPrototypeOf(childClass.constructor);
|
|
46
|
+
|
|
47
|
+
const parents = [] as any[];
|
|
48
|
+
|
|
49
|
+
// Traverse the prototype chain
|
|
50
|
+
while (current && current !== Function.prototype) {
|
|
51
|
+
parents.push(current);
|
|
52
|
+
current = Object.getPrototypeOf(current);
|
|
53
|
+
}
|
|
54
|
+
return parents;
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the class inheritance chain as an array of class names
|
|
60
|
+
*
|
|
61
|
+
* @param childClass
|
|
62
|
+
* @returns
|
|
63
|
+
*/
|
|
64
|
+
static getClassInheritanceChain(childClass: any): any[] {
|
|
65
|
+
|
|
66
|
+
// first we need to check is that a constructor or instance
|
|
67
|
+
let current = typeof childClass === 'function'
|
|
68
|
+
? Object.getPrototypeOf(childClass)
|
|
69
|
+
: Object.getPrototypeOf(childClass.constructor);
|
|
70
|
+
|
|
71
|
+
// then if input is instance we have to include its own class name
|
|
72
|
+
const chain = typeof childClass === 'function'
|
|
73
|
+
? [childClass]
|
|
74
|
+
: [childClass.constructor];
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
// Traverse the prototype chain
|
|
78
|
+
while (current && current !== Function.prototype) {
|
|
79
|
+
chain.push(current);
|
|
80
|
+
current = Object.getPrototypeOf(current);
|
|
81
|
+
}
|
|
82
|
+
return chain;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get the parent class of a given class
|
|
87
|
+
*
|
|
88
|
+
* @param childClass
|
|
89
|
+
* @returns
|
|
90
|
+
*/
|
|
91
|
+
static getParentClass(childClass: any): any {
|
|
92
|
+
return Object.getPrototypeOf(childClass);
|
|
93
|
+
}
|
|
94
|
+
|
|
33
95
|
/**
|
|
34
96
|
* Omit properties from an object or array with nested objects
|
|
35
97
|
*
|
package/tests/A-Feature.test.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { A_Scope } from "@adaas/a-concept/global/A-Scope/A-Scope.class";
|
|
|
5
5
|
import { A_Caller } from '@adaas/a-concept/global/A-Caller/A_Caller.class';
|
|
6
6
|
import { A_Context } from '@adaas/a-concept/global/A-Context/A-Context.class';
|
|
7
7
|
import { A_TYPES__ComponentMetaKey } from '@adaas/a-concept/global/A-Component/A-Component.constants';
|
|
8
|
-
import { A_Error, A_TYPES__FeatureState } from "../src";
|
|
8
|
+
import { A_Entity, A_Error, A_TYPES__FeatureState } from "../src";
|
|
9
9
|
|
|
10
10
|
jest.retryTimes(0);
|
|
11
11
|
|
|
@@ -582,4 +582,88 @@ describe('A-Feature tests', () => {
|
|
|
582
582
|
});
|
|
583
583
|
|
|
584
584
|
}, 5000);
|
|
585
|
+
it('Should allow to use extension if only parent class provided', async () => {
|
|
586
|
+
const executionResults: string[] = [];
|
|
587
|
+
|
|
588
|
+
class BaseEntity extends A_Entity {
|
|
589
|
+
async test() {
|
|
590
|
+
await this.call('myFeature');
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
class MyComponent extends A_Component {
|
|
596
|
+
|
|
597
|
+
@A_Feature.Extend({
|
|
598
|
+
name: 'myFeature',
|
|
599
|
+
scope: [BaseEntity]
|
|
600
|
+
})
|
|
601
|
+
testMethod() {
|
|
602
|
+
executionResults.push('testMethod');
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
class My_Entity extends BaseEntity {
|
|
607
|
+
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const scope = new A_Scope({ name: 'TestScope', components: [MyComponent] });
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
const myEntity = new My_Entity({ name: 'MyEntityInstance' });
|
|
614
|
+
const baseEntity = new BaseEntity({ name: 'BaseEntityInstance' });
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
scope.register(myEntity);
|
|
618
|
+
scope.register(baseEntity);
|
|
619
|
+
|
|
620
|
+
await baseEntity.test();
|
|
621
|
+
|
|
622
|
+
expect(executionResults).toEqual(['testMethod']);
|
|
623
|
+
|
|
624
|
+
await myEntity.test();
|
|
625
|
+
|
|
626
|
+
expect(executionResults).toEqual(['testMethod', 'testMethod']);
|
|
627
|
+
});
|
|
628
|
+
it('Should allow be possible to do a Feature Chaining without Caller Change', async () => {
|
|
629
|
+
const executionResults: string[] = [];
|
|
630
|
+
|
|
631
|
+
class Component_B extends A_Component {
|
|
632
|
+
|
|
633
|
+
@A_Feature.Extend()
|
|
634
|
+
featureB(
|
|
635
|
+
@A_Inject(A_Caller) caller: Component_A
|
|
636
|
+
) {
|
|
637
|
+
executionResults.push('featureB');
|
|
638
|
+
|
|
639
|
+
expect(caller).toBeInstanceOf(Component_A);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
class Component_A extends A_Component {
|
|
644
|
+
|
|
645
|
+
@A_Feature.Extend()
|
|
646
|
+
async featureA(
|
|
647
|
+
@A_Inject(A_Feature) feature: A_Feature,
|
|
648
|
+
@A_Inject(Component_B) compb: Component_B
|
|
649
|
+
) {
|
|
650
|
+
executionResults.push('featureA');
|
|
651
|
+
|
|
652
|
+
await feature.chain(compb, 'featureB');
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
const scope = new A_Scope({ name: 'TestScope', components: [Component_A, Component_B] });
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
const compA = scope.resolve(Component_A)!;
|
|
662
|
+
|
|
663
|
+
await compA.call('featureA');
|
|
664
|
+
|
|
665
|
+
expect(executionResults).toEqual(['featureA', 'featureB']);
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
});
|
|
585
669
|
});
|