Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions core/core-decorator/src/util/PrototypeUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import {
EggMultiInstancePrototypeInfo,
EggProtoImplClass,
EggPrototypeInfo,
EggPrototypeName,
EggPrototypeName, InitTypeQualifierAttribute,
InjectConstructorInfo,
InjectObjectInfo,
InjectType,
InjectType, LoadUnitNameQualifierAttribute,
MultiInstancePrototypeGetObjectsContext, QualifierAttribute,
} from '@eggjs/tegg-types';
import { MetadataUtil } from './MetadataUtil';
Expand Down Expand Up @@ -141,9 +141,24 @@ export class PrototypeUtil {
}
const callBackMetadata = MetadataUtil.getMetaData<EggMultiInstanceCallbackPrototypeInfo>(PrototypeUtil.MULTI_INSTANCE_PROTOTYPE_CALLBACK_PROPERTY, clazz);
if (callBackMetadata) {
const objects = callBackMetadata.getObjects(ctx);
const defaultQualifier = [{
attribute: InitTypeQualifierAttribute,
value: callBackMetadata.initType,
}, {
attribute: LoadUnitNameQualifierAttribute,
value: ctx.moduleName,
}];
for (const object of objects) {
defaultQualifier.forEach(qualifier => {
if (!object.qualifiers.find(t => t.attribute === qualifier.attribute)) {
object.qualifiers.push(qualifier);
}
});
}
Comment on lines +144 to +158
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure object.qualifiers is initialized before usage

The code assumes that object.qualifiers is already an array. If object.qualifiers is undefined, accessing find or pushing to it will result in a runtime error. It's important to ensure that object.qualifiers is initialized.

Apply this diff to initialize object.qualifiers if it's not already:

for (const object of objects) {
+  if (!object.qualifiers) {
+    object.qualifiers = [];
+  }
   defaultQualifier.forEach(qualifier => {
     if (!object.qualifiers.find(t => t.attribute === qualifier.attribute)) {
       object.qualifiers.push(qualifier);
     }
   });
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const objects = callBackMetadata.getObjects(ctx);
const defaultQualifier = [{
attribute: InitTypeQualifierAttribute,
value: callBackMetadata.initType,
}, {
attribute: LoadUnitNameQualifierAttribute,
value: ctx.moduleName,
}];
for (const object of objects) {
defaultQualifier.forEach(qualifier => {
if (!object.qualifiers.find(t => t.attribute === qualifier.attribute)) {
object.qualifiers.push(qualifier);
}
});
}
const objects = callBackMetadata.getObjects(ctx);
const defaultQualifier = [{
attribute: InitTypeQualifierAttribute,
value: callBackMetadata.initType,
}, {
attribute: LoadUnitNameQualifierAttribute,
value: ctx.moduleName,
}];
for (const object of objects) {
if (!object.qualifiers) {
object.qualifiers = [];
}
defaultQualifier.forEach(qualifier => {
if (!object.qualifiers.find(t => t.attribute === qualifier.attribute)) {
object.qualifiers.push(qualifier);
}
});
}

return {
...callBackMetadata,
objects: callBackMetadata.getObjects(ctx),
objects,
};
}
}
Expand Down
19 changes: 18 additions & 1 deletion core/metadata/src/factory/EggPrototypeCreatorFactory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PrototypeUtil } from '@eggjs/core-decorator';
import { InitTypeQualifierAttribute, LoadUnitNameQualifierAttribute, PrototypeUtil } from '@eggjs/core-decorator';
import type {
EggProtoImplClass,
EggPrototypeInfo,
Expand Down Expand Up @@ -28,12 +28,29 @@ export class EggPrototypeCreatorFactory {
moduleName: loadUnit.name,
})!;
for (const obj of multiInstanceProtoInfo.objects) {
const defaultQualifier = [{
attribute: InitTypeQualifierAttribute,
value: PrototypeUtil.getInitType(clazz, {
unitPath: loadUnit.unitPath,
moduleName: loadUnit.name,
})!,
}, {
attribute: LoadUnitNameQualifierAttribute,
value: loadUnit.name,
}];
defaultQualifier.forEach(qualifier => {
if (!obj.qualifiers.find(t => t.attribute === qualifier.attribute)) {
obj.qualifiers.push(qualifier);
}
});

properties.push({
name: obj.name,
protoImplType: multiInstanceProtoInfo.protoImplType,
initType: multiInstanceProtoInfo.initType,
accessLevel: multiInstanceProtoInfo.accessLevel,
qualifiers: obj.qualifiers,
properQualifiers: obj.properQualifiers,
className: multiInstanceProtoInfo.className,
});
}
Expand Down
28 changes: 19 additions & 9 deletions core/metadata/src/impl/EggPrototypeBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class EggPrototypeBuilder {
private injectObjects: Array<InjectObject | InjectConstructor> = [];
private loadUnit: LoadUnit;
private qualifiers: QualifierInfo[] = [];
private properQualifiers: Record<string, QualifierInfo[]> = {};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent naming: 'properQualifiers' vs 'propertyQualifiers'

There is inconsistency in variable names between properQualifiers and propertyQualifiers. This can lead to confusion and potential errors. Consider standardizing the naming throughout the codebase to either properQualifiers or propertyQualifiers for clarity and maintainability.

private className?: string;
private multiInstanceConstructorIndex?: number;
private multiInstanceConstructorAttributes?: QualifierAttribute[];
Expand All @@ -56,38 +57,47 @@ export class EggPrototypeBuilder {
...QualifierUtil.getProtoQualifiers(clazz),
...(ctx.prototypeInfo.qualifiers ?? []),
];
console.log('proto: ', ctx.prototypeInfo.properQualifiers);
builder.properQualifiers = ctx.prototypeInfo.properQualifiers ?? {};
builder.multiInstanceConstructorIndex = PrototypeUtil.getMultiInstanceConstructorIndex(clazz);
builder.multiInstanceConstructorAttributes = PrototypeUtil.getMultiInstanceConstructorAttributes(clazz);
return builder.build();
}

