Skip to content

Commit

Permalink
abstracted jsonplaceholder api communication to a service
Browse files Browse the repository at this point in the history
  • Loading branch information
enochval committed Jun 8, 2024
1 parent 8f80169 commit 4d52c97
Show file tree
Hide file tree
Showing 21 changed files with 216 additions and 254 deletions.
7 changes: 4 additions & 3 deletions src/albums/albums.module.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { Module, forwardRef } from '@nestjs/common';
import { AlbumsService } from './albums.service';
import { HttpModule } from '@nestjs/axios';
import { PhotosService } from './photos/photos.service';
import { AlbumResolver } from './albums.resolver';
import { UserModule } from 'src/users/users.module';
import { JsonplaceholderModule } from 'src/jsonplaceholder/jsonplaceholder.module';
import { PhotosResolver } from './photos/photos.resolver';

@Module({
imports: [
forwardRef(() => UserModule),
HttpModule
JsonplaceholderModule
],
providers: [AlbumsService, AlbumResolver, PhotosService],
providers: [AlbumsService, AlbumResolver, PhotosService, PhotosResolver],
exports: [AlbumsService, PhotosService]
})
export class AlbumsModule {}
4 changes: 2 additions & 2 deletions src/albums/albums.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export class AlbumResolver {

@ResolveField('photos')
async getAlbumPhotos(@Parent() album, @Args() args): Promise<Photo[]> {
const { id } = album
return await this.photosService.getPhotosByAlbumId(id, args)
args.albumId = album.id
return await this.photosService.getPhotos(args)
}

@ResolveField('user')
Expand Down
46 changes: 10 additions & 36 deletions src/albums/albums.service.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,23 @@
import { HttpService } from '@nestjs/axios';
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AxiosError } from 'axios';
import { catchError, firstValueFrom } from 'rxjs';
import { Injectable } from '@nestjs/common';
import { Album } from 'src/graphql';
import { JsonplaceholderService } from 'src/jsonplaceholder/jsonplaceholder.service';

@Injectable()
export class AlbumsService {
private readonly baseUrl: string
private readonly logger: Logger = new Logger(AlbumsService.name)

constructor(
private readonly httpService: HttpService,
private readonly configService: ConfigService
) {
this.baseUrl = this.configService.get<string>('api.baseurl')
}
private readonly jsonPlaceHolderService: JsonplaceholderService<Album>
) {}

async getAlbums(args: any): Promise<Album[]> {
const slashAlbumId = `${args.albumId ? '/' + args.albumId : ''}`
const queryUserId = `${args.userId ? '?userId=' + args.userId : ''}`
const uri = `${this.baseUrl}/albums${slashAlbumId}${queryUserId}`

const { data } = await firstValueFrom(
this.httpService.get<any|Album[]>(uri)
.pipe(
catchError((err: AxiosError) => {
this.logger.error(err.response.data)
throw new BadRequestException('An error happened!')
})
)
)

if(args.albumId) {
const album: Array<Album> = []
album.push(data)
return album
}
const slashAlbumId: string = `${args.albumId ? '/' + args.albumId : ''}`
const queryUserId: string = `${args.userId ? '?userId=' + args.userId : ''}`
const uri: string = `/albums${slashAlbumId}${queryUserId}`

let rsp: Array<any> = data
if (args.first) {
const length = (args.first > rsp.length) ? rsp.length : args.first
rsp = rsp.slice(0, length)
if (args.albumId) {
args.isOne = true
}

return rsp
return await this.jsonPlaceHolderService.handleGetRequest(uri, args)
}
}
26 changes: 26 additions & 0 deletions src/albums/photos/photos.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Args, Parent, Query, ResolveField, Resolver } from "@nestjs/graphql";
import { Album, Photo } from "src/graphql";
import { AlbumsService } from "../albums.service";
import { PhotosService } from "./photos.service";


@Resolver('Photo')
export class PhotosResolver {

constructor(
private readonly albumService: AlbumsService,
private readonly photosService: PhotosService
) {}

@Query('photos')
async getPhotos(@Args() args): Promise<Photo[]> {
return await this.photosService.getPhotos(args)
}

@ResolveField('album')
async getPhotoAlbum(@Parent() photo): Promise<Album> {
const args: any = { albumId: photo.albumId }
const rsp = await this.albumService.getAlbums(args)
return rsp.find(() => true)
}
}
38 changes: 11 additions & 27 deletions src/albums/photos/photos.service.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,23 @@
import { HttpService } from '@nestjs/axios';
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AxiosError } from 'axios';
import { catchError, firstValueFrom } from 'rxjs';
import { Injectable } from '@nestjs/common';
import { Photo } from 'src/graphql';
import { JsonplaceholderService } from 'src/jsonplaceholder/jsonplaceholder.service';

