{ | { | ||||
"collection": "@nestjs/schematics", | "collection": "@nestjs/schematics", | ||||
"sourceRoot": "src" | |||||
"sourceRoot": "src", | |||||
"compilerOptions": { | |||||
"plugins": ["@nestjs/graphql/plugin"] | |||||
} | |||||
} | } |
"dependencies": { | "dependencies": { | ||||
"@nestjs/common": "^7.0.0", | "@nestjs/common": "^7.0.0", | ||||
"@nestjs/core": "^7.0.0", | "@nestjs/core": "^7.0.0", | ||||
"@nestjs/graphql": "^7.7.0", | |||||
"@nestjs/mongoose": "^7.0.2", | "@nestjs/mongoose": "^7.0.2", | ||||
"@nestjs/platform-express": "^7.0.0", | "@nestjs/platform-express": "^7.0.0", | ||||
"@types/podcast": "^1.3.0", | "@types/podcast": "^1.3.0", | ||||
"apollo-server-express": "^2.18.2", | |||||
"graphql": "^15.3.0", | |||||
"graphql-tools": "^6.2.4", | |||||
"mongoose": "^5.10.9", | "mongoose": "^5.10.9", | ||||
"podcast": "^1.3.0", | "podcast": "^1.3.0", | ||||
"reflect-metadata": "^0.1.13", | "reflect-metadata": "^0.1.13", | ||||
"rimraf": "^3.0.2", | "rimraf": "^3.0.2", | ||||
"rxjs": "^6.5.4" | |||||
"rxjs": "^6.5.4", | |||||
"uuid": "^8.3.0" | |||||
}, | }, | ||||
"devDependencies": { | "devDependencies": { | ||||
"@nestjs/cli": "^7.0.0", | "@nestjs/cli": "^7.0.0", | ||||
"@types/mongoose": "^5.7.36", | "@types/mongoose": "^5.7.36", | ||||
"@types/node": "^13.9.1", | "@types/node": "^13.9.1", | ||||
"@types/supertest": "^2.0.8", | "@types/supertest": "^2.0.8", | ||||
"@types/uuid": "^8.3.0", | |||||
"@typescript-eslint/eslint-plugin": "3.9.1", | "@typescript-eslint/eslint-plugin": "3.9.1", | ||||
"@typescript-eslint/parser": "3.9.1", | "@typescript-eslint/parser": "3.9.1", | ||||
"eslint": "7.7.0", | "eslint": "7.7.0", |
import { Episode } from "schemas/entity/episode.entity"; | |||||
export class CreateEpisodeDto extends Episode { | |||||
} |
import { Field, InputType } from '@nestjs/graphql'; | |||||
import { ItunesEpisodeTypeEnum } from 'schemas/enum/itunes-episode-type.enum'; | |||||
import { EpisodeEnclosureCreateDto } from './episode-enclosure-create.dto'; | |||||
@InputType() | |||||
export class EpisodeCreateDto { | |||||
@Field({ nullable: true }) | |||||
title?: string; | |||||
@Field({ nullable: true }) | |||||
description?: string; | |||||
@Field() | |||||
url: string; | |||||
@Field({ nullable: true }) | |||||
guid?: string; | |||||
@Field(type => [String], { nullable: true }) | |||||
categories?: string[]; | |||||
@Field({ nullable: true }) | |||||
author?: string; | |||||
@Field() | |||||
date: Date; | |||||
@Field({ nullable: true }) | |||||
lat?: number; | |||||
@Field({ nullable: true }) | |||||
long?: number; | |||||
@Field(type => EpisodeEnclosureCreateDto, { nullable: true }) | |||||
enclosure?: EpisodeEnclosureCreateDto; | |||||
@Field({ nullable: true }) | |||||
content?: string; | |||||
@Field({ nullable: true }) | |||||
itunesAuthor?: string; | |||||
@Field({ nullable: true }) | |||||
itunesExplicit?: boolean; | |||||
@Field({ nullable: true }) | |||||
itunesSubtitle?: string; | |||||
@Field({ nullable: true }) | |||||
itunesSummary?: string; | |||||
@Field({ nullable: true }) | |||||
itunesDuration?: number; | |||||
@Field({ nullable: true }) | |||||
itunesImage?: string; | |||||
@Field({ nullable: true }) | |||||
itunesSeason?: number; | |||||
@Field({ nullable: true }) | |||||
itunesEpisode?: number; | |||||
@Field({ nullable: true }) | |||||
itunesTitle?: string; | |||||
@Field(type => ItunesEpisodeTypeEnum, { nullable: true }) | |||||
itunesEpisodeType?: ItunesEpisodeTypeEnum; | |||||
} |
import { Field, InputType } from '@nestjs/graphql'; | |||||
@InputType() | |||||
export class EpisodeEnclosureCreateDto { | |||||
@Field() | |||||
url: string; | |||||
@Field({nullable: true}) | |||||
file?: string; | |||||
@Field({nullable: true}) | |||||
size?: number; | |||||
@Field({nullable: true}) | |||||
type?: string; | |||||
} |
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; | import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; | ||||
import Podcast from 'podcast'; | import Podcast from 'podcast'; | ||||
import { Document } from 'mongoose'; | import { Document } from 'mongoose'; | ||||
import { Field, ID, InputType, ObjectType } from '@nestjs/graphql'; | |||||
export type EpisodeEnclosureDocument = EpisodeEnclosure & Document; | export type EpisodeEnclosureDocument = EpisodeEnclosure & Document; | ||||
@Schema() | @Schema() | ||||
@ObjectType() | |||||
export class EpisodeEnclosure implements Podcast.ItemEnclosure { | export class EpisodeEnclosure implements Podcast.ItemEnclosure { | ||||
@Prop() | @Prop() | ||||
@Field(type => ID) | |||||
id: string; | |||||
@Prop() | |||||
@Field() | |||||
url: string; | url: string; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
file?: string; | file?: string; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
size?: number; | size?: number; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
type?: string; | type?: string; | ||||
} | } | ||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; | import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; | ||||
import Podcast from 'podcast'; | import Podcast from 'podcast'; | ||||
import { ItunesEpisodeTypeEnum } from 'schemas/enum/itunes-episode-type.enum'; | import { ItunesEpisodeTypeEnum } from 'schemas/enum/itunes-episode-type.enum'; | ||||
import { EpisodeEnclosure } from './episode-enclosure'; | |||||
import { EpisodeEnclosure } from './episode-enclosure.entity'; | |||||
import { Document } from 'mongoose'; | import { Document } from 'mongoose'; | ||||
import { Field, ID, ObjectType } from '@nestjs/graphql'; | |||||
export type EpisodeDocument = Episode & Document; | export type EpisodeDocument = Episode & Document; | ||||
@Schema() | @Schema() | ||||
@ObjectType() | |||||
export class Episode implements Podcast.Item { | export class Episode implements Podcast.Item { | ||||
@Prop() | @Prop() | ||||
@Field(type => ID) | |||||
id: string; | |||||
@Prop() | |||||
@Field({ nullable: true }) | |||||
title?: string; | title?: string; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
description?: string; | description?: string; | ||||
@Prop() | @Prop() | ||||
@Field() | |||||
url: string; | url: string; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
guid?: string; | guid?: string; | ||||
@Prop([String]) | @Prop([String]) | ||||
@Field(type => [String], {nullable: true}) | |||||
categories?: string[]; | categories?: string[]; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
author?: string; | author?: string; | ||||
@Prop() | @Prop() | ||||
@Field() | |||||
date: Date; | date: Date; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
lat?: number; | lat?: number; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
long?: number; | long?: number; | ||||
@Prop(EpisodeEnclosure) | @Prop(EpisodeEnclosure) | ||||
@Field(type => EpisodeEnclosure, {nullable: true}) | |||||
enclosure?: EpisodeEnclosure; | enclosure?: EpisodeEnclosure; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
content?: string; | content?: string; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
itunesAuthor?: string; | itunesAuthor?: string; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
itunesExplicit?: boolean; | itunesExplicit?: boolean; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
itunesSubtitle?: string; | itunesSubtitle?: string; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
itunesSummary?: string; | itunesSummary?: string; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
itunesDuration?: number; | itunesDuration?: number; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
itunesImage?: string; | itunesImage?: string; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
itunesSeason?: number; | itunesSeason?: number; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
itunesEpisode?: number; | itunesEpisode?: number; | ||||
@Prop() | @Prop() | ||||
@Field({ nullable: true }) | |||||
itunesTitle?: string; | itunesTitle?: string; | ||||
@Prop(ItunesEpisodeTypeEnum) | @Prop(ItunesEpisodeTypeEnum) | ||||
@Field(type => ItunesEpisodeTypeEnum, {nullable: true}) | |||||
itunesEpisodeType?: ItunesEpisodeTypeEnum; | itunesEpisodeType?: ItunesEpisodeTypeEnum; | ||||
} | } | ||||
import { registerEnumType } from '@nestjs/graphql'; | |||||
export enum ItunesEpisodeTypeEnum { | export enum ItunesEpisodeTypeEnum { | ||||
FULL = "full", | |||||
TRAILER = "trailer", | |||||
BONUS = "bonus", | |||||
} | |||||
FULL = 'full', | |||||
TRAILER = 'trailer', | |||||
BONUS = 'bonus', | |||||
} | |||||
registerEnumType(ItunesEpisodeTypeEnum, { | |||||
name: 'ItunesEpisodeTypeEnum', | |||||
}); |
import { Episode, EpisodeSchema } from 'schemas/entity/episode.entity'; | import { Episode, EpisodeSchema } from 'schemas/entity/episode.entity'; | ||||
import { AppService } from './app.service'; | import { AppService } from './app.service'; | ||||
import { EpisodeService } from './episode/episode.service'; | import { EpisodeService } from './episode/episode.service'; | ||||
import { GraphQLModule } from '@nestjs/graphql'; | |||||
import { join } from 'path'; | |||||
import { EpisodeResolver } from './episode/episode.resolver'; | |||||
@Module({ | @Module({ | ||||
imports: [ | imports: [ | ||||
MongooseModule.forFeature([{ name: Episode.name, schema: EpisodeSchema }]), | MongooseModule.forFeature([{ name: Episode.name, schema: EpisodeSchema }]), | ||||
MongooseModule.forRoot('mongodb://localhost:27017/test') | |||||
MongooseModule.forRoot('mongodb://localhost:27017/test'), | |||||
GraphQLModule.forRoot({ | |||||
debug: false, | |||||
playground: true, | |||||
autoSchemaFile: join(process.cwd(), 'src/schema.gql'), | |||||
installSubscriptionHandlers: true, | |||||
}), | |||||
], | ], | ||||
providers: [AppService, EpisodeService], | |||||
providers: [AppService, EpisodeService, EpisodeResolver], | |||||
}) | }) | ||||
export class AppModule {} | export class AppModule {} |
import { Test, TestingModule } from '@nestjs/testing'; | |||||
import { EpisodeResolver } from './episode.resolver'; | |||||
describe('EpisodeResolver', () => { | |||||
let resolver: EpisodeResolver; | |||||
beforeEach(async () => { | |||||
const module: TestingModule = await Test.createTestingModule({ | |||||
providers: [EpisodeResolver], | |||||
}).compile(); | |||||
resolver = module.get<EpisodeResolver>(EpisodeResolver); | |||||
}); | |||||
it('should be defined', () => { | |||||
expect(resolver).toBeDefined(); | |||||
}); | |||||
}); |
import { Resolver, Query, Args, Mutation } from '@nestjs/graphql'; | |||||
import { EpisodeCreateDto } from 'schemas/dto/episode-create.dto'; | |||||
import { Episode } from 'schemas/entity/episode.entity'; | |||||
import { EpisodeService } from './episode.service'; | |||||
@Resolver() | |||||
export class EpisodeResolver { | |||||
constructor(protected episodeService: EpisodeService) {} | |||||
@Query(returns => [Episode]) | |||||
async listEpisodes(): Promise<Episode[]> { | |||||
return this.episodeService.findAll(); | |||||
} | |||||
@Mutation(returns => Episode) | |||||
async createEpisode( | |||||
@Args('episodeCreateDto') episodeCreateDto: EpisodeCreateDto, | |||||
) { | |||||
const episode = await this.episodeService.create(episodeCreateDto); | |||||
return episode; | |||||
} | |||||
} |
import { Injectable } from '@nestjs/common'; | import { Injectable } from '@nestjs/common'; | ||||
import { InjectModel } from '@nestjs/mongoose'; | import { InjectModel } from '@nestjs/mongoose'; | ||||
import { Model } from 'mongoose'; | import { Model } from 'mongoose'; | ||||
import { CreateEpisodeDto } from 'schemas/dto/create-episode.dto'; | |||||
import { EpisodeCreateDto } from 'schemas/dto/episode-create.dto'; | |||||
import { Episode, EpisodeDocument } from 'schemas/entity/episode.entity'; | import { Episode, EpisodeDocument } from 'schemas/entity/episode.entity'; | ||||
import { v4 as uuid } from 'uuid'; | |||||
@Injectable() | @Injectable() | ||||
export class EpisodeService { | export class EpisodeService { | ||||
constructor( | constructor( | ||||
@InjectModel(Episode.name) private episodeModel: Model<EpisodeDocument>, | @InjectModel(Episode.name) private episodeModel: Model<EpisodeDocument>, | ||||
) {} | ) {} | ||||
async create(createEpisodeDto: CreateEpisodeDto): Promise<Episode> { | |||||
const createdEpisode = new this.episodeModel(createEpisodeDto); | |||||
async create(episodeCreateDto: EpisodeCreateDto): Promise<Episode> { | |||||
const createdEpisode = new this.episodeModel(episodeCreateDto); | |||||
createdEpisode.id = uuid(); | |||||
return createdEpisode.save(); | return createdEpisode.save(); | ||||
} | } | ||||
async findAll(): Promise<Episode[]> { | async findAll(): Promise<Episode[]> { | ||||
return this.episodeModel.find().exec(); | return this.episodeModel.find().exec(); | ||||
} | } | ||||
async findOneById(): Promise<Episode[]> { | |||||
return this.episodeModel.find().exec(); | |||||
} | |||||
} | } |
# ------------------------------------------------------ | |||||
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY) | |||||
# ------------------------------------------------------ | |||||
type EpisodeEnclosure { | |||||
id: ID! | |||||
url: String! | |||||
file: String | |||||
size: Float | |||||
type: String | |||||
} | |||||
type Episode { | |||||
id: ID! | |||||
title: String | |||||
description: String | |||||
url: String! | |||||
guid: String | |||||
categories: [String!] | |||||
author: String | |||||
date: DateTime! | |||||
lat: Float | |||||
long: Float | |||||
enclosure: EpisodeEnclosure | |||||
content: String | |||||
itunesAuthor: String | |||||
itunesExplicit: Boolean | |||||
itunesSubtitle: String | |||||
itunesSummary: String | |||||
itunesDuration: Float | |||||
itunesImage: String | |||||
itunesSeason: Float | |||||
itunesEpisode: Float | |||||
itunesTitle: String | |||||
itunesEpisodeType: ItunesEpisodeTypeEnum | |||||
} | |||||
""" | |||||
A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format. | |||||
""" | |||||
scalar DateTime | |||||
enum ItunesEpisodeTypeEnum { | |||||
FULL | |||||
TRAILER | |||||
BONUS | |||||
type | |||||
} | |||||
type Query { | |||||
listEpisodes: [Episode!]! | |||||
} | |||||
type Mutation { | |||||
createEpisode(episodeCreateDto: EpisodeCreateDto!): Episode! | |||||
} | |||||
input EpisodeCreateDto { | |||||
title: String | |||||
description: String | |||||
url: String! | |||||
guid: String | |||||
categories: [String!] | |||||
author: String | |||||
date: DateTime! | |||||
lat: Float | |||||
long: Float | |||||
enclosure: EpisodeEnclosureCreateDto | |||||
content: String | |||||
itunesAuthor: String | |||||
itunesExplicit: Boolean | |||||
itunesSubtitle: String | |||||
itunesSummary: String | |||||
itunesDuration: Float | |||||
itunesImage: String | |||||
itunesSeason: Float | |||||
itunesEpisode: Float | |||||
itunesTitle: String | |||||
itunesEpisodeType: ItunesEpisodeTypeEnum | |||||
} | |||||
input EpisodeEnclosureCreateDto { | |||||
url: String! | |||||
file: String | |||||
size: Float | |||||
type: String | |||||
} |