Skip to content
This repository has been archived by the owner on Aug 18, 2024. It is now read-only.

Commit

Permalink
Improvement around type assersion and the error message
Browse files Browse the repository at this point in the history
  • Loading branch information
yasaichi committed Mar 21, 2024
1 parent dc62c40 commit 414acc8
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 39 deletions.
6 changes: 3 additions & 3 deletions libs/nestjs-kiota/src/NestRequestAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe(NestRequestAdapter.name, () => {
bar: 'bar',
};
const baseUrl = 'https://example.com';
const defaultApiError = new DefaultApiError();
const defaultApiError = new DefaultApiError('unexpected error type');
defaultApiError.responseStatusCode = 401;
const primitiveResponseModel = 42;
const responseModel = { foo: 'foo' };
Expand Down Expand Up @@ -130,7 +130,7 @@ describe(NestRequestAdapter.name, () => {
error.getStatus(),
defaultApiError.responseStatusCode,
);
assert.deepEqual(error.getResponse(), {});
assert.deepEqual(error.getResponse(), defaultApiError.message);

return true;
},
Expand Down Expand Up @@ -223,7 +223,7 @@ describe(NestRequestAdapter.name, () => {
error.getStatus(),
defaultApiError.responseStatusCode,
);
assert.deepEqual(error.getResponse(), {});
assert.deepEqual(error.getResponse(), defaultApiError.message);

return true;
},
Expand Down
69 changes: 36 additions & 33 deletions libs/nestjs-kiota/src/NestRequestAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ import {
type RequestInformation,
} from '@microsoft/kiota-abstractions';
import { HttpException } from '@nestjs/common';
import { identity, Match } from 'effect';
import { Data, identity, Match, Predicate } from 'effect';
import type { SetNonNullable } from 'type-fest';

// NOTE: Deserialized and thrown error actually has only `additionalData`, `responseHeaders`,
// `responseStatusCode`, and the response body. For further details, see the following lines:
// https://github.com/microsoft/kiota-typescript/blob/5ce4f291d27a8142a6df0716a38e06a765fb6563/packages/http/fetch/src/fetchRequestAdapter.ts#L343-L357
export type DeserializedApiError =
& Pick<ApiError, 'responseHeaders' | 'responseStatusCode'>
& AdditionalDataHolder
& { [key: string]: unknown };
& SetNonNullable<Pick<ApiError, 'responseHeaders' | 'responseStatusCode'>>
& Record<string, unknown>;

export class NestRequestAdapter implements RequestAdapter {
constructor(private readonly requestAdapter: RequestAdapter) {}
Expand Down Expand Up @@ -125,42 +129,41 @@ export class NestRequestAdapter implements RequestAdapter {
} catch (error) {
throw Match.value(error).pipe(
Match.when(
Match.instanceOf(DefaultApiError),
(
{
name: _name,
message: _message,
stack: _stack,
cause: _cause,
...apiError
},
) => this.convertToNestHttpException(apiError),
),
Match.when(
{
responseStatusCode: Match.number,
responseHeaders: Match.any,
},
(apiError) =>
this.convertToNestHttpException({
...apiError,
// NOTE: The following line is only for passing the type check
responseHeaders: undefined,
}),
this.isApiError,
(apiError) => this.convertToNestHttpException(apiError),
),
Match.orElse(identity),
);
}
}

private convertToNestHttpException(apiError: DeserializedApiError) {
const {
additionalData: _additionalData,
responseHeaders: _responseHeaders,
responseStatusCode = 500,
...body
} = apiError;

return new HttpException(body, responseStatusCode, { cause: apiError });
const [response, status] = Match.value(apiError).pipe(
Match.when(
Match.instanceOf(DefaultApiError),
({ message, responseStatusCode }) =>
Data.tuple(message, responseStatusCode!),
),
Match.orElse(
(
{
additionalData: _additionalData,
responseHeaders: _responseHeaders,
responseStatusCode,
...responseBody
},
) => Data.tuple(responseBody, responseStatusCode),
),
);

return new HttpException(response, status, { cause: apiError });
}

private isApiError(input: unknown): input is DeserializedApiError {
return Predicate.isRecord(input) &&
Predicate.hasProperty(input, 'responseHeaders') &&
Predicate.isRecord(input.responseHeaders) &&
Predicate.hasProperty(input, 'responseStatusCode') &&
Predicate.isNumber(input.responseStatusCode);
}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"@nestjs/platform-express": "^10.0.0",
"effect": "^2.4.0",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
"rxjs": "^7.8.1",
"type-fest": "^4.13.1"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/effective-users/effective-users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
Injectable,
NotFoundException,
} from '@nestjs/common';
import { Effect, Match, Schedule } from 'effect';
import { Data, Effect, Match, Schedule } from 'effect';

@Injectable()
export class EffectiveUsersService {
Expand Down Expand Up @@ -49,7 +49,7 @@ export class EffectiveUsersService {
Effect.flatMap(([user, posts]) =>
!user || !posts
? Effect.die('Something went wrong!')
: Effect.succeed([user, posts] as const)
: Effect.succeed(Data.tuple(user, posts))
),
Effect.map(([user, posts]) => ({
id: user.id,
Expand Down

0 comments on commit 414acc8

Please sign in to comment.