Skip to content

Commit a17fa4d

Browse files
authored
Merge pull request #341 from ForgeRock/mock-api-v2-refactor
Mock api v2 refactor
2 parents aafdc89 + 83772e6 commit a17fa4d

60 files changed

Lines changed: 1548 additions & 1572 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

e2e/mock-api-v2/README.md

Lines changed: 60 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,93 @@
1-
### Mock Api
1+
A mock API server for simulating Ping Identity and OpenID Connect flows, built using [Effect](https://effect.website/) and [@effect/platform](https://github.com/Effect-TS/platform). This service is designed for end-to-end testing and development, providing realistic responses for authentication and authorization scenarios.
22

3-
## Docs
3+
## Features
44

5-
you can run the server and visit `http://localhost:9443/docs#` to visit the swagger docs
6-
The swagger docs are automatically created (with effect-http) by way of the schemas that are defined in the [spec file]('./src/spec.ts')
5+
- Implements endpoints for:
6+
- Healthcheck
7+
- OpenID Connect Discovery
8+
- Davinci Authorization
9+
- Token acquisition
10+
- UserInfo (protected, requires Bearer token)
711

8-
## Creating an endpoint in the Api specification
12+
- Uses Effect and @effect/platform for functional, type-safe API definition and handling.
13+
- Includes middleware for logging, CORS, cookie management, and Bearer token authorization.
914

10-
To create an endpoint, visit the [spec]('./src/spec.ts') file and add your endpoint. For organization and cleanliness, endpoints can be abstracted into the [endpoints]('./src/endpoints/') folder.
15+
## Tech Stack
1116

12-
When an endpoint is made, you can add it to the [`spec`]('./src/spec.ts') `pipe`.
17+
- [Effect](https://effect.website/)
18+
- [@effect/platform](https://github.com/Effect-TS/platform)
19+
- Node.js (via @effect/platform-node)
20+
- TypeScript
1321

14-
## Handling your new endpoint
22+
## Getting Started
1523

16-
When you have created an endpoint in the specification, you need to now handle the endpoint. This is the actual code implementation of your endpoint.
24+
### Install dependencies
1725

18-
handlers are saved in the [handlers]('./src/handlers/') folder.
19-
20-
You use RouterBuilder.handler, passing in the [api spec]('./src/spec.ts') and the name of the route, and then an Effect returning function (`Effect.gen` is the simplest form of this).
21-
22-
The request arguments, you define in your endpoint specification, (query params, path params, request bodies, etc) are the arguments passed to the callback function of `RouterBuilder.handler`.
23-
24-
Ensure that you also add your `handler` to the RouterBuilder.handle [here]('./src/main.ts');
25-
26-
## Adding a journey / flow to the response map
27-
28-
If you are adding a flow to the response map the first thing to do is open up [responseMap]('./src/responses/index.ts')
29-
30-
This file is a `map` of Names -> Array<Step> where a Step is the response you want to return (in order). Order is key here.
31-
32-
If the Response you want to return is not already defined as a schema, you will have to define a new Schema and add the response.
33-
34-
A schema is defined in the schemas folder [here]('./src/schemas/');
35-
A response is defined in the responses folder [here]('./src/responses/')
36-
37-
This is still a work in progress in terms of making it more scalable.
38-
39-
## Validating your code in Test
40-
41-
After adding a journey/flow to the response map and defining a schema, you next want to have some validation on the submitted request. You can do this by adding it to the `validator` function [here]('./src/helpers/match.ts');
42-
43-
This functions job is to `match` the type passed in, and validate based on the condition provided. If it passes, a boolean is returned, if it fails, a new Error should be returned.
44-
45-
## Services
46-
47-
To make it so types line up easier, each route, has a service dedicated to itself. The service under the hood, uses the `Requester` service. The `Requester` service is to mimic a call to the authorization server.
48-
49-
Let's look at the `Authorize` service. This service is the workhorse of the `authorize` handler.
26+
```sh
27+
pnpm install
28+
```
5029

51-
`Authorize`, the service, uses `Requester` which will fetch a response from the authorization server.
30+
### Build
5231

53-
After retrieving the response, the service will catch any errors that may be thrown, and mold them into HttpErrors to respond back to the client.
32+
```sh
33+
pnpm build
34+
```
5435

55-
In a mock environment, rather than fetching from the client, authorization service will grab the next response from the `responseMap`.
36+
### Run the server
5637

57-
In a live environment, it will forward a request to the Fetch service, and return that response.
38+
```sh
39+
pnpm serve
40+
```
5841

59-
## Creating Errors
42+
The server will start on port `9443`.
6043

61-
If you want to create an error, it is simple. This is the skeleton of how to create an Error in `Effect`
44+
### Run tests
6245

63-
```
64-
class MyErrorName {
65-
readonly _tag = 'MyErrorName'
66-
}
46+
```sh
47+
pnpm test
6748
```
6849

69-
The `_tag` is important as this is the name of the error, and how we can `catchTags` in our error handling. For simplicity, you can name is the same as your error class.
50+
## API Overview
7051

71-
## Handling Errors
52+
### Healthcheck
7253

73-
We want to return our errors back to the client, but typically we need an error response body that informs the client of the issue.
54+
- `GET /healthcheck`
55+
- Returns `"Healthy"` if the server is running.
7456

75-
You should add your error responses to the response folder [here]('./src/responses');
57+
### OpenID Connect Discovery
7658

77-
In the service where you want to handle your error, you will see a `catchTags` function.
59+
- `GET /:envid/as/.well-known/openid-configuration`
60+
- Returns a static OpenID configuration response.
7861

79-
Let's pause here to understand the `Effect` type.
62+
### Davinci Authorization
8063

81-
```ts
82-
Effect<Success, Error, Requirements>;
83-
```
64+
- `GET /:envid/as/authorize`
65+
- Accepts query parameters for authorization.
66+
- Returns a mock authorization response.
8467

85-
When reading an Effect type, the first generic, is what is returned if the effect is successful.
68+
### Token Endpoint
8669

87-
The second argument is what is returned if the effect is unsuccessful.
70+
- `POST /:envid/as/token`
71+
- Returns a mock access token response.
8872

89-
The third argument is any services (or layers) that are required to run this effect.
73+
### UserInfo (Protected)
9074

91-
So if we have an `effect` like this `Effect<Users, HttpError.HttpError | NoSuchElementException, never>`
75+
- `GET /:envid/as/userinfo`
76+
- Requires a valid Bearer token.
77+
- Returns mock user information.
9278

93-
This tells us the `effect` returns `users`, and can error two ways, `NoSuchElementException`, and with an `HttpError`.
79+
### End Session
9480

95-
We would rather handle this `NoSuchElementException` and send back to the client an HttpError informing them of the error that occurred.
81+
- `GET /:envid/as/endSession`
82+
- Ends the user's session.
83+
- Accepts `post_logout_redirect_uri` and `state` query parameters for redirects.
84+
- Returns a confirmation message.
9685

97-
We can do something like this now
98-
99-
```ts
100-
Effect.catchTag('NoSuchElementException', () =>
101-
HttpError.unauthorizedError('no such element found'),
102-
);
103-
```
86+
## Notes
10487

105-
This will return a 401 with that message.
88+
- The `customHtmlRoutes` and related endpoints are not currently implemented.
89+
- All endpoints return static or mock data for testing purposes.
10690

107-
When handling errors, we try to keep the handler always returning an `HttpError`, so we should handle any other errors we have deeper in the call stack, to return HttpError unless there is a valid reason to allow the error to bubble up.
91+
## License
10892

109-
If you have a shape of an error that you want to return from a handler, that does not match the current schema, you can add it to the `api spec`.
93+
MIT © Ping Identity Corporation

e2e/mock-api-v2/eslint.config.mjs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,9 @@ export default [
1010
rules: {
1111
'@typescript-eslint/no-empty-interface': 'off',
1212
'@typescript-eslint/no-empty-object-type': 'off',
13+
'require-yield': 'off',
1314
},
1415
},
15-
{
16-
files: ['**/*.ts', '**/*.tsx'],
17-
// Override or add rules here
18-
rules: {},
19-
},
2016
{
2117
files: ['**/*.js', '**/*.jsx'],
2218
// Override or add rules here

e2e/mock-api-v2/package.json

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,29 @@
44
"private": true,
55
"description": "",
66
"type": "module",
7-
"main": "./dist/index.js",
7+
"main": "./src/main.js",
88
"scripts": {
99
"build": "pnpm nx nxBuild",
10+
"dev": "node dist/src/main.js --watch-path=./",
1011
"lint": "pnpm nx nxLint",
11-
"serve": "node dist/e2e/mock-api-v2/src/main.js",
12-
"serve:dev": "nodemon dist/e2e/mock-api-v2/src/main.js",
12+
"serve": "node dist/src/main.js",
1313
"test": "pnpm nx nxTest"
1414
},
1515
"dependencies": {
16-
"@effect/language-service": "^0.2.0",
17-
"@effect/platform": "^0.58.27",
18-
"@effect/platform-node": "^0.53.26",
19-
"@effect/schema": "^0.68.23",
20-
"effect": "^3.12.7",
21-
"effect-http": "^0.73.0",
22-
"effect-http-node": "^0.27.0"
16+
"@effect/language-service": "catalog:effect",
17+
"@effect/opentelemetry": "0.53.1",
18+
"@effect/platform": "catalog:effect",
19+
"@effect/platform-node": "catalog:effect",
20+
"@opentelemetry/sdk-logs": "0.202.0",
21+
"@opentelemetry/sdk-metrics": "2.0.1",
22+
"@opentelemetry/sdk-trace-base": "2.0.1",
23+
"@opentelemetry/sdk-trace-node": "2.0.1",
24+
"@opentelemetry/sdk-trace-web": "2.0.1",
25+
"effect": "catalog:effect",
26+
"nanoid": "5.1.5"
2327
},
2428
"devDependencies": {
25-
"@effect/vitest": "^0.19.0"
29+
"@effect/vitest": "catalog:effect"
2630
},
2731
"nx": {
2832
"tags": ["scope:e2e"],

e2e/mock-api-v2/src/assets/.gitkeep

Whitespace-only changes.

e2e/mock-api-v2/src/endpoints/custom-html.endpoint.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

e2e/mock-api-v2/src/endpoints/davinci-authorize.endpoint.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.

e2e/mock-api-v2/src/endpoints/open-id-configuration.endpoint.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

e2e/mock-api-v2/src/endpoints/token.endpoint.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.

e2e/mock-api-v2/src/endpoints/userinfo.endpoint.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.

e2e/mock-api-v2/src/errors/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class FetchError {
1515
class InvalidProtectNode {
1616
readonly _tag = 'InvalidProtectNode';
1717
}
18+
1819
class UnableToFindNextStep {
1920
readonly _tag = 'UnableToFindNextStep';
2021
}

0 commit comments

Comments
 (0)