@Injectable()
export class PhotosService {
private readonly logger: Logger = new Logger(PhotosService.name)
private readonly baseUrl: string

constructor(
private readonly httpService: HttpService,
private readonly configService: ConfigService
){
this.baseUrl = this.configService.get<string>('api.baseurl')
}

async getPhotosByAlbumId(albumId: number, args: any): Promise<Photo[]> {
const { data } = await firstValueFrom(
this.httpService.get<any[]>(`${this.baseUrl}/photos`).pipe(
catchError((err: AxiosError) => {
this.logger.error(err.response.data)
throw new BadRequestException("An error happened!")
})
)
)
private readonly jsonplaceholdService: JsonplaceholderService<Photo>
){}

const photos: Photo[] = data.filter(o => o.albumId === albumId)
async getPhotos(args: any): Promise<Photo[]> {
const slashPhotoId: string = `${args.photoId ? '/'+args.photoId : ''}`
const queryAlbumId: string = `${args.albumId ? '?albumId=' + args.albumId : ''}`
const uri: string = `/photos${slashPhotoId}${queryAlbumId}`

if (args.first) {
const length = (args.first > photos.length) ? photos.length : args.first
return photos.slice(0, length)
if (args.photoId) {
args.isOne = true
}

return photos;
return await this.jsonplaceholdService.handleGetRequest(uri, args)
}
}
12 changes: 7 additions & 5 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PostsModule } from './posts/posts.module';
import { ConfigModule } from '@nestjs/config';
import { AlbumsModule } from './albums/albums.module';
import { TodosModule } from './todos/todos.module';
import { JsonplaceholderModule } from './jsonplaceholder/jsonplaceholder.module';
import configuration from './config/configuration';

@Module({
Expand All @@ -22,15 +23,16 @@ import configuration from './config/configuration';
typePaths: ['./**/*.graphql'],
playground: true,
introspection: true,
// definitions: {
// path: join(process.cwd(), 'src/graphql.ts'),
// outputAs: 'class'
// },
definitions: {
path: join(process.cwd(), 'src/graphql.ts'),
outputAs: 'class'
},
}),
UserModule,
PostsModule,
AlbumsModule,
TodosModule
TodosModule,
JsonplaceholderModule
],
controllers: [AppController],
providers: [AppService],
Expand Down
2 changes: 2 additions & 0 deletions src/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export abstract class IQuery {
abstract comments(first?: Nullable<number>, commentId?: Nullable<number>, postId?: Nullable<number>): Nullable<Nullable<Comment>[]> | Promise<Nullable<Nullable<Comment>[]>>;

abstract albums(first?: Nullable<number>, albumId?: Nullable<number>, userId?: Nullable<number>): Nullable<Nullable<Album>[]> | Promise<Nullable<Nullable<Album>[]>>;

abstract photos(first?: Nullable<number>, photoId?: Nullable<number>, albumId?: Nullable<number>): Nullable<Nullable<Photo>[]> | Promise<Nullable<Nullable<Photo>[]>>;
}

type Nullable<T> = T | null;
10 changes: 10 additions & 0 deletions src/jsonplaceholder/jsonplaceholder.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { JsonplaceholderService } from './jsonplaceholder.service';
import { HttpModule } from '@nestjs/axios';

@Module({
imports: [HttpModule],
providers: [JsonplaceholderService],
exports: [JsonplaceholderService]
})
export class JsonplaceholderModule {}
18 changes: 18 additions & 0 deletions src/jsonplaceholder/jsonplaceholder.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { JsonplaceholderService } from './jsonplaceholder.service';

describe('JsonplaceholderService', () => {
let service: JsonplaceholderService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [JsonplaceholderService],
}).compile();

service = module.get<JsonplaceholderService>(JsonplaceholderService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
51 changes: 51 additions & 0 deletions src/jsonplaceholder/jsonplaceholder.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { HttpService } from '@nestjs/axios';
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AxiosError } from 'axios';
import { catchError, firstValueFrom } from 'rxjs';

