Skip to content

Commit

Permalink
feat(api): mongo
Browse files Browse the repository at this point in the history
closes #31
  • Loading branch information
Hagith committed Oct 30, 2020
1 parent 9a08ff2 commit 9d51821
Show file tree
Hide file tree
Showing 17 changed files with 262 additions and 60 deletions.
3 changes: 3 additions & 0 deletions apps/api/.env.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
NODE_ENV=production
AUTH_SECRET=
MONGODB_URI=
3 changes: 3 additions & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
"@nestjs/config": "^0.2.2",
"@nestjs/core": "^6.11.7",
"@nestjs/jwt": "^6.1.1",
"@nestjs/mongoose": "^7.0.2",
"@nestjs/passport": "^6.1.1",
"@nestjs/platform-express": "^6.11.7",
"mongoose": "^5.10.11",
"passport": "^0.4.1",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
Expand All @@ -40,6 +42,7 @@
"@types/express": "^4.17.2",
"@types/hapi__joi": "^16.0.9",
"@types/jest": "^24.0.18",
"@types/mongoose": "^5.7.36",
"@types/node": "^12.7.5",
"@types/passport-jwt": "^3.0.3",
"@types/passport-local": "^1.0.33",
Expand Down
10 changes: 9 additions & 1 deletion apps/api/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
Expand All @@ -15,6 +16,13 @@ import { UsersModule } from './users/users.module';
}),
AuthModule,
UsersModule,
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
uri: configService.get<string>('mongoDbUri'),
}),
inject: [ConfigService],
}),
],
controllers: [AppController],
providers: [AppService],
Expand Down
30 changes: 0 additions & 30 deletions apps/api/src/auth/auth.controller.spec.ts

This file was deleted.

5 changes: 2 additions & 3 deletions apps/api/src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing';
import { configServiceMock } from '../config/__tests__/config.service.mock';
import { usersServiceMockProvider } from '../users/__tests__/users.service.mock';
import { UsersTestingModule } from '../users/__tests__/users.testing.module';
import { AuthTestingModule } from './__tests__/auth.testing.module';
import { AuthService } from './auth.service';