private tryFindDefaultPrototype(injectObject: InjectObject): EggPrototype {
const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName);
return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, propertyQualifiers);
const multiInstancePropertyQualifiers = this.properQualifiers[injectObject.refName as string] ?? [];
console.log('multi instance: ', this.properQualifiers, injectObject.refName);
return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, [
...propertyQualifiers,
...multiInstancePropertyQualifiers,
]);
Comment on lines +69 to +74
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor to eliminate code duplication in prototype retrieval

The logic for retrieving qualifiers and calling EggPrototypeFactory.instance.getPrototype is duplicated across the methods tryFindDefaultPrototype, tryFindContextPrototype, and tryFindSelfInitTypePrototype. Refactoring this repeated code into a shared helper method would improve maintainability and reduce duplication.

Suggested refactoring:

Add a private helper method to combine qualifiers:

private getCombinedQualifiers(injectObject: InjectObject): QualifierInfo[] {
  const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName);
  const multiInstancePropertyQualifiers = this.properQualifiers[injectObject.refName as string] ?? [];
  return [
    ...propertyQualifiers,
    ...multiInstancePropertyQualifiers,
  ];
}

Modify the methods to use this helper:

// In tryFindDefaultPrototype
-const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName);
-const multiInstancePropertyQualifiers = this.properQualifiers[injectObject.refName as string] ?? [];
 return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, [
-  ...propertyQualifiers,
-  ...multiInstancePropertyQualifiers,
+  ...this.getCombinedQualifiers(injectObject),
 ]);

// Similar changes in tryFindContextPrototype and tryFindSelfInitTypePrototype

Also applies to: 78-80, 91-93

}

private tryFindContextPrototype(injectObject: InjectObject): EggPrototype {
let propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName);
propertyQualifiers = [
const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName);
const multiInstancePropertyQualifiers = this.properQualifiers[injectObject.refName as string] ?? [];
return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, [
...propertyQualifiers,
...multiInstancePropertyQualifiers,
{
attribute: InitTypeQualifierAttribute,
value: ObjectInitType.CONTEXT,
},
];
return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, propertyQualifiers);
]);
}

