File: docs/CONTRIBUTING.md
Instructions for Logging Issues
1. Search for Duplicates
Search the existing issues before logging a new one.
Some search tips:
- Don't restrict your search to only open issues. An issue with a title similar to yours may have been closed as a duplicate of one with a less-findable title.
- Check for synonyms. For example, if your bug involves an interface, it likely also occurs with type aliases or classes.
- Search for the title of the issue you're about to log. This sounds obvious but 80% of the time this is sufficient to find a duplicate when one exists.
- Read more than the first page of results. Many bugs here use the same words so relevancy sorting is not particularly strong.
- If you have a crash, search for the first few topmost function names shown in the call stack.
2. Did you find a bug?
Please follow the issue template and include as much information as you can. This increases the speed of a fix being deployed because we need all of the information we can to be able to reproduce the bug first.
3. Do you have a suggestion?
We also accept suggestions in the issue tracker.
- Be sure to search first for a duplicate.
- Please ensure that your suggestion adheres to the goals and philosophy of the project
Instructions for Contributing Code
General
If you have a bugfix or new feature that you would like to contribute to tsoa, please find or open an issue about it first. Talk about what you would like to do. It may be that somebody is already working on it, or that there are particular issues that you should know about before implementing the change.
We enjoy working with contributors to get their code accepted. There are many approaches to fixing a problem and it is important to find the best approach before writing too much code.
In situations where the code is very hard to extend, we may want to refactor modules or redesign certain aspects of the project. The new design should be proposed and agreed upon in the attached issue. While we strongly value enhancements to code quality, significant refactoring should usually be reserved for situations where the non-refactored version of code would be too difficult to work with.
Contributing bug fixes
Your pull request should include a link to the bug that you are fixing. If you've submitted a PR for a bug, please post a comment in the bug to avoid duplication of effort.
Contributing features
Features (things that add new or improved functionality to tsoa) may be accepted, but will need to first be approved by having the ("help wanted" label by a tsoa project maintainer) in the suggestion issue.
Design changes will not be accepted at this time. If you have a design change proposal, please log a suggestion issue.
Information on the types of tests in tsoa
unit tests
You will find tests in tests\unit that test specific functions or classes within tsoa. If possible, it's ideal to add test coverage to these files since you can provide more detailed assertions about specific scenarios. These also run much more quickly. However, in some cases the only way to test tsoa (like in the case of reading a controller file) is run an integrative test. Read below:
integrative tests
These tests read controller files (just like tsoa does for real tsoa users) (via the tests\prepare.ts file) and then execute API tests against the generated routes. This allows tsoa contributors to validate that all of the code is connected well and is performing the desired function. These tests take longer to run but achieve higher levels of quality assurance. A good mix of integrative tests and unit tests will surely result in a faster review process for any contribution.
Housekeeping
Your pull request should:
- Include a description of what your change intends to do
- Be a child commit of a reasonably recent commit in the master branch
- Requests need not be a single commit, but should be a linear sequence of commits (i.e. no merge commits in your PR)
- It is desirable, but not necessary, for the tests to pass at each commit
- Have clear commit messages
- e.g. "Minor refactor in goToTypeDefinition", "Fix iterated type in for-await-of", "Add test for preserveWatchOutput on command line"
- Include adequate tests
- At least one test should fail in the absence of your non-test code changes. If your PR does not match this criteria, please specify why
- Tests should include reasonable permutations of the target fix/change
- Include baseline changes with your change
- All changed code must have some degree of code coverage (either integrative or unit level)
- To avoid line ending issues, set
autocrlf = inputandwhitespace = cr-at-eolin your git configuration
File: docs/ExternalInterfacesExplanation.MD
Why am I getting this error?
No matching model found for referenced type
If you get that error, the most likely cause is that you are asking TSOA to create a swagger model from an interface that lives in an external dependency.
Why is that a problem?
- Technical problem
- TSOA can't currently get interfaces from node_modules. For performance reasons, TSOA should not be crawling all of the files in node_modules.
- Architectural/Quality problem
- The consumers of the API you are writing do not want to have that API contract change on them. If your swagger/open-api documents/contracts were to be generated from external interfaces they would risk being changed any time that you update a library.
Solution:
Create an interface that lives inside your own code base (i.e. it does not live in ./node_modules) but that has the same structure.
Detailed Solution
Here's the code that's getting the No matching model found error:
import * as externalDependency from 'some-external-dependency';
@Route('Users')
export class UsersController {
/**
* Create a user
* @param request This is a user creation request description
*/
@Post()
public async Create(@Body() user: externalDependency.IUser): Promise<void> {
return externalDependency.doStuff(user);
}
}And that external dependency has the following code:
// node_modules/someExternalDependency/index.d.ts
export interface IUser {
name: string;
}
export function doStuff(user: IUser) {
// ...
}Here's how you solve it:
import * as externalDependency from 'some-external-dependency';
// a local copy:
interface IUserAbstraction {
name: string
}
@Route('Users')
export class UsersController {
/**
* Create a user
* @param request This is a user creation request description
*/
@Post()
public async Create(@Body() user: IUserAbstraction): Promise<void> {
return externalDependency.doStuff(user);
}
}Does that solution have any negatives?
While it may appear that having a duplicate interface may be a performance problem, the types are stripped out when TypeScript compiles. So there won't be a performance problem.
And you also don't have to worry about the duplicate interface acting as a replacement for the external library's interface. This will work just fine due to TypeScript's "structural subtyping." Here's an example.
Let's say you want to expose the IHuman interface because you need to call the onlyAcceptsAHuman function from a fictional NPM package called HumanLogger.
// node_modules/humanLogger/index.ts
export interface IHuman {
name: string;
}
export function onlyAcceptsAHuman(human: IHuman){
console.log(`I got a human with the name ${human.name}`
}And here's your local code that has its own interface that has a different name but the same structure. The fact that the following code compiles shows that the two interfaces can be substituted for each other due to structural subtyping:
import * as humanLogger from 'human-logger';
// your own code
interface IPerson {
name: string;
}
function makePerson(): IPerson {
return {
name: "Bob Smith"
};
}
humanLogger.onlyAcceptsAHuman( makePerson() ); // <-- yay! It compiles!Quality Benefits of this approach
Abstraction layers are a valuable concept in programming. Here's a classic example of why it will help you and your API consumers.
Let's say that some-external-dependency changes their method to take a different data structure.
If tsoa were to update your swagger documentation to the new type, then your API consumers would get 400 BAD REQUEST errors. See here:
// node_modules/someExternalDependency/index.d.ts
export interface IUser {
// name: string; // we used to accept "name" but now we don't
firstName: string;
lastName: string;
}
export function doStuff(user: IUser) {
// ...
}Now your swagger documentation has changed, but the API consumer hasn't been notified of the breaking contract. So they call the API:
const response = someHttpLib.post('/api/Users/', {
name: "Bob Smith"
}); // Throw 400 error: "firstName is a required parameter"We don't want our API consumers to have a new error pop up in their production applications, so instead we handle our internal change without having to bother our consumers:
import * as externalDependency from 'some-external-dependency';
// a local copy:
interface IUserAbstraction {
name: string
}
@Route('Users')
export class UsersController {
/**
* Create a user
* @param request This is a user creation request description
*/
@Post()
public async Create(@Body() user: IUserAbstraction): Promise<void> {
const namePortions = user.name.split(" ");
// translate the abstraction into the shape the library expects
const libUser: externalDependency.IUser = {
firstName: namePortions[0],
lastName: namePortions[1]
}
return externalDependency.doStuff(user);
}
}File: docs/ISSUE_TEMPLATE.md
Sorting
I'm submitting a ...
- bug report
- feature request
- support request
I confirm that I
- used the search to make sure that a similar issue hasn't already been submit
Expected Behavior
Current Behavior
Possible Solution
Steps to Reproduce
Context (Environment)
Version of the library:
Version of NodeJS:
- Confirm you were using yarn not npm: [ ]
Detailed Description
Breaking change?
File: docs/PULL_REQUEST_TEMPLATE.md
All Submissions:
- Have you followed the guidelines in our Contributing document?
- Have you checked to ensure there aren't other open Pull Requests for the same update/change?
- Have you written unit tests?
- Have you written unit tests that cover the negative cases (i.e.: if bad data is submitted, does the library respond properly)?
- This PR is associated with an existing issue?
Closing issues
Put closes #XXXX (where XXXX is the issue number) in your comment to auto-close the issue that your PR fixes.
If this is a new feature submission:
- Has the issue had a maintainer respond to the issue and clarify that the feature is something that aligns with the goals and philosophy of the project?
Potential Problems With The Approach
Test plan
File: packages/tsoa/README.MD
Goal
- TypeScript controllers and models as the single source of truth for your API
- A valid OpenAPI (formerly Swagger) spec (2.0 or 3.0 if you choose ๐) is generated from your controllers and models, including:
- Paths (e.g. GET /users)
- Definitions based on TypeScript interfaces (models)
- Parameters/model properties marked as required or optional based on TypeScript (e.g. myProperty?: string is optional in the OpenAPI spec)
- jsDoc supported for object descriptions (most other metadata can be inferred from TypeScript types)
- Routes are generated for middleware of choice
- Express, Hapi, and Koa currently supported, other middleware can be supported using a simple handlebars template
- Validate request payloads
Philosophy
- Rely on TypeScript type annotations to generate API metadata if possible
- If regular type annotations aren't an appropriate way to express metadata, use decorators
- Use jsdoc for pure text metadata (e.g. endpoint descriptions)
- Minimize boilerplate
- Models are best represented by interfaces (pure data structures), but can also be represented by classes
- Runtime validation of tsoa should behave as closely as possible to the specifications that the generated OpenAPI 2/3 schema describes. Any differences in validation logic are clarified by logging warnings during the generation of the OpenAPI Specification (OAS) and/or the routes.
- Please note that by enabling OpenAPI 3 you minimize the chances of divergent validation logic since OpenAPI 3 has a more expressive schema syntax.
Getting Started
Examples
Check out the guides
See example controllers in the tests
See example models in the tests
Help wanted
Contributing code
To contribute (via a PR), please first see the Contributing Guide
Becoming a maintainer
tsoa wants additional maintainers! The library has increased in popularity and has quite a lot of pull requests and issues. Please post in this issue if you're willing to take on the role of a maintainer.
File: packages/runtime/README.MD
This package contains the runtime helpers for tsoa.
For a comprehensive Readme, please refer to the main readme.
File: packages/cli/README.MD
This package contains the tsoa build time components.
For a comprehensive Readme, please refer to the main readme.