Skip to content

Commit

Permalink
Merge branch 'main' into renovate/chokidar-4.x
Browse files Browse the repository at this point in the history
  • Loading branch information
pmcelhaney authored Oct 2, 2024
2 parents 788471a + a585b91 commit 34925af
Show file tree
Hide file tree
Showing 16 changed files with 787 additions and 229 deletions.
5 changes: 5 additions & 0 deletions .changeset/fast-toys-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"counterfact": minor
---

If a path is defined at the root (/) it's mapped to index.ts
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# counterfact

## 1.0.2

### Patch Changes

- 2748fa2: fix: request body type is not found in OpenAPI 2 when consumes is in the root

## 1.0.1

### Patch Changes
Expand Down
5 changes: 2 additions & 3 deletions docs/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Copy the following command into your terminal. The only prerequisite is Node 16+.

```sh copy
npx counterfact@latest https://petstore3.swagger.io/api/v3/openapi.json api --open
npx counterfact@latest https://petstore3.swagger.io/api/v3/openapi.json api
```

## What does that command do?
Expand All @@ -12,8 +12,7 @@ npx counterfact@latest https://petstore3.swagger.io/api/v3/openapi.json api --op
2. reads an [OpenAPI 3](https://oai.github.io/Documentation/) document (`https://petstore3.swagger.io/api/v3/openapi.json`)
3. generates TypeScript files in the `api` directory
4. starts a server which implements the API
5. opens your browser to [Swagger UI](https://swagger.io/tools/swagger-ui/) (`--open`)

You can use Swagger to try out the auto-generated API. Out of the box, it returns random responses using metadata from the OpenAPI document. Edit the files under `./api/routes` to add more realistic behavior. There's no need to restart the server.
You can use [Swagger UI](https://swagger.io/tools/swagger-ui/) to try out the auto-generated API. Out of the box, it returns random responses using metadata from the OpenAPI document. Edit the files under `./api/routes` to add more realistic behavior. There's no need to restart the server.

To learn more, see the [Usage Guide](./usage.md).
4 changes: 2 additions & 2 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ See [Generated Code FAQ](./faq-generated-code.md) for details.
## Routing is where it's at 🔀
In the `routes` directory, you should find TypeScript files with code like the following.
In the `routes` directory, you should find a TypeScript file corresponding to each of the paths in your OpenAPI file. For example "/users/{userid}" will create `./routes/users/{userid}.ts`. (If you have a path for the root, "/", it will map to `./routes/index.ts`.) The contents of each file will look something like this:
```ts
export const GET: HTTP_GET = ($) => {
Expand All @@ -95,7 +95,7 @@ export const POST: HTTP_POST = ($) => {
};
```
The TypeScript file's path corresponds to the endpoint's URL. Each of the exported functions implements an HTTP request method (GET, POST, PUT, etc.). Each of these functions takes one argument -- `$` -- which is used to access request information, build a response, and interact with the server's state.
Each of the exported functions implements an HTTP request method (GET, POST, PUT, etc.). Each of these functions takes one argument -- `$` -- which is used to access request information, build a response, and interact with the server's state.

> [!TIP]
> If you're familiar with Express, `$` is sort of a combination of `req` and `res` with type safety and extra super powers.
Expand Down
14 changes: 14 additions & 0 deletions openapi-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ info:
title: Sample API
description: A sample API to illustrate OpenAPI concepts
paths:
/:
get:
description: the root
responses:
default:
description: root
content:
text/plain:
schema:
type: string
examples:
root:
value: This is the root.

/count:
get:
description: outputs the number of time each URL was visited
Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "counterfact",
"version": "1.0.1",
"version": "1.0.2",
"description": "a library for building a fake REST API for testing",
"type": "module",
"main": "./src/server/counterfact.js",
Expand Down Expand Up @@ -45,9 +45,9 @@
},
"devDependencies": {
"@changesets/cli": "2.27.8",
"@stryker-mutator/core": "8.5.0",
"@stryker-mutator/jest-runner": "8.5.0",
"@stryker-mutator/typescript-checker": "8.5.0",
"@stryker-mutator/core": "8.6.0",
"@stryker-mutator/jest-runner": "8.6.0",
"@stryker-mutator/typescript-checker": "8.6.0",
"@swc/core": "1.7.26",
"@swc/jest": "0.2.36",
"@testing-library/dom": "10.4.0",
Expand All @@ -57,9 +57,9 @@
"@types/koa-bodyparser": "4.3.12",
"@types/koa-proxy": "1.0.7",
"@types/koa-static": "4.0.4",
"@types/lodash": "4.17.7",
"@types/lodash": "4.17.9",
"copyfiles": "2.4.1",
"eslint": "9.10.0",
"eslint": "9.11.1",
"eslint-config-hardcore": "47.0.1",
"eslint-formatter-github-annotations": "0.1.0",
"eslint-import-resolver-typescript": "3.6.3",
Expand All @@ -74,7 +74,7 @@
"husky": "9.1.6",
"jest": "29.7.0",
"node-mocks-http": "1.16.0",
"nodemon": "3.1.4",
"nodemon": "3.1.7",
"rimraf": "6.0.1",
"stryker-cli": "1.0.2",
"supertest": "7.0.0",
Expand Down
1 change: 1 addition & 0 deletions src/server/module-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ export class ModuleLoader extends EventTarget {
);
}
} else {
if (url === "/index") this.registry.add("/", endpoint as Module);
this.registry.add(url, endpoint as Module);
}
} catch (error: unknown) {
Expand Down
3 changes: 2 additions & 1 deletion src/typescript-generator/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,10 @@ export async function generate(
paths.forEach((pathDefinition, key) => {
debug("processing path %s", key);

const path = key === "/" ? "/index" : key;
pathDefinition.forEach((operation, requestMethod) => {
repository
.get(`routes${key}.ts`)
.get(`routes${path}.ts`)
.export(new OperationCoder(operation, requestMethod, securitySchemes));
});
});
Expand Down
1 change: 1 addition & 0 deletions src/typescript-generator/operation-coder.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import nodePath from "node:path";

import { Coder } from "./coder.js";
import { OperationTypeCoder } from "./operation-type-coder.js";
import path from "node:path";

export class OperationCoder extends Coder {
constructor(requirement, requestMethod, securitySchemes = {}) {
Expand Down
20 changes: 12 additions & 8 deletions src/typescript-generator/operation-type-coder.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class OperationTypeCoder extends TypeCoder {
.replaceAll("~1", "/");

return `${nodePath
.join("types/paths", pathString)
.join("types/paths", pathString === "/" ? "/index" : pathString)
.replaceAll("\\", "/")}.types.ts`;
}

Expand Down Expand Up @@ -114,13 +114,17 @@ export class OperationTypeCoder extends TypeCoder {
script,
);

const bodyRequirement = this.requirement.get("consumes")
? parameters
.find((parameter) =>
["body", "formData"].includes(parameter.get("in").data),
)
.get("schema")
: this.requirement.select("requestBody/content/application~1json/schema");
const bodyRequirement =
this.requirement.get("consumes") ||
this.requirement.specification?.rootRequirement?.get("consumes")
? parameters
?.find((parameter) =>
["body", "formData"].includes(parameter.get("in").data),
)
?.get("schema")
: this.requirement.select(
"requestBody/content/application~1json/schema",
);

const bodyType =
bodyRequirement === undefined
Expand Down
24 changes: 24 additions & 0 deletions test/server/module-loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,30 @@ describe("a module loader", () => {
expect(registry.exists("GET", "/a/b/c")).toBe(true);
});
});

it("maps /index to /", async () => {
await usingTemporaryFiles(async ($) => {
await $.add(
"index.js",
`export function GET() {
return {
body: "GET from a/b/c"
};
}`,
);

await $.add("package.json", '{ "type": "module" }');

const registry: Registry = new Registry();
const loader: ModuleLoader = new ModuleLoader($.path(""), registry);

await loader.load();

expect(registry.exists("GET", "/index")).toBe(true);
expect(registry.exists("GET", "/")).toBe(true);
});
});

it("updates the registry when a file is deleted", async () => {
await usingTemporaryFiles(async ($) => {
await $.add(
Expand Down
Loading

0 comments on commit 34925af

Please sign in to comment.