Skip to content

Commit

Permalink
Add GraphQL layer
Browse files Browse the repository at this point in the history
Related to #16

Add GraphQL layer to the repository.

* **Dependencies**: Add `@nestjs/graphql`, `graphql`, and `@nestjs/apollo` to `package.json`.
* **Schema**: Create `src/graphql/schema.graphql` with type definitions for Customer, Employee, and Shipper models, including queries and mutations.
* **Resolvers**: Create `src/graphql/resolvers.ts` with resolvers for Customer, Employee, and Shipper models, implementing queries and mutations.
* **App Module**: Modify `src/app.module.ts` to import and configure `GraphQLModule` with `forRoot` method, and add `Resolvers` to `providers` array.
* **Tests**: Add `src/graphql/graphql.test.ts` with tests for GraphQL endpoints, including queries and mutations for Customer, Employee, and Shipper models.
  • Loading branch information
vishwamartur committed Jan 5, 2025
1 parent 898c931 commit 3ad7ffe
Show file tree
Hide file tree
Showing 5 changed files with 413 additions and 1 deletion.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"socket.io": "^4.8.1",
"sqlstring": "^2.3.3"
"sqlstring": "^2.3.3",
"@nestjs/graphql": "^10.0.0",
"graphql": "^15.5.0",
"@nestjs/apollo": "^10.0.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
Expand Down
7 changes: 7 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { ConfigModule, ConfigService } from '@nestjs/config'
import { JwtModule } from '@nestjs/jwt'
import { ScheduleModule } from '@nestjs/schedule'
import Redis from 'ioredis'
import { GraphQLModule } from '@nestjs/graphql'
import { join } from 'path'

import { AuthController } from './app.controller.auth'
import { DeleteController } from './app.controller.delete'
Expand Down Expand Up @@ -41,6 +43,7 @@ import { REDIS_PUB_CLIENT_TOKEN, REDIS_SUB_CLIENT_TOKEN } from './modules/websoc
import { WebsocketGateway } from './modules/websocket/websocket.gateway'
import { WebsocketService } from './modules/websocket/websocket.service'
import { Env } from './utils/Env'
import { Resolvers } from './graphql/resolvers'

const singleServerRedisPubsub = new RedisMockWithPubSub() // in-memory pubsub for testing or single server setup