@Injectable()
export class JsonplaceholderService<T> {

private readonly baseUrl: string
private readonly logger: Logger = new Logger(JsonplaceholderService.name)

constructor(
private readonly httpService: HttpService,
private readonly configService: ConfigService
) {
this.baseUrl = this.configService.get<string>('api.baseurl')
}

async handleGetRequest(uri: string, args?: any): Promise<any> {
const url: string = `${this.baseUrl}${uri}`

const { data } = await firstValueFrom(
this.httpService.get<any|T[]>(url)
.pipe(
catchError((err: AxiosError) => {
this.logger.error(err.response.data)
throw new BadRequestException('An error happened!')
})
)
)

if(args && args.isOne) {
const resp: Array<T> = []
resp.push(data)
return resp
}

if (!Array.isArray(data)) {
return data
}

let rsp: Array<T> = data
if (args.first) {
const length = (args.first > rsp.length) ? rsp.length : args.first
rsp = rsp.slice(0, length)
}

return rsp
}
}
2 changes: 1 addition & 1 deletion src/posts/comments/comments.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ export class CommentsResolver {

@ResolveField('post')
async getCommentPost(@Parent() comment): Promise<Post> {
return await this.postsService.getPost(comment.postId)
return await this.postsService.getPostById(comment.postId)
}
}
47 changes: 13 additions & 34 deletions src/posts/comments/comments.service.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,28 @@
import { HttpService } from '@nestjs/axios';
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AxiosError } from 'axios';
import { catchError, firstValueFrom } from 'rxjs';
import { Injectable } from '@nestjs/common';
import { Comment } from 'src/graphql';
import { JsonplaceholderService } from 'src/jsonplaceholder/jsonplaceholder.service';

@Injectable()
export class CommentsService {
private readonly logger: Logger = new Logger(CommentsService.name)
private baseUrl: string

constructor(
private readonly httpService: HttpService,
private readonly configService: ConfigService
){
this.baseUrl = this.configService.get<string>('api.baseurl')
}
private readonly jsonplaceholderService: JsonplaceholderService<Comment>
){}

async getComments(args: any): Promise<Comment[]> {
const slashCommentId = `${args.commentId ? '/'+args.commentId : ''}`
const queryPostId = `${args.postId ? '?postId=' + args.postId : ''}`
const uri = `${this.baseUrl}/comments${slashCommentId}${queryPostId}`

const { data } = await firstValueFrom(
this.httpService.get<any|Comment[]>(uri).pipe(
catchError((err: AxiosError) => {
this.logger.error(err.response.data)
throw new BadRequestException("An error happened!")
})
)
)
const slashCommentId: string = `${args.commentId ? '/'+args.commentId : ''}`
const queryPostId: string = `${args.postId ? '?postId=' + args.postId : ''}`
const uri: string = `/comments${slashCommentId}${queryPostId}`

if (args.commentId) {
const res: Array<Comment> = []
res.push(data)
return res
args.isOne = true
}

let rsp: Array<Comment> = data
return this.jsonplaceholderService.handleGetRequest(uri, args)
}

if (args.first) {
const length = (args.first > rsp.length) ? rsp.length : args.first
rsp = rsp.slice(0, length)
}

return rsp
async getPostComments(postId: number, args: any): Promise<Comment[]> {
const uri: string = `/post/${postId}/comments`
return await this.jsonplaceholderService.handleGetRequest(uri, args)
}
}
14 changes: 11 additions & 3 deletions src/posts/posts.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { Module, forwardRef } from '@nestjs/common';
import { PostsService } from './posts.service';
import { HttpModule } from '@nestjs/axios';
import { PostsResolver } from './posts.resolver';
import { UserModule } from 'src/users/users.module';
import { CommentsService } from './comments/comments.service';
import { CommentsResolver } from './comments/comments.resolver';
import { JsonplaceholderModule } from 'src/jsonplaceholder/jsonplaceholder.module';

@Module({
imports: [HttpModule, forwardRef(() => UserModule)],
providers: [PostsService, PostsResolver, CommentsService, CommentsResolver],
imports: [
forwardRef(() => UserModule),
JsonplaceholderModule
],
providers: [
PostsService,
PostsResolver,
CommentsService,
CommentsResolver
],
exports: [PostsService]
})
export class PostsModule {}
Loading

0 comments on commit 4d52c97

Please sign in to comment.