Closed Esya closed 5 years ago
Generally with these sorts of conflicts, the best bet is to use the "ignore" configuration. From a project I use:
{
"swagger": {
"outputDirectory": "./dist",
"entryFile": "./src/server.ts",
"basePath": "/api",
"name": "-----"
},
"routes": {
"basePath": "/api",
"entryFile": "./src/server.ts",
"routesDir": "./src/api/routes"
},
"ignore": [
"**/node_modules/**"
]
}
I've tried using this :
"ignore": [
"**/node_modules/sequelize/**",
"**/node_modules/sequelize-typescript/**"
]
But to no avail; the error becomes :
There was a problem resolving type of 'Model'.
There was a problem resolving type of 'User'.
Generate swagger error.
Error: No matching model found for referenced type Model.
at new GenerateMetadataError (/home/esya/Documents/Git/microservice-tsoa-working/node_modules/tsoa/dist/metadataGeneration/exceptions.js:17:28)
at getModelTypeDeclaration (/home/esya/Documents/Git/microservice-tsoa-working/node_modules/tsoa/dist/metadataGeneration/resolveType.js:419:15)
at getReferenceType (/home/esya/Documents/Git/microservice-tsoa-working/node_modules/tsoa/dist/metadataGeneration/resolveType.js:280:25)
at /home/esya/Documents/Git/microservice-tsoa-working/node_modules/tsoa/dist/metadataGeneration/resolveType.js:572:33
at Array.forEach (<anonymous>)
at /home/esya/Documents/Git/microservice-tsoa-working/node_modules/tsoa/dist/metadataGeneration/resolveType.js:570:22
at Array.forEach (<anonymous>)
at getModelInheritedProperties (/home/esya/Documents/Git/microservice-tsoa-working/node_modules/tsoa/dist/metadataGeneration/resolveType.js:566:21)
at getReferenceType (/home/esya/Documents/Git/microservice-tsoa-working/node_modules/tsoa/dist/metadataGeneration/resolveType.js:283:35)
at resolveType (/home/esya/Documents/Git/microservice-tsoa-working/node_modules/tsoa/dist/metadataGeneration/resolveType.js:95:25)
Model
is defined in an ignored folder (sequelize-typescript). User extends Model, but we only need the properties of User. Do you see any reason why it would not be ignored?
I'm encountering the exact same issue.
The problem here, I think, is that tsoa actually needs to know about Model in order to correctly form the definition for User.
Depending on the structure of sequelize, you may be able to ignore everything except the file that Model is in, but I realize that's not a great solution. I think we're going to need to some way to mark a model as the "real" matching model.
Just a follow up on this - do you get 'No matching model found for referenced type SequelizeOrigin.' even without any ignore setting in tsoa.json? Is it failing to find matching models because that directory is ignored?
If we are ignoring that directory, then there's a new feature where you can mark a model as the designated model with @tsoaModel which should reduce the need to ignore external libraries.
Could this be solved with an interface? From the code above:
export default class User extends Model<User>
what happens if we change this to:
export default class User extends Model<User> implements IUser
and changing your route to match:
public userJson(@Request() request: express.Request): IUser {
Then you can define IUser to be just the parts of Model
I'm running into similar issue but slightly different scenario where my Typescript types are generated from a protobuffer file.
Protobuf file:
syntax = "proto3";
package "mypackage.test";
message User {
string id = 1;
}
The generated code (using protobufjs
) looks like below:
compiled.d.ts:
export namespace mypackage {
namespace test {
interface IUser {
id: string;
}
}
}
The generated code is in organized into its own npm package, @hien/domain
.
and used in the controller as:
import proto from "@hien/domain"
class Test extends Controller {
@Get()
public async get(): proto.mypackage.test.IUser {
...
}
}
I get
Error: No matching module declarations found for proto.
This issue seems related but I can create a separate issue if needed.
Thanks!
I was able to resolve this by extending the interface to a class, then decorating the interface with the @tsoaModel reference. NOTE: the property in question had to be moved out of the interface and into the class for this to work. While admittedly this does go against the purpose of using an interface as a contract for a class (and for anyone who only uses interfaces it may be a dealbreaker), this at least works as an immediate solution until a better one is implemented.
Here are the specifics:
CASE: I have a User interface with a property of type CollectionReference
(from the node_modules reference to Google Firestore types). When running 'tsoa routes' I would get the error "No matching model found for referenced type CollectionReference."
SOLUTION: Extending the User interface with a User class, I moved the 'CollectionReference' property into the class. Then I decorated the interface with @tsoaModel
/**
* @tsoaModel
*/
export interface User {
uid: string;
email: string;
password: string;
displayName?: string;
phoneNumber?: string;
photoURL: string
}
export class User implements User{
subUsers: CollectionReference
}
Also important to note that this will exclude the property from your api doc, but for my situation it is acceptable since the CollectionReference is a Firestore property which is processed internally, and cannot be passed in or returned as it would warrant the inclusion of all associated Firebase subtypes into my docs
This seems reasonable and expected.
Database objects probably shouldn't be exposed to the rest API. Typically there is a process in the controller that maps the lightweight serializeable json view models to heavyweight domain objects.
To tightly couple the rest layer interface with the domain level data model would break both SOLID principles and standard domain-driven design.
I'm no authority here, but aside from @hienqnguyen (who has a separate issue from the reported one. I suspect tsoa cannot find the reference to his auto-generated models) I don't see any actionable item in this particular issue
Database objects probably shouldn't be exposed to the rest API. Typically there is a process in the controller that maps the lightweight serializeable json view models to heavyweight domain objects.
I agree.
The solution is to not expose any interfaces from dependencies in your swagger/tsoa models. Basically, just make a copy of the interfaces you got from other libraries and use those instead. Trust me it will work (see this explanation here on "structural subtyping" as to why this works).
Hi,
We're having some serious issues with our Sequelize models and the tsoa swagger/routes generation. The metadata generator fails when we do something as simple as this :
Where User is a simple Sequelize model like so :
When we try generating the routes we get :
We've found a very very nasty work-around which is to block the typedetection when it reaches "Model" or "SequelizeOrigin" but that means a fork of tsoa with a quick & dirty fix.
Would you have a solution for this? Thanks!