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

Commit

Permalink
Temporary commit
Browse files Browse the repository at this point in the history
  • Loading branch information
yasaichi committed Mar 11, 2024
1 parent 880f9ec commit 250f744
Show file tree
Hide file tree
Showing 15 changed files with 273 additions and 15 deletions.
2 changes: 2 additions & 0 deletions deno.lock

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

6 changes: 6 additions & 0 deletions libs/fake-api-kiota/schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,9 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/user'
'404':
description: User no found
content:
application/json:
schema:
type: object
6 changes: 5 additions & 1 deletion libs/fake-api-kiota/src/fake-api-kiota.module-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@ import { ConfigurableModuleBuilder } from '@nestjs/common';
import { type FakeApiKiotaModuleOptions } from './interfaces/fake-api-kiota-module-options.interface.ts';

export const { ConfigurableModuleClass, OPTIONS_TYPE } =
new ConfigurableModuleBuilder<FakeApiKiotaModuleOptions>().build();
new ConfigurableModuleBuilder<FakeApiKiotaModuleOptions>()
.setExtras(
{ global: true },
(definition, { global }) => ({ ...definition, global }),
).build();
2 changes: 1 addition & 1 deletion libs/fake-api-kiota/src/generated/kiota-lock.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"descriptionHash": "0683261456014BDB99EB7499816A79D9C2DEDBF2CC09A63492F5F55BCD573D5109F8B31274FAEB43F6CF71C04276537F2930B3821D71B83D74773043C65DB6FD",
"descriptionHash": "A96356C19D4668CADDEA5EFCD60A9836FDBDA51766A42DACAF06005B42810633B3DFBBC85CF74FCD8F6F102D7CC2811614A8751FA3EEF55AB6BFECBECD8D83B1",
"descriptionLocation": "../../schema.yaml",
"lockFileVersion": "1.0.0",
"kiotaVersion": "1.12.0",
Expand Down
35 changes: 34 additions & 1 deletion libs/fake-api-kiota/src/generated/users/item/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,37 @@
/* eslint-disable */
// Generated by Microsoft Kiota
import { createUserFromDiscriminatorValue, type User } from '../../models/index.ts';
import { type BaseRequestBuilder, type Parsable, type ParsableFactory, type RequestConfiguration, type RequestInformation, type RequestsMetadata } from '@microsoft/kiota-abstractions';
import { type AdditionalDataHolder, type ApiError, type BaseRequestBuilder, type Parsable, type ParsableFactory, type ParseNode, type RequestConfiguration, type RequestInformation, type RequestsMetadata, type SerializationWriter } from '@microsoft/kiota-abstractions';