Expand Down Expand Up @@ -72,6 +75,9 @@ function createPubSubOnlyRedisClient() {
isGlobal: true,
}),
ScheduleModule.forRoot(),
GraphQLModule.forRoot({
autoSchemaFile: join(process.cwd(), 'src/graphql/schema.gql'),
}),
],
controllers: [AuthController, DocsController, DeleteController, GetController, PostController, PutController],
providers: [
Expand All @@ -96,6 +102,7 @@ function createPubSubOnlyRedisClient() {
Webhook,
WebsocketGateway,
WebsocketService,
Resolvers,
{
provide: REDIS_PUB_CLIENT_TOKEN,
useFactory: createPubSubOnlyRedisClient,
Expand Down
213 changes: 213 additions & 0 deletions src/graphql/graphql.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import { INestApplication } from '@nestjs/common'
import { Test } from '@nestjs/testing'
import * as request from 'supertest'
import { AppModule } from '../app.module'

describe('GraphQL', () => {
let app: INestApplication

beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile()

app = moduleRef.createNestApplication()
await app.init()
})

afterAll(async () => {
await app.close()
})

describe('Customer', () => {
it('should get a customer by id', async () => {
const response = await request(app.getHttpServer())
.post('/graphql')
.send({
query: `
query {
getCustomer(id: "1") {
id
companyName
contactName
contactTitle
address
city
region
postalCode
country
phone
fax
}
}
`,
})
.expect(200)

expect(response.body.data.getCustomer).toBeDefined()
})

it('should create a customer', async () => {
const response = await request(app.getHttpServer())
.post('/graphql')
.send({
query: `
mutation {
createCustomer(input: {
companyName: "Test Company"
contactName: "Test Contact"
contactTitle: "Test Title"
address: "Test Address"
city: "Test City"
region: "Test Region"
postalCode: "Test PostalCode"
country: "Test Country"
phone: "Test Phone"
fax: "Test Fax"
}) {
id
companyName
contactName
contactTitle
address
city
region
postalCode
country
phone
fax
}
}
`,
})
.expect(200)

expect(response.body.data.createCustomer).toBeDefined()
})
})

describe('Employee', () => {
it('should get an employee by id', async () => {
const response = await request(app.getHttpServer())
.post('/graphql')
.send({
query: `
query {
getEmployee(id: "1") {
id
lastName
firstName
title
titleOfCourtesy
birthDate
hireDate
address
city
region
postalCode
country
homePhone
extension
notes
reportsTo
}
}
`,
})
.expect(200)

expect(response.body.data.getEmployee).toBeDefined()
})

it('should create an employee', async () => {
const response = await request(app.getHttpServer())
.post('/graphql')
.send({
query: `
mutation {
createEmployee(input: {
lastName: "Test LastName"
firstName: "Test FirstName"
title: "Test Title"
titleOfCourtesy: "Test Courtesy"
birthDate: "2000-01-01"
hireDate: "2020-01-01"
address: "Test Address"
city: "Test City"
region: "Test Region"
postalCode: "Test PostalCode"
country: "Test Country"
homePhone: "Test Phone"
extension: "Test Extension"
notes: "Test Notes"
reportsTo: "1"
}) {
id
lastName
firstName
title
titleOfCourtesy
birthDate
hireDate
address
city
region
postalCode
country
homePhone
extension
notes
reportsTo
}
}
`,
})
.expect(200)

expect(response.body.data.createEmployee).toBeDefined()
})
})

describe('Shipper', () => {
it('should get a shipper by id', async () => {
const response = await request(app.getHttpServer())
.post('/graphql')
.send({
query: `
query {
getShipper(id: "1") {
id
companyName
phone
}
}
`,
})
.expect(200)

expect(response.body.data.getShipper).toBeDefined()
})

it('should create a shipper', async () => {
const response = await request(app.getHttpServer())
.post('/graphql')
.send({
query: `
mutation {
createShipper(input: {
companyName: "Test Company"
phone: "Test Phone"
}) {
id
companyName
phone
}
}
`,
})
.expect(200)

expect(response.body.data.createShipper).toBeDefined()
})
})
})
94 changes: 94 additions & 0 deletions src/graphql/resolvers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { CustomerService } from '../services/customer.service';
import { EmployeeService } from '../services/employee.service';
import { ShipperService } from '../services/shipper.service';
import { Customer } from '../models/customer.model';
import { Employee } from '../models/employee.model';
import { Shipper } from '../models/shipper.model';
import { CustomerInput } from '../dto/customer.input';
import { EmployeeInput } from '../dto/employee.input';
import { ShipperInput } from '../dto/shipper.input';

@Resolver()
export class Resolvers {
constructor(
private readonly customerService: CustomerService,
private readonly employeeService: EmployeeService,
private readonly shipperService: ShipperService,
) {}

@Query(() => Customer)
async getCustomer(@Args('id') id: string) {
return this.customerService.findOne(id);
}

@Query(() => [Customer])
async getCustomers() {
return this.customerService.findAll();
}

@Mutation(() => Customer)
async createCustomer(@Args('input') input: CustomerInput) {
return this.customerService.create(input);
}

@Mutation(() => Customer)
async updateCustomer(@Args('id') id: string, @Args('input') input: CustomerInput) {
return this.customerService.update(id, input);
}

@Mutation(() => Boolean)
async deleteCustomer(@Args('id') id: string) {
return this.customerService.delete(id);
}

@Query(() => Employee)
async getEmployee(@Args('id') id: string) {
return this.employeeService.findOne(id);
}

@Query(() => [Employee])
async getEmployees() {
return this.employeeService.findAll();
}

@Mutation(() => Employee)
async createEmployee(@Args('input') input: EmployeeInput) {
return this.employeeService.create(input);
}

@Mutation(() => Employee)
async updateEmployee(@Args('id') id: string, @Args('input') input: EmployeeInput) {
return this.employeeService.update(id, input);
}

@Mutation(() => Boolean)
async deleteEmployee(@Args('id') id: string) {
return this.employeeService.delete(id);
}

@Query(() => Shipper)
async getShipper(@Args('id') id: string) {
return this.shipperService.findOne(id);
}

@Query(() => [Shipper])
async getShippers() {
return this.shipperService.findAll();
}

@Mutation(() => Shipper)
async createShipper(@Args('input') input: ShipperInput) {
return this.shipperService.create(input);
}

@Mutation(() => Shipper)
async updateShipper(@Args('id') id: string, @Args('input') input: ShipperInput) {
return this.shipperService.update(id, input);
}

@Mutation(() => Boolean)
async deleteShipper(@Args('id') id: string) {
return this.shipperService.delete(id);
}
}
Loading

0 comments on commit 3ad7ffe

Please sign in to comment.