Skip to content

Commit bb81839

Browse files
committed
chore: update-routes
1 parent 777e771 commit bb81839

28 files changed

Lines changed: 617 additions & 944 deletions

e2e/mock-api-v2/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
},
1515
"dependencies": {
1616
"@effect/language-service": "catalog:effect",
17-
"@effect/opentelemetry": "0.53.1",
17+
"@effect/opentelemetry": "catalog:effect",
1818
"@effect/platform": "catalog:effect",
1919
"@effect/platform-node": "catalog:effect",
2020
"@opentelemetry/sdk-logs": "0.202.0",

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
* This software may be modified and distributed under the terms
55
* of the MIT license. See the LICENSE file for details.
66
*/
7-
class InvalidUsernamePassword {
8-
readonly _tag = 'InvalidUsernamePassword';
9-
}
107

118
class FetchError {
129
readonly _tag = 'FetchError';
@@ -20,4 +17,4 @@ class UnableToFindNextStep {
2017
readonly _tag = 'UnableToFindNextStep';
2118
}
2219

23-
export { FetchError, InvalidUsernamePassword, InvalidProtectNode, UnableToFindNextStep };
20+
export { FetchError, InvalidProtectNode, UnableToFindNextStep };

e2e/mock-api-v2/src/handlers/authorize.handler.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,26 @@
66
*/
77
import { Effect } from 'effect';
88

9-
import { Authorize } from '../services/authorize.service.js';
109
import { MockApi } from '../spec.js';
11-
import { HttpApiBuilder } from '@effect/platform';
10+
import { HttpApiBuilder, HttpApiError } from '@effect/platform';
11+
import { getFirstElementAndRespond } from '../services/mock-env-helpers/index.js';
1212

1313
const AuthorizeHandlerMock = HttpApiBuilder.group(MockApi, 'Authorization', (handlers) =>
14-
handlers.handle('DavinciAuthorize', ({ urlParams }) =>
14+
handlers.handle('authorize', ({ urlParams }) =>
1515
Effect.gen(function* () {
16-
const { handleAuthorize } = yield* Authorize;
16+
/**
17+
* We expect an acr_value query parameter to be present in the request.
18+
* If it is not present, we return a 404 Not Found error.
19+
*/
20+
const acr_value = urlParams?.acr_values ?? '';
1721

18-
const response = yield* handleAuthorize(urlParams);
22+
if (!acr_value) {
23+
return yield* Effect.fail(new HttpApiError.NotFound());
24+
}
1925

20-
return response.body;
26+
const response = yield* getFirstElementAndRespond(urlParams);
27+
28+
return response;
2129
}).pipe(Effect.withSpan('DavinciAuthorize')),
2230
),
2331
);
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/**
2+
* Copyright (c) 2025 Ping Identity Corporation. All rights reserved.
3+
*
4+
* This software may be modified and distributed under the terms
5+
* of the MIT license. See the LICENSE file for details.
6+
*/
7+
import { Effect, pipe } from 'effect';
8+
import { MockApi } from '../spec.js';
9+
import {
10+
HttpApiBuilder,
11+
HttpApiError,
12+
HttpBody,
13+
HttpServerRequest,
14+
HttpServerResponse,
15+
} from '@effect/platform';
16+
import { responseMap } from '../responses/index.js';
17+
import { validator } from '../helpers/match.js';
18+
import { returnSuccessResponseRedirect } from '../responses/return-success-redirect.js';
19+
20+
const CapabilitiesHandlerMock = HttpApiBuilder.group(MockApi, 'Capabilities', (handlers) =>
21+
handlers.handle('capabilities', ({ urlParams, payload }) =>
22+
Effect.gen(function* () {
23+
/**
24+
* We expect an acr_value query parameter to be present in the request.
25+
* If it is not present, we return a 404 Not Found error.
26+
*/
27+
const acr_value = urlParams?.acr_values ?? '';
28+
29+
if (!acr_value) {
30+
return yield* Effect.fail(new HttpApiError.NotFound());
31+
}
32+
33+
/**
34+
* We need a step index cookie to determine which step of the authentication process we are on.
35+
* If the cookie is not present, we return a 404 Not Found error.
36+
*/
37+
38+
const req = yield* HttpServerRequest.HttpServerRequest;
39+
40+
const stepIndexCookie = req.cookies['stepIndex'];
41+
42+
/**
43+
* If we are here with no step index that means we can't continue through a flow.
44+
* We should error
45+
*/
46+
if (!stepIndexCookie) {
47+
return yield* Effect.fail(new HttpApiError.NotFound());
48+
}
49+
50+
const stepIndex = parseInt(stepIndexCookie);
51+
52+
/**
53+
* If we have no step index, we should error or if its an invalid number
54+
*/
55+
56+
if (isNaN(stepIndex) || stepIndex < 0) {
57+
return yield* Effect.fail(new HttpApiError.NotFound());
58+
}
59+
60+
/**
61+
* Match the body against validators now
62+
* if the body has no match, we are defaulting to a successful response.
63+
*/
64+
const result = yield* validator(payload);
65+
66+
if (result === false) {
67+
return yield* Effect.fail(new HttpApiError.Unauthorized());
68+
}
69+
70+
/**
71+
* We use the step index to find the next step in the response map.
72+
* If the step index is out of bounds, we return a 404 Not Found error.
73+
*/
74+
const steps = responseMap[acr_value];
75+
76+
/**
77+
* This may not be the best way to write this.
78+
* An alternative option would be for us to include the success response we want to return,
79+
* in the response map.
80+
*
81+
* then we can check if we are at the last step. if we are we write the cookie
82+
* and then we return the success response (last item in array)
83+
*
84+
* for now, this returns a default success response and writes cookies.
85+
*/
86+
if (stepIndex + 1 >= steps.length) {
87+
/**
88+
* we need to return a success because we have not failed yet,
89+
* and we have no more steps to process.
90+
*/
91+
const body = yield* HttpBody.json(returnSuccessResponseRedirect).pipe(
92+
/**
93+
* Decide on a better way to handle this error possibiltiy
94+
*/
95+
Effect.catchTag('HttpBodyError', () =>
96+
Effect.fail(
97+
new HttpApiError.HttpApiDecodeError({
98+
message: 'Failed to encode body',
99+
issues: [],
100+
}),
101+
),
102+
),
103+
);
104+
return pipe(
105+
HttpServerResponse.json(body),
106+
HttpServerResponse.setCookie('ST', 'MockApiCookie123'),
107+
HttpServerResponse.setCookie(
108+
'interactionId',
109+
returnSuccessResponseRedirect.interactionId,
110+
{
111+
httpOnly: true,
112+
secure: true,
113+
sameSite: 'strict',
114+
},
115+
),
116+
HttpServerResponse.setCookie(
117+
'interactionToken',
118+
returnSuccessResponseRedirect.interactionToken,
119+
{
120+
httpOnly: true,
121+
secure: true,
122+
sameSite: 'strict',
123+
},
124+
),
125+
HttpServerResponse.removeCookie('stepIndex'),
126+
HttpServerResponse.setStatus(200),
127+
HttpServerResponse.setHeader('Content-Type', 'application/json'),
128+
);
129+
}
130+
131+
/**
132+
* The stepIndex middleware is used to auto-increment the step index
133+
* based on the request type. If the step index is out of bounds,
134+
* we return a 404 Not Found error. so we won't increment it, but we check for the next step
135+
* in the flow.
136+
*/
137+
const nextStep = steps[stepIndex + 1];
138+
139+
return nextStep;
140+
}).pipe(Effect.withSpan('Capabilities Handler Mock')),
141+
),
142+
);
143+
144+
export { CapabilitiesHandlerMock };

e2e/mock-api-v2/src/handlers/custom-html-template.handler.ts

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

e2e/mock-api-v2/src/handlers/revoke.handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Tokens } from '../services/tokens.service.js';
99
import { HttpApiBuilder } from '@effect/platform';
1010
import { Effect } from 'effect';
1111

12-
const RevokeTokenHandler = HttpApiBuilder.group(MockApi, 'TokenRevocation', (handlers) =>
12+
const RevokeTokenHandler = HttpApiBuilder.group(MockApi, 'Revoke', (handlers) =>
1313
handlers.handle('RevokeToken', () =>
1414
Effect.gen(function* () {
1515
const { revokeToken } = yield* Tokens;

e2e/mock-api-v2/src/handlers/userinfo.handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { UserInfo } from '../services/userinfo.service.js';
1010
import { HttpApiBuilder } from '@effect/platform';
1111
import { BearerToken } from '../middleware/Authorization.js';
1212

13-
const UserInfoMockHandler = HttpApiBuilder.group(MockApi, 'Protected Requests', (handlers) =>
13+
const UserInfoMockHandler = HttpApiBuilder.group(MockApi, 'ProtectedRequests', (handlers) =>
1414
handlers.handle('UserInfo', () =>
1515
Effect.gen(function* () {
1616
const authToken = yield* BearerToken;

e2e/mock-api-v2/src/helpers/match.ts

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@
66
*/
77
import { Effect, Match, Schema } from 'effect';
88

9-
import { InvalidUsernamePassword, InvalidProtectNode } from '../errors/index.js';
10-
import { PingOneCustomHtmlRequestBody } from '../schemas/custom-html-template/custom-html-template-request.schema.js';
9+
import { HttpApiError } from '@effect/platform';
10+
import { CapabilitiesRequestBody } from '../schemas/capabilities/capabilities.request.schema.js';
1111

12-
type PingRequestData = Schema.Schema.Type<
13-
typeof PingOneCustomHtmlRequestBody
14-
>['parameters']['data']['formData']['value'];
12+
type PingRequestData = Schema.Schema.Type<typeof CapabilitiesRequestBody>;
1513
/**
1614
* Using this to match on the data types, realistically, this will be a schema of possible
1715
* response bodies we want to validate against they validate to our conditions.
@@ -20,18 +18,19 @@ type PingRequestData = Schema.Schema.Type<
2018
* or we can continue to the next step in the flow
2119
*/
2220
const validator = Match.type<PingRequestData>().pipe(
23-
Match.when({ username: Match.string, password: Match.string }, ({ username, password }) => {
24-
return Effect.if(username == 'testuser' && password === 'Password', {
25-
onFalse: () => Effect.fail(new InvalidUsernamePassword()),
26-
onTrue: () => Effect.succeed(true),
27-
});
28-
}),
29-
Match.when({ pingprotectsdk: Match.string }, ({ pingprotectsdk }) => {
30-
return Effect.if(pingprotectsdk.length > 1, {
31-
onTrue: () => Effect.succeed(true),
32-
onFalse: () => Effect.fail(new InvalidProtectNode()),
33-
});
34-
}),
35-
Match.exhaustive,
21+
Match.when(
22+
{ parameters: { data: { formData: { username: Match.string, password: Match.string } } } },
23+
({ parameters }) =>
24+
Effect.if(
25+
parameters.data.formData.username == 'testuser' &&
26+
parameters.data.formData.password === 'Password',
27+
{
28+
onFalse: () => Effect.fail(new HttpApiError.Unauthorized()),
29+
onTrue: () => Effect.succeed(true),
30+
},
31+
),
32+
),
33+
Match.orElse(() => Effect.succeed(true)),
3634
);
35+
3736
export { validator, PingRequestData };

e2e/mock-api-v2/src/index.css

Whitespace-only changes.

e2e/mock-api-v2/src/main.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { HealthCheckLive } from './handlers/healthcheck.handler.js';
1313
import { OpenidConfigMock } from './handlers/open-id-configuration.handler.js';
1414
import { IncrementStepIndexMock } from './middleware/CookieMiddleware.js';
1515
import { AuthorizeHandlerMock } from './handlers/authorize.handler.js';
16-
import { AuthorizeMock } from './services/authorize.service.js';
16+
import { CapabilitiesHandlerMock } from './handlers/capabilities.handler.js';
1717
import { TokensMock } from './services/tokens.service.js';
1818
import { TokensHandler } from './handlers/token.handler.js';
1919
import { UserInfoMockHandler } from './handlers/userinfo.handler.js';
@@ -26,6 +26,15 @@ import { BatchSpanProcessor, ConsoleSpanExporter } from '@opentelemetry/sdk-trac
2626
import { EndSessionHandlerMock } from './handlers/end-session.handler.js';
2727
import { RevokeTokenHandler } from './handlers/revoke.handler.js';
2828

29+
const Services = [
30+
Layer.provide(TokensMock),
31+
Layer.provide(IncrementStepIndexMock),
32+
Layer.provide(AuthorizationMock),
33+
Layer.provide(UserInfoMockService),
34+
Layer.provide(SessionMiddlewareMock),
35+
Layer.provide(SessionStorage.Default),
36+
] as const;
37+
2938
const NodeSdkLive = NodeSdk.layer(() => ({
3039
resource: { serviceName: 'Mock-Api' },
3140
spanProcessor: new BatchSpanProcessor(new ConsoleSpanExporter()),
@@ -36,22 +45,15 @@ const APIMock = HttpApiBuilder.api(MockApi).pipe(
3645
Layer.provide(OpenidConfigMock),
3746
Layer.provide(AuthorizeHandlerMock),
3847
Layer.provide(TokensHandler),
48+
Layer.provide(CapabilitiesHandlerMock),
3949
Layer.provide(UserInfoMockHandler),
4050
Layer.provide(EndSessionHandlerMock),
4151
Layer.provide(RevokeTokenHandler),
52+
...Services,
4253
);
4354

4455
const ServerMock = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(
4556
Layer.provide(HttpApiSwagger.layer()),
46-
Layer.provide(APIMock),
47-
Layer.provide(TokensMock),
48-
Layer.provide(IncrementStepIndexMock),
49-
Layer.provide(AuthorizationMock),
50-
Layer.provide(UserInfoMockService),
51-
Layer.provide(SessionMiddlewareMock),
52-
Layer.provide(SessionStorage.Default),
53-
Layer.provide(AuthorizeMock),
54-
Layer.provide(NodeSdkLive),
5557
Layer.provide(
5658
HttpApiBuilder.middlewareCors({
5759
allowedMethods: ['GET', 'PUT', 'POST', 'OPTIONS'],
@@ -60,6 +62,9 @@ const ServerMock = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(
6062
maxAge: 3600,
6163
}),
6264
),
65+
Layer.provide(APIMock),
66+
67+
Layer.provide(NodeSdkLive),
6368
HttpServer.withLogAddress,
6469
Layer.provide(NodeHttpServer.layer(createServer, { port: 9443 })),
6570
);

0 commit comments

Comments
 (0)