Skip to content

Commit

Permalink
Merge pull request #12445 from micalevisk/feat/disambiguate-provider-…
Browse files Browse the repository at this point in the history
…token-on-error

feat(core): disambiguate string-based providers from class-based providers on error message
  • Loading branch information
kamilmysliwiec authored Oct 23, 2023
2 parents 55c6a9a + 18fbf27 commit f3339a3
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,14 @@ describe('Optional factory provider deps', () => {
} catch (err) {
expect(err).to.be.instanceOf(UnknownDependenciesException);
expect(err.message).to
.equal(`Nest can't resolve dependencies of the POSSIBLY_MISSING_DEP (?). Please make sure that the argument MISSING_DEP at index [0] is available in the RootTestModule context.
.equal(`Nest can't resolve dependencies of the POSSIBLY_MISSING_DEP (?). Please make sure that the argument "MISSING_DEP" at index [0] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If MISSING_DEP is a provider, is it part of the current RootTestModule?
- If MISSING_DEP is exported from a separate @Module, is that module imported within RootTestModule?
- If "MISSING_DEP" is a provider, is it part of the current RootTestModule?
- If "MISSING_DEP" is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing MISSING_DEP */ ]
imports: [ /* the Module containing "MISSING_DEP" */ ]
})
`);
}
Expand Down
34 changes: 21 additions & 13 deletions packages/core/errors/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,31 @@ const getInstanceName = (instance: unknown): string => {
};

/**
* Returns the name of the dependency
* Returns the name of the dependency.
* Tries to get the class name, otherwise the string value
* (= injection token). As fallback it returns '+'
* (= injection token). As fallback to any falsy value for `dependency`, it
* returns `fallbackValue`
* @param dependency The name of the dependency to be displayed
* @param fallbackValue The fallback value if the dependency is falsy
* @param disambiguated Whether dependency's name is disambiguated with double quotes
*/
const getDependencyName = (dependency: InjectorDependency): string =>
const getDependencyName = (
dependency: InjectorDependency | undefined,
fallbackValue: string,
disambiguated = true,
): string =>
// use class name
getInstanceName(dependency) ||
// use injection token (symbol)
(isSymbol(dependency) && dependency.toString()) ||
// use string directly
(dependency as string) ||
(dependency
? disambiguated
? `"${dependency as string}"`
: (dependency as string)
: undefined) ||
// otherwise
'+';
fallbackValue;

/**
* Returns the name of the module
Expand All @@ -54,14 +65,9 @@ export const UNKNOWN_DEPENDENCIES_MESSAGE = (
unknownDependencyContext: InjectorDependencyContext,
module: Module,
) => {
const {
index,
name = 'dependency',
dependencies,
key,
} = unknownDependencyContext;
const { index, name, dependencies, key } = unknownDependencyContext;
const moduleName = getModuleName(module);
const dependencyName = getDependencyName(name);
const dependencyName = getDependencyName(name, 'dependency');

const potentialSolutions =
// If module's name is well defined
Expand Down Expand Up @@ -90,7 +96,9 @@ Potential solutions:
message += `. Please make sure that the "${key.toString()}" property is available in the current context.${potentialSolutions}`;
return message;
}
const dependenciesName = (dependencies || []).map(getDependencyName);
const dependenciesName = (dependencies || []).map(dependencyName =>
getDependencyName(dependencyName, '+', false),
);
dependenciesName[index] = '?';

message += ` (`;
Expand Down
22 changes: 22 additions & 0 deletions packages/core/test/errors/test/messages.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,28 @@ describe('Error Messages', () => {

expect(actualMessage).to.equal(expectedResult);
});
it('should display the provide token as double-quoted string for string-based tokens', () => {
const expectedResult =
stringCleaner(`Nest can't resolve dependencies of the CatService (?). Please make sure that the argument "FooRepository" at index [0] is available in the current context.
Potential solutions:
- If "FooRepository" is a provider, is it part of the current Module?
- If "FooRepository" is exported from a separate @Module, is that module imported within Module?
@Module({
imports: [ /* the Module containing "FooRepository" */ ]
})
`);

const actualMessage = stringCleaner(
new UnknownDependenciesException('CatService', {
index: 0,
dependencies: ['FooRepository'],
name: 'FooRepository',
}).message,
);

expect(actualMessage).to.equal(expectedResult);
});
it('should display the function name', () => {
const expectedResult =
stringCleaner(`Nest can't resolve dependencies of the CatService (?, CatFunction). Please make sure that the argument dependency at index [0] is available in the current context.
Expand Down

0 comments on commit f3339a3

Please sign in to comment.