Expand All @@ -9,10 +9,9 @@ describe('AuthService', () => {

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [AuthTestingModule],
imports: [AuthTestingModule, UsersTestingModule],
providers: [
AuthService,
usersServiceMockProvider,
configServiceMock({
auth: { secret: 'testsecret', tokenLifetime: '1s', refreshTokenLifetime: '2s' },
}),
Expand Down
5 changes: 3 additions & 2 deletions apps/api/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import { User, UsersService } from '../users/users.service';
import { User } from '../users/schema';
import { UsersService } from '../users/users.service';

// TODO storage
let refreshTokens: string[] = [];
Expand All @@ -24,7 +25,7 @@ export class AuthService {
}

async validate(login: string, password: string): Promise<User> {
const user = await this.users.findOne(login);
const user = await this.users.findByLogin(login);
if (user && password === user.password) {
return user;
}
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/auth/local.strategy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { IStrategyOptions, Strategy } from 'passport-local';
import { User } from '../users/users.service';
import { User } from '../users/schema';
import { AuthService } from './auth.service';

@Injectable()
Expand Down
4 changes: 4 additions & 0 deletions apps/api/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const configSchema = Joi.object({
AUTH_SECRET: Joi.string().required(),
AUTH_TOKEN_LIFETIME: Joi.alternatives(Joi.string(), Joi.number()).default('1d'),
AUTH_REFRESH_TOKEN_LIFETIME: Joi.alternatives(Joi.string(), Joi.number()).default('30d'),

MONGODB_URI: Joi.string().required(),
});

export interface Config {
Expand All @@ -22,6 +24,7 @@ export interface Config {
tokenLifetime: number | string;
refreshTokenLifetime: number | string;
};
mongoDbUri: string;
}

export const configFactory = (): Config => {
Expand All @@ -41,5 +44,6 @@ export const configFactory = (): Config => {
tokenLifetime: process.env.AUTH_TOKEN_LIFETIME,
refreshTokenLifetime: process.env.AUTH_REFRESH_TOKEN_LIFETIME,
},
mongoDbUri: process.env.MONGODB_URI,
};
};
18 changes: 15 additions & 3 deletions apps/api/src/users/__tests__/users.service.mock.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { Provider } from '@nestjs/common';
import { User, UsersService } from '../users.service';
import { getModelToken } from '@nestjs/mongoose';
import { User } from '../schema/user.schema';
import { UsersService } from '../users.service';

export class UsersServiceMock extends UsersService {
protected readonly users: User[] = [
{
id: 1,
id: '1',
login: 'demo',
password: 'demo',
},
{
id: '2',
login: 'john',
password: 'doe',
},
];

async findOne(login: string): Promise<User | undefined> {
async findByLogin(login: string): Promise<User> {
return this.users.find((user) => user.login === login);
}
}
Expand All @@ -19,3 +26,8 @@ export const usersServiceMockProvider: Provider = {
provide: UsersService,
useClass: UsersServiceMock,
};

export const userModelMockProvider: Provider = {
provide: getModelToken(User.name),
useValue: {},
};
10 changes: 10 additions & 0 deletions apps/api/src/users/__tests__/users.testing.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { UsersService } from '../users.service';
import { userModelMockProvider, usersServiceMockProvider } from './users.service.mock';

@Module({
imports: [],
providers: [userModelMockProvider, usersServiceMockProvider],
exports: [UsersService],
})
export class UsersTestingModule {}
1 change: 1 addition & 0 deletions apps/api/src/users/schema/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './user.schema';
17 changes: 17 additions & 0 deletions apps/api/src/users/schema/user.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

export type UserDocument = User & Document;

@Schema()
export class User {
id: string;

@Prop({ required: true })
login: string;

@Prop({ required: true })
password: string;
}

export const UserSchema = SchemaFactory.createForClass(User);
3 changes: 3 additions & 0 deletions apps/api/src/users/users.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from './schema/user.schema';
import { UsersService } from './users.service';

@Module({
imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])],
exports: [UsersService],
providers: [UsersService],
})
Expand Down
24 changes: 23 additions & 1 deletion apps/api/src/users/users.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
import { getModelToken } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { Model } from 'mongoose';
import { User } from './schema';
import { UserDocument } from './schema/user.schema';
import { UsersService } from './users.service';

describe('UsersService', () => {
let service: UsersService;
let UserModel: Partial<Model<UserDocument>>;

beforeEach(async () => {
UserModel = {
findOne: jest.fn(),
};

const module: TestingModule = await Test.createTestingModule({
providers: [UsersService],
providers: [
UsersService,
{
provide: getModelToken(User.name),
useValue: UserModel,
},
],
}).compile();

service = module.get<UsersService>(UsersService);
Expand All @@ -15,4 +30,11 @@ describe('UsersService', () => {
it('should be defined', () => {
expect(service).toBeDefined();
});

it('findByLogin', async () => {
await service.findByLogin('john').then(() => {
expect(UserModel.findOne).toBeCalledWith({ login: 'john' });
expect(UserModel.findOne).toHaveBeenCalledTimes(1);
});
});
});
22 changes: 6 additions & 16 deletions apps/api/src/users/users.service.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import { Injectable } from '@nestjs/common';

// TODO domain + storage
export interface User {
id: number;
login: string;
password: string;
}
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User, UserDocument } from './schema';

@Injectable()
export class UsersService {
protected readonly users: User[] = [
{
id: 1,
login: 'demo',
password: 'demo',
},
];
constructor(@InjectModel(User.name) protected userModel: Model<UserDocument>) {}

async findOne(login: string): Promise<User | undefined> {
return this.users.find((user) => user.login === login);
async findByLogin(login: string): Promise<User> {
return this.userModel.findOne({ login });
}
}
2 changes: 1 addition & 1 deletion libs/dto/auth/jwt-claims.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface JwtClaimsDto {
sub: number;
sub: string;
login: string;
}
Loading

0 comments on commit 9d51821

Please sign in to comment.