-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #450 from ForgeRock/mock-api-v2
Mock api v2
- Loading branch information
Showing
62 changed files
with
3,780 additions
and
159 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"extends": ["../../.eslintrc.json"], | ||
"ignorePatterns": ["!**/*", "README.md", ".DS_Store"], | ||
"overrides": [ | ||
{ | ||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"], | ||
"rules": { | ||
"@typescript-eslint/no-empty-interface": [ | ||
"error", | ||
{ | ||
"allowSingleExtends": true | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"files": ["*.ts", "*.tsx"], | ||
"rules": {} | ||
}, | ||
{ | ||
"files": ["*.js", "*.jsx"], | ||
"rules": {} | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
### Mock Api | ||
|
||
## Docs | ||
|
||
you can run the server and visit `http://localhost:9443/docs#` to visit the swagger docs | ||
The swagger docs are automatically created (with effect-http) by way of the schemas that are defined in the [spec file]('./src/spec.ts') | ||
|
||
## Creating an endpoint in the Api specification | ||
|
||
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. | ||
|
||
When an endpoint is made, you can add it to the [`spec`]('./src/spec.ts') `pipe`. | ||
|
||
## Handling your new endpoint | ||
|
||
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. | ||
|
||
handlers are saved in the [handlers]('./src/handlers/') folder. | ||
|
||
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). | ||
|
||
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`. | ||
|
||
Ensure that you also add your `handler` to the RouterBuilder.handle [here]('./src/main.ts'); | ||
|
||
## Adding a journey / flow to the response map | ||
|
||
If you are adding a flow to the response map the first thing to do is open up [responseMap]('./src/responses/index.ts') | ||
|
||
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. | ||
|
||
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. | ||
|
||
A schema is defined in the schemas folder [here]('./src/schemas/'); | ||
A response is defined in the responses folder [here]('./src/responses/') | ||
|
||
This is still a work in progress in terms of making it more scalable. | ||
|
||
## Validating your code in Test | ||
|
||
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'); | ||
|
||
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. | ||
|
||
## Services | ||
|
||
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. | ||
|
||
Let's look at the `Authorize` service. This service is the workhorse of the `authorize` handler. | ||
|
||
`Authorize`, the service, uses `Requester` which will fetch a response from the authorization server. | ||
|
||
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. | ||
|
||
In a mock environment, rather than fetching from the client, authorization service will grab the next response from the `responseMap`. | ||
|
||
In a live environment, it will forward a request to the Fetch service, and return that response. | ||
|
||
## Creating Errors | ||
|
||
If you want to create an error, it is simple. This is the skeleton of how to create an Error in `Effect` | ||
|
||
``` | ||
class MyErrorName { | ||
readonly _tag = 'MyErrorName' | ||
} | ||
``` | ||
|
||
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. | ||
|
||
## Handling Errors | ||
|
||
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. | ||
|
||
You should add your error responses to the response folder [here]('./src/responses'); | ||
|
||
In the service where you want to handle your error, you will see a `catchTags` function. | ||
|
||
Let's pause here to understand the `Effect` type. | ||
|
||
```ts | ||
Effect<Success, Error, Requirements>; | ||
``` | ||
|
||
When reading an Effect type, the first generic, is what is returned if the effect is successful. | ||
|
||
The second argument is what is returned if the effect is unsuccessful. | ||
|
||
The third argument is any services (or layers) that are required to run this effect. | ||
|
||
So if we have an `effect` like this `Effect<Users, HttpError.HttpError | NoSuchElementException, never>` | ||
|
||
This tells us the `effect` returns `users`, and can error two ways, `NoSuchElementException`, and with an `HttpError`. | ||
|
||
We would rather handle this `NoSuchElementException` and send back to the client an HttpError informing them of the error that occurred. | ||
|
||
We can do something like this now | ||
|
||
```ts | ||
Effect.catchTag('NoSuchElementException', () => | ||
HttpError.unauthorizedError('no such element found'), | ||
); | ||
``` | ||
|
||
This will return a 401 with that message. | ||
|
||
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. | ||
|
||
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`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
{ | ||
"name": "e2e-mock-api-v2", | ||
"$schema": "../../node_modules/nx/schemas/project-schema.json", | ||
"sourceRoot": "e2e/mock-api-v2/src", | ||
"projectType": "application", | ||
"tags": ["e2e"], | ||
"targets": { | ||
"build": { | ||
"executor": "@nx/esbuild:esbuild", | ||
"outputs": ["{options.outputPath}"], | ||
"defaultConfiguration": "production", | ||
"options": { | ||
"platform": "node", | ||
"outputPath": "dist/e2e/mock-api-v2", | ||
"format": ["cjs"], | ||
"bundle": false, | ||
"main": "e2e/mock-api-v2/src/main.ts", | ||
"tsConfig": "e2e/mock-api-v2/tsconfig.app.json", | ||
"assets": ["e2e/mock-api-v2/src/assets"], | ||
"generatePackageJson": false, | ||
"esbuildOptions": { | ||
"sourcemap": true, | ||
"outExtension": { | ||
".js": ".js" | ||
} | ||
} | ||
}, | ||
"configurations": { | ||
"development": { | ||
"watch": true | ||
}, | ||
"production": { | ||
"esbuildOptions": { | ||
"sourcemap": false, | ||
"outExtension": { | ||
".js": ".js" | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
"serve": { | ||
"executor": "@nx/js:node", | ||
"defaultConfiguration": "development", | ||
"options": { | ||
"buildTarget": "e2e-mock-api-v2:build" | ||
}, | ||
"configurations": { | ||
"development": { | ||
"buildTarget": "e2e-mock-api-v2:build:development" | ||
}, | ||
"production": { | ||
"buildTarget": "e2e-mock-api-v2:build:production" | ||
} | ||
} | ||
}, | ||
"lint": { | ||
"executor": "@nx/eslint:lint" | ||
}, | ||
"test": { | ||
"executor": "@nx/vite:test", | ||
"outputs": ["{options.reportsDirectory}"], | ||
"options": { | ||
"reportsDirectory": "../../coverage/e2e/mock-api-v2" | ||
}, | ||
"configurations": { | ||
"watch": { | ||
"watch": true | ||
} | ||
} | ||
} | ||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Schema } from '@effect/schema'; | ||
import { pipe } from 'effect'; | ||
import { Api, ApiResponse } from 'effect-http'; | ||
|
||
import { | ||
DavinciAuthorizeHeaders, | ||
DavinciAuthorizeResponseHeaders, | ||
} from '../schemas/authorize.schema'; | ||
import { | ||
PingOneCustomHtmlRequestBody, | ||
PingOneRequestQuery, | ||
} from '../schemas/custom-html-template/custom-html-template-request.schema'; | ||
import { | ||
PingOneCustomHtmlResponseBody, | ||
PingOneCustomHtmlResponseErrorBody, | ||
PingOnePathParams, | ||
} from '../schemas/custom-html-template/custom-html-template-response.schema'; | ||
import { SuccessResponseRedirect } from '../schemas/return-success-response-redirect.schema'; | ||
|
||
const customHtmlEndPoint = Api.addEndpoint( | ||
pipe( | ||
Api.post( | ||
'PingOneCustomHtml', | ||
'/:envid/davinci/connections/:connectionid/capabilities/customHTMLTemplate', | ||
).pipe( | ||
Api.setRequestPath(PingOnePathParams), | ||
Api.setRequestQuery(PingOneRequestQuery), | ||
Api.setRequestBody(PingOneCustomHtmlRequestBody), | ||
|
||
Api.setRequestHeaders(DavinciAuthorizeHeaders), | ||
|
||
Api.setResponseBody(Schema.Union(PingOneCustomHtmlResponseBody, SuccessResponseRedirect)), | ||
Api.setResponseHeaders(DavinciAuthorizeResponseHeaders), | ||
|
||
Api.addResponse( | ||
ApiResponse.make(401, Schema.Union(PingOneCustomHtmlResponseErrorBody, Schema.String)), | ||
), | ||
Api.addResponse(ApiResponse.make(403, Schema.String)), | ||
), | ||
), | ||
); | ||
|
||
export { customHtmlEndPoint }; |
28 changes: 28 additions & 0 deletions
28
e2e/mock-api-v2/src/endpoints/davinci-authorize.endpoint.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Schema } from '@effect/schema'; | ||
import { pipe } from 'effect'; | ||
import { Api } from 'effect-http'; | ||
|
||
import { | ||
AuthorizePath, | ||
DavinciAuthorizeHeaders, | ||
DavinciAuthorizeQuery, | ||
DavinciAuthorizeResponseHeaders, | ||
} from '../schemas/authorize.schema'; | ||
import { PingOneCustomHtmlResponseBody } from '../schemas/custom-html-template/custom-html-template-response.schema'; | ||
import { SuccessResponseRedirect } from '../schemas/return-success-response-redirect.schema'; | ||
|
||
const davinciAuthorize = Api.addEndpoint( | ||
pipe( | ||
Api.get('DavinciAuthorize', '/:envid/as/authorize').pipe( | ||
Api.setRequestPath(AuthorizePath), | ||
Api.setRequestQuery(DavinciAuthorizeQuery), | ||
Api.setRequestHeaders(DavinciAuthorizeHeaders), | ||
), | ||
|
||
Api.setResponseBody(Schema.Union(PingOneCustomHtmlResponseBody, SuccessResponseRedirect)), | ||
Api.setResponseHeaders(DavinciAuthorizeResponseHeaders), | ||
Api.setResponseStatus(200), | ||
), | ||
); | ||
|
||
export { davinciAuthorize }; |
16 changes: 16 additions & 0 deletions
16
e2e/mock-api-v2/src/endpoints/open-id-configuration.endpoint.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Schema } from '@effect/schema'; | ||
import { pipe } from 'effect'; | ||
import { Api } from 'effect-http'; | ||
import { openIdConfigurationResponseSchema } from '../schemas/open-id-configuration/open-id-configuration-response.schema'; | ||
|
||
const openidConfiguration = Api.addEndpoint( | ||
pipe( | ||
Api.get('openidConfiguration', '/:envid/as/.well-known/openid-configuration').pipe( | ||
Api.setRequestPath(Schema.Struct({ envid: Schema.String })), | ||
Api.setResponseBody(openIdConfigurationResponseSchema), | ||
Api.setResponseStatus(200), | ||
), | ||
), | ||
); | ||
|
||
export { openidConfiguration }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { Schema } from '@effect/schema'; | ||
import { pipe } from 'effect'; | ||
import { Api } from 'effect-http'; | ||
|
||
import { TokenResponseBody } from '../schemas/token/token.schema'; | ||
|
||
const pingOneToken = Api.addEndpoint( | ||
pipe( | ||
Api.post('PingOneToken', '/:envid/as/token').pipe( | ||
Api.setRequestPath(Schema.Struct({ envid: Schema.String })), | ||
Api.setRequestBody(Api.FormData), | ||
|
||
// Responses | ||
Api.setResponseBody(TokenResponseBody), | ||
Api.setResponseStatus(200), | ||
), | ||
), | ||
); | ||
|
||
export { pingOneToken }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Schema } from '@effect/schema'; | ||
import { pipe } from 'effect'; | ||
import { Api, ApiResponse, Security } from 'effect-http'; | ||
|
||
import { UserInfoSchema } from '../schemas/userinfo/userinfo.schema'; | ||
|
||
const userInfo = Api.addEndpoint( | ||
pipe( | ||
Api.get('UserInfo', '/:envid/as/userinfo').pipe( | ||
Api.setRequestPath(Schema.Struct({ envid: Schema.String })), | ||
Api.setSecurity(Security.bearer({})), | ||
Api.setResponseStatus(200), | ||
Api.setResponseBody(UserInfoSchema), | ||
Api.addResponse(ApiResponse.make(401, Schema.String)), | ||
), | ||
), | ||
); | ||
|
||
export { userInfo }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
class InvalidUsernamePassword { | ||
readonly _tag = 'InvalidUsernamePassword'; | ||
} | ||
|
||
class FetchError { | ||
readonly _tag = 'FetchError'; | ||
} | ||
|
||
class InvalidProtectNode { | ||
readonly _tag = 'InvalidProtectNode'; | ||
} | ||
class UnableToFindNextStep { | ||
readonly _tag = 'UnableToFindNextStep'; | ||
} | ||
|
||
export { FetchError, InvalidUsernamePassword, InvalidProtectNode, UnableToFindNextStep }; |
Oops, something went wrong.