private tryFindSelfInitTypePrototype(injectObject: InjectObject): EggPrototype {
let propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName);
propertyQualifiers = [
const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName);
const multiInstancePropertyQualifiers = this.properQualifiers[injectObject.refName as string] ?? [];
return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, [
...propertyQualifiers,
...multiInstancePropertyQualifiers,
{
attribute: InitTypeQualifierAttribute,
value: this.initType,
},
];
return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, propertyQualifiers);
]);
}

private findInjectObjectPrototype(injectObject: InjectObject): EggPrototype {
Expand Down
109 changes: 33 additions & 76 deletions core/metadata/src/impl/ModuleLoadUnit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,17 @@ class ProtoNode implements GraphNodeObj {
readonly qualifiers: QualifierInfo[];
readonly initType: ObjectInitTypeLike;

constructor(clazz: EggProtoImplClass, objName: EggPrototypeName, unitPath: string, moduleName: string) {
constructor(
clazz: EggProtoImplClass,
objName: EggPrototypeName,
initType: ObjectInitTypeLike,
qualifiers: QualifierInfo[],
) {
this.name = objName;
this.id = '' + (id++);
this.clazz = clazz;
this.qualifiers = QualifierUtil.getProtoQualifiers(clazz);
this.initType = PrototypeUtil.getInitType(clazz, {
unitPath,
moduleName,
})!;
this.qualifiers = qualifiers;
this.initType = initType;
}

verifyQualifiers(qualifiers: QualifierInfo[]): boolean {
Expand All @@ -65,77 +67,21 @@ class ProtoNode implements GraphNodeObj {
}
}

class MultiInstanceProtoNode implements GraphNodeObj {
readonly clazz: EggProtoImplClass;
readonly name: EggPrototypeName;
readonly id: string;
readonly qualifiers: QualifierInfo[];
readonly initType: ObjectInitTypeLike;
readonly unitPath: string;
readonly moduleName: string;

constructor(clazz: EggProtoImplClass, objName: EggPrototypeName, unitPath: string, moduleName: string) {
this.name = objName;
this.id = '' + (id++);
this.clazz = clazz;
this.qualifiers = QualifierUtil.getProtoQualifiers(clazz);
this.initType = PrototypeUtil.getInitType(clazz, {
unitPath,
moduleName,
})!;
this.unitPath = unitPath;
this.moduleName = moduleName;
}

verifyQualifiers(qualifiers: QualifierInfo[]): boolean {
const property = PrototypeUtil.getMultiInstanceProperty(this.clazz, {
unitPath: this.unitPath,
moduleName: this.moduleName,
});
if (!property) {
return false;
}
for (const obj of property.objects) {
const selfQualifiers = [
...this.qualifiers,
...obj.qualifiers,
];
if (this.verifyInstanceQualifiers(selfQualifiers, qualifiers)) {
return true;
}
}
return false;
}

verifyInstanceQualifiers(selfQualifiers: QualifierInfo[], qualifiers: QualifierInfo[]): boolean {
for (const qualifier of qualifiers) {
if (!selfQualifiers.find(t => t.attribute === qualifier.attribute && t.value === qualifier.value)) {
return false;
}
}
return true;
}

toString(): string {
return `${this.clazz.name}@${PrototypeUtil.getFilePath(this.clazz)}`;
}
}

export class ModuleGraph {
private graph: Graph<ProtoNode | MultiInstanceProtoNode>;
private graph: Graph<ProtoNode>;
clazzList: EggProtoImplClass[];
readonly unitPath: string;
readonly name: string;

constructor(clazzList: EggProtoImplClass[], unitPath: string, name: string) {
this.clazzList = clazzList;
this.graph = new Graph<ProtoNode | MultiInstanceProtoNode>();
this.graph = new Graph<ProtoNode>();
this.unitPath = unitPath;
this.name = name;
this.build();
}

private findInjectNode(objName: EggPrototypeName, qualifiers: QualifierInfo[], parentInitTye: ObjectInitTypeLike): GraphNode<ProtoNode | MultiInstanceProtoNode> | undefined {
private findInjectNode(objName: EggPrototypeName, qualifiers: QualifierInfo[], parentInitTye: ObjectInitTypeLike): GraphNode<ProtoNode> | undefined {
let nodes = Array.from(this.graph.nodes.values())
.filter(t => t.val.name === objName)
.filter(t => t.val.verifyQualifiers(qualifiers));
Expand All @@ -156,7 +102,7 @@ export class ModuleGraph {
return nodes[0];
}

const temp: Map<EggProtoImplClass, GraphNode<ProtoNode | MultiInstanceProtoNode>> = new Map();
const temp: Map<EggProtoImplClass, GraphNode<ProtoNode>> = new Map();
for (const node of nodes) {
temp.set(node.val.clazz, node);
}
Expand All @@ -170,17 +116,28 @@ export class ModuleGraph {
}

private build() {
const protoGraphNodes: GraphNode<ProtoNode | MultiInstanceProtoNode>[] = [];
const protoGraphNodes: GraphNode<ProtoNode>[] = [];
for (const clazz of this.clazzList) {
const objNames = PrototypeUtil.getObjNames(clazz, {
unitPath: this.unitPath,
moduleName: this.name,
});
for (const objName of objNames) {
if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) {
protoGraphNodes.push(new GraphNode(new MultiInstanceProtoNode(clazz, objName, this.unitPath, this.name)));
} else {
protoGraphNodes.push(new GraphNode(new ProtoNode(clazz, objName, this.unitPath, this.name)));
if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) {
const properties = PrototypeUtil.getMultiInstanceProperty(clazz, {
unitPath: this.unitPath,
moduleName: this.name,
});
if (properties) {
const qualifiers = QualifierUtil.getProtoQualifiers(clazz);
for (const obj of properties.objects || []) {
const instanceQualifiers = [
...qualifiers,
...obj.qualifiers,
];
protoGraphNodes.push(new GraphNode(new ProtoNode(clazz, obj.name, properties.initType, instanceQualifiers)));
}
}
} else {
const qualifiers = QualifierUtil.getProtoQualifiers(clazz);
const property = PrototypeUtil.getProperty(clazz);
if (property) {
protoGraphNodes.push(new GraphNode(new ProtoNode(clazz, property.name, property.initType, qualifiers)));
}
}
}
Expand Down
35 changes: 19 additions & 16 deletions core/metadata/src/model/AppGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,18 @@ export class ClazzMap {
});
assert(property, `multi instance property not found for ${clazz.name}`);
for (const info of property.objects) {
const instanceQualifiers = [
...qualifiers,
...info.qualifiers,
];
Comment on lines +58 to +61
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure info.qualifiers is defined before using spread operator

When using the spread operator on info.qualifiers, if it is undefined, it will cause a runtime error. To prevent this, ensure that info.qualifiers is always an array.

Consider providing a default empty array if info.qualifiers might be undefined:

 const instanceQualifiers = [
   ...qualifiers,
-  ...info.qualifiers,
+  ...(info.qualifiers || []),
 ];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const instanceQualifiers = [
...qualifiers,
...info.qualifiers,
];
const instanceQualifiers = [
...qualifiers,
...(info.qualifiers || []),
];

clazzMap[info.name] = clazzMap[info.name] || [];
clazzMap[info.name].push({
name: info.name,
accessLevel: PrototypeUtil.getAccessLevel(clazz, {
unitPath: instanceNode.val.moduleConfig.path,
moduleName: instanceNode.val.moduleConfig.name,
}) as AccessLevel,
qualifiers: [
...qualifiers,
...info.qualifiers,
],
qualifiers: instanceQualifiers,
properQualifiers: info.properQualifiers || {},
instanceModule: instanceNode,
ownerModule: ownerNode,
Expand Down Expand Up @@ -179,18 +180,20 @@ export class ModuleNode implements GraphNodeObj {
if (!this.clazzList.includes(clazz)) {
this.clazzList.push(clazz);
}
const defaultQualifier = [{
attribute: InitTypeQualifierAttribute,
value: PrototypeUtil.getInitType(clazz, {
unitPath: this.moduleConfig.path,
moduleName: this.moduleConfig.name,
})!,
}, {
attribute: LoadUnitNameQualifierAttribute,
value: this.name,
}];
for (const qualifier of defaultQualifier) {
QualifierUtil.addProtoQualifier(clazz, qualifier.attribute, qualifier.value);
if (!PrototypeUtil.isEggMultiInstancePrototype(clazz)) {
const defaultQualifier = [{
attribute: InitTypeQualifierAttribute,
value: PrototypeUtil.getInitType(clazz, {
unitPath: this.moduleConfig.path,
moduleName: this.moduleConfig.name,
})!,
}, {
attribute: LoadUnitNameQualifierAttribute,
value: this.name,
}];
for (const qualifier of defaultQualifier) {
QualifierUtil.addProtoQualifier(clazz, qualifier.attribute, qualifier.value);
}
Comment on lines +183 to +196
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle possible undefined return value from getInitType

The PrototypeUtil.getInitType method is called with a non-null assertion operator !. If getInitType returns undefined, this could lead to runtime errors. It's important to handle the possibility that initType might be undefined.

Consider checking if initType is undefined before proceeding:

 const initType = PrototypeUtil.getInitType(clazz, {
   unitPath: this.moduleConfig.path,
   moduleName: this.moduleConfig.name,
 });
+if (!initType) {
+  // Handle undefined initType appropriately
+  throw new Error(`InitType is undefined for ${clazz.name}`);
+}

 const defaultQualifier = [{
   attribute: InitTypeQualifierAttribute,
-  value: PrototypeUtil.getInitType(clazz, {
-    unitPath: this.moduleConfig.path,
-    moduleName: this.moduleConfig.name,
-  })!,
+  value: initType,
 }, {
   attribute: LoadUnitNameQualifierAttribute,
   value: this.name,
 }];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!PrototypeUtil.isEggMultiInstancePrototype(clazz)) {
const defaultQualifier = [{
attribute: InitTypeQualifierAttribute,
value: PrototypeUtil.getInitType(clazz, {
unitPath: this.moduleConfig.path,
moduleName: this.moduleConfig.name,
})!,
}, {
attribute: LoadUnitNameQualifierAttribute,
value: this.name,
}];
for (const qualifier of defaultQualifier) {
QualifierUtil.addProtoQualifier(clazz, qualifier.attribute, qualifier.value);
}
if (!PrototypeUtil.isEggMultiInstancePrototype(clazz)) {
const initType = PrototypeUtil.getInitType(clazz, {
unitPath: this.moduleConfig.path,
moduleName: this.moduleConfig.name,
});
if (!initType) {
// Handle undefined initType appropriately
throw new Error(`InitType is undefined for ${clazz.name}`);
}
const defaultQualifier = [{
attribute: InitTypeQualifierAttribute,
value: initType,
}, {
attribute: LoadUnitNameQualifierAttribute,
value: this.name,
}];
for (const qualifier of defaultQualifier) {
QualifierUtil.addProtoQualifier(clazz, qualifier.attribute, qualifier.value);
}

}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
BizManager:
clients:
foo: {}
bar: {}
foo:
secret: '1'
bar:
secret: '2'

secret:
keys:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Inject, SingletonProto } from '@eggjs/core-decorator';
import { Inject, ModuleQualifier, SingletonProto } from '@eggjs/core-decorator';
import { Secret, SecretQualifier } from '../foo/Secret';

@SingletonProto()
export class App2 {
@Inject()
@SecretQualifier('app2')
@ModuleQualifier('app2')
@SecretQualifier('1')
secret: Secret;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import {
MultiInstanceInfo,
} from '@eggjs/tegg';
import { ModuleConfigUtil } from '@eggjs/tegg-common-util';
import { EggProtoImplClass, QualifierUtil } from '@eggjs/core-decorator';
import { EggProtoImplClass, LoadUnitNameQualifierAttribute, QualifierUtil } from '@eggjs/core-decorator';
import { Secret, SecretQualifierAttribute } from '../foo/Secret';

export const BizManagerQualifierAttribute = Symbol.for('Qualifier.ChatModel');
export const BizManagerQualifierAttribute = Symbol.for('Qualifier.BizManager');
export const BizManagerInjectName = 'bizManager';

export function BizManagerQualifier(chatModelName: string) {
Expand Down Expand Up @@ -41,6 +41,9 @@ export function BizManagerQualifier(chatModelName: string) {
properQualifiers: {
secret: [{
attribute: SecretQualifierAttribute,
value: clients[clientName].secret,
}, {
attribute: LoadUnitNameQualifierAttribute,
value: name,
}],
},
Expand Down
Loading