/**
* Creates a new instance of the appropriate class based on discriminator value
* @param parseNode The parse node to use to read the discriminator value and create the object
* @returns {User404Error}
*/
export function createUser404ErrorFromDiscriminatorValue(parseNode: ParseNode | undefined) : ((instance?: Parsable) => Record<string, (node: ParseNode) => void>) {
return deserializeIntoUser404Error;
}
/**
* The deserialization information for the current model
* @returns {Record<string, (node: ParseNode) => void>}
*/
export function deserializeIntoUser404Error(user404Error: Partial<User404Error> | undefined = {}) : Record<string, (node: ParseNode) => void> {
return {
}
}
/**
* Serializes information the current object
* @param writer Serialization writer to use to serialize this model
*/
export function serializeUser404Error(writer: SerializationWriter, user404Error: Partial<User404Error> | undefined = {}) : void {
writer.writeAdditionalData(user404Error.additionalData);
}
export interface User404Error extends AdditionalDataHolder, ApiError, Parsable {
/**
* Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.
*/
additionalData?: Record<string, unknown>;
}
/**
* Builds and executes requests for operations under /users/{user-id}
*/
Expand All @@ -12,6 +41,7 @@ export interface UserItemRequestBuilder extends BaseRequestBuilder<UserItemReque
* Get user by ID
* @param requestConfiguration Configuration for the request such as headers, query parameters, and middleware options.
* @returns {Promise<User>}
* @throws {User404Error} error when the service returns a 404 status code
*/
get(requestConfiguration?: RequestConfiguration<object> | undefined) : Promise<User | undefined>;
/**
Expand All @@ -32,6 +62,9 @@ export const UserItemRequestBuilderRequestsMetadata: RequestsMetadata = {
get: {
uriTemplate: UserItemRequestBuilderUriTemplate,
responseBodyContentType: "application/json",
errorMappings: {
404: createUser404ErrorFromDiscriminatorValue as ParsableFactory<Parsable>,
},
adapterMethodName: "send",
responseBodyFactory: createUserFromDiscriminatorValue,
},
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
"start:debug": "deno run --allow-env --allow-net --allow-read=. --inspect=127.0.0.1:9229 --watch src/main.ts",
"start:prod": "deno run --allow-env --allow-net --allow-read=. src/main.ts",
"lint": "deno lint",
"test": "deno test --allow-env --ignore=test/ --parallel",
"test": "deno test --allow-env --allow-read=. --ignore=test/ --parallel",
"test:watch": "deno task test --watch",
"test:cov": "deno task test --coverage && deno coverage --exclude='/generated/' && deno coverage --exclude='/generated/' --lcov --output=coverage/lcov.info",
"test:debug": "deno test --inspect-brk=127.0.0.1:9230",
"test:debug": "deno test --allow-env --allow-read=. --inspect-brk=127.0.0.1:9230",
"test:e2e": "deno test --allow-env --allow-net --allow-read=. --parallel test/"
},
"dependencies": {
Expand All @@ -30,6 +30,7 @@
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"effect": "^2.4.0",
"http-errors": "^2.0.0",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
},
Expand All @@ -39,6 +40,7 @@
"@nestjs/testing": "^10.0.0",
"@std/testing": "npm:@jsr/std__testing@^0.218.2",
"@types/express": "^4.17.17",
"@types/http-errors": "^2.0.4",
"@types/node": "^20.3.1",
"@types/supertest": "^6.0.0",
"fetch-mock": "^9.11.0",
Expand Down
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

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

41 changes: 35 additions & 6 deletions src/app.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@ import {
CallHandler,
ExecutionContext,
Injectable,
Logger,
NestInterceptor,
} from '@nestjs/common';
import { Effect } from 'effect';
import { Effect, identity, Match, unsafeCoerce } from 'effect';
import createError from 'http-errors';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class AppInterceptor implements NestInterceptor {
private readonly logger: Logger;

constructor() {
this.logger = new Logger(AppInterceptor.name);
}

intercept(
_context: ExecutionContext,
next: CallHandler,
Expand All @@ -18,11 +26,32 @@ export class AppInterceptor implements NestInterceptor {
.handle()
.pipe(
map((value) =>
// NOTE: `_op` is a special key that indicates that the value is an effect
// https://github.com/Effect-TS/effect/blob/12345bf7955d126d4f3f48b604ed1f86da744148/packages/effect/src/internal/fiberRuntime.ts#L1282
typeof value === 'object' && '_op' in value
? Effect.runPromise(value)
: value
Match.value(value).pipe(
Match.when(() => Effect.isEffect(value), async (effect) =>
Match.value(
await Effect.runPromiseExit<unknown, Error>(
unsafeCoerce(effect),
),
).pipe(
Match.tag('Success', (success) =>
success.value),
Match.tag(
'Failure',
(failure) =>
Match.value(failure.cause).pipe(
Match.tag('Fail', ({ error }) => {
throw error;
}),
Match.orElse((anotherFailure) => {
this.logger.error(anotherFailure);
throw createError(500);
}),
),
),
Match.exhaustive,
)),
Match.orElse(identity),
)
),
);
}
Expand Down
8 changes: 7 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { FakeApiKiotaModule } from '@app/fake-api-kiota';
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { AppController } from './app.controller.ts';
import { AppInterceptor } from './app.interceptor.ts';
import { AppService } from './app.service.ts';
import { EffectiveUsersModule } from './effective-users/effective-users.module.ts';
import { SimpleUsersModule } from './simple-users/simple-users.module.ts';

@Module({
imports: [SimpleUsersModule],
imports: [
FakeApiKiotaModule.register({ global: true }),
EffectiveUsersModule,
SimpleUsersModule,
],
controllers: [AppController],
providers: [AppService, {
provide: APP_INTERCEPTOR,
Expand Down
12 changes: 12 additions & 0 deletions src/effective-users/effective-users.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Controller, Get, Param } from '@nestjs/common';
import { EffectiveUsersService } from './effective-users.service.ts';

@Controller('effective/users')
export class EffectiveUsersController {
constructor(private readonly effectiveUsersService: EffectiveUsersService) {}

@Get(':id')
findOne(@Param('id') id: number) {
return this.effectiveUsersService.findOneWithLatestPosts(id);
}
}
9 changes: 9 additions & 0 deletions src/effective-users/effective-users.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { EffectiveUsersController } from './effective-users.controller.ts';
import { EffectiveUsersService } from './effective-users.service.ts';

@Module({
controllers: [EffectiveUsersController],
providers: [EffectiveUsersService],
})
export class EffectiveUsersModule {}
86 changes: 86 additions & 0 deletions src/effective-users/effective-users.service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { FakeApiKiotaModule, type Post, type User } from '@app/fake-api-kiota';
import { Test, TestingModule } from '@nestjs/testing';
import { beforeEach, describe, it } from '@std/testing/bdd';
import { Effect } from 'effect';
import fetchMock from 'fetch-mock';
import createHttpError from 'http-errors';
import assert from 'node:assert';
import { EffectiveUsersService } from './effective-users.service.ts';

describe(EffectiveUsersService.name, () => {
let service: EffectiveUsersService;

describe(EffectiveUsersService.prototype.findOneWithLatestPosts.name, () => {
const userId = 42;
const user: User = {
id: userId,
name: 'Foo Bar',
username: 'foobar',
email: '[email protected]',
};
const posts: Post[] = [{
id: 1,
userId,
title: 'post 1 title',
body: 'post 1 body',
}];

describe('when the user is not found', () => {
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
FakeApiKiotaModule.register({
customFetch: fetchMock
.sandbox()
.get(`path:/users/${userId}`, { body: {}, status: 404 })
.getOnce('path:/posts', posts, { query: { userId, limit: 5 } }),
}),
],
providers: [EffectiveUsersService],
}).compile();

service = module.get(EffectiveUsersService);
});

it('should throw an error equivalent to HTTP 404 error', () => {
assert.rejects(
Effect.runPromise(
service.findOneWithLatestPosts(userId).pipe(
Effect.withConcurrency('unbounded'),
),
),
createHttpError(404),
);
});
});

describe('when the user is found', () => {
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
FakeApiKiotaModule.register({
customFetch: fetchMock
.sandbox()
.get(`path:/users/${userId}`, user)
.getOnce('path:/posts', posts, { query: { userId, limit: 5 } }),
}),
],
providers: [EffectiveUsersService],
}).compile();

service = module.get(EffectiveUsersService);
});

it('should return a specified user with the latest posts', async () => {
assert.deepStrictEqual(
await Effect.runPromise(service.findOneWithLatestPosts(userId)),
{
id: user.id,
username: user.username,
latestPosts: [{ id: posts[0].id, title: posts[0].title }],
},
);
});
});
});
});
Loading

0 comments on commit 250f744

Please sign in to comment.