| @@ -1,4 +1,7 @@ | |||
| { | |||
| "collection": "@nestjs/schematics", | |||
| "sourceRoot": "src" | |||
| "sourceRoot": "src", | |||
| "compilerOptions": { | |||
| "plugins": ["@nestjs/graphql/plugin"] | |||
| } | |||
| } | |||
| @@ -23,14 +23,19 @@ | |||
| "dependencies": { | |||
| "@nestjs/common": "^7.0.0", | |||
| "@nestjs/core": "^7.0.0", | |||
| "@nestjs/graphql": "^7.7.0", | |||
| "@nestjs/mongoose": "^7.0.2", | |||
| "@nestjs/platform-express": "^7.0.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", | |||
| "podcast": "^1.3.0", | |||
| "reflect-metadata": "^0.1.13", | |||
| "rimraf": "^3.0.2", | |||
| "rxjs": "^6.5.4" | |||
| "rxjs": "^6.5.4", | |||
| "uuid": "^8.3.0" | |||
| }, | |||
| "devDependencies": { | |||
| "@nestjs/cli": "^7.0.0", | |||
| @@ -41,6 +46,7 @@ | |||
| "@types/mongoose": "^5.7.36", | |||
| "@types/node": "^13.9.1", | |||
| "@types/supertest": "^2.0.8", | |||
| "@types/uuid": "^8.3.0", | |||
| "@typescript-eslint/eslint-plugin": "3.9.1", | |||
| "@typescript-eslint/parser": "3.9.1", | |||
| "eslint": "7.7.0", | |||
| @@ -1,5 +0,0 @@ | |||
| import { Episode } from "schemas/entity/episode.entity"; | |||
| export class CreateEpisodeDto extends Episode { | |||
| } | |||
| @@ -0,0 +1,69 @@ | |||
| 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; | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| 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; | |||
| } | |||
| @@ -1,18 +1,31 @@ | |||
| import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; | |||
| import Podcast from 'podcast'; | |||
| import { Document } from 'mongoose'; | |||
| import { Field, ID, InputType, ObjectType } from '@nestjs/graphql'; | |||
| export type EpisodeEnclosureDocument = EpisodeEnclosure & Document; | |||
| @Schema() | |||
| @ObjectType() | |||
| export class EpisodeEnclosure implements Podcast.ItemEnclosure { | |||
| @Prop() | |||
| @Field(type => ID) | |||
| id: string; | |||
| @Prop() | |||
| @Field() | |||
| url: string; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| file?: string; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| size?: number; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| type?: string; | |||
| } | |||
| @@ -1,54 +1,101 @@ | |||
| import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; | |||
| import Podcast from 'podcast'; | |||
| 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 { Field, ID, ObjectType } from '@nestjs/graphql'; | |||
| export type EpisodeDocument = Episode & Document; | |||
| @Schema() | |||
| @ObjectType() | |||
| export class Episode implements Podcast.Item { | |||
| @Prop() | |||
| @Field(type => ID) | |||
| id: string; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| title?: string; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| description?: string; | |||
| @Prop() | |||
| @Field() | |||
| url: string; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| guid?: string; | |||
| @Prop([String]) | |||
| @Field(type => [String], {nullable: true}) | |||
| categories?: string[]; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| author?: string; | |||
| @Prop() | |||
| @Field() | |||
| date: Date; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| lat?: number; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| long?: number; | |||
| @Prop(EpisodeEnclosure) | |||
| @Field(type => EpisodeEnclosure, {nullable: true}) | |||
| enclosure?: EpisodeEnclosure; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| content?: string; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| itunesAuthor?: string; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| itunesExplicit?: boolean; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| itunesSubtitle?: string; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| itunesSummary?: string; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| itunesDuration?: number; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| itunesImage?: string; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| itunesSeason?: number; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| itunesEpisode?: number; | |||
| @Prop() | |||
| @Field({ nullable: true }) | |||
| itunesTitle?: string; | |||
| @Prop(ItunesEpisodeTypeEnum) | |||
| @Field(type => ItunesEpisodeTypeEnum, {nullable: true}) | |||
| itunesEpisodeType?: ItunesEpisodeTypeEnum; | |||
| } | |||
| @@ -1,5 +1,11 @@ | |||
| import { registerEnumType } from '@nestjs/graphql'; | |||
| export enum ItunesEpisodeTypeEnum { | |||
| FULL = "full", | |||
| TRAILER = "trailer", | |||
| BONUS = "bonus", | |||
| } | |||
| FULL = 'full', | |||
| TRAILER = 'trailer', | |||
| BONUS = 'bonus', | |||
| } | |||
| registerEnumType(ItunesEpisodeTypeEnum, { | |||
| name: 'ItunesEpisodeTypeEnum', | |||
| }); | |||
| @@ -3,12 +3,21 @@ import { MongooseModule } from '@nestjs/mongoose'; | |||
| import { Episode, EpisodeSchema } from 'schemas/entity/episode.entity'; | |||
| import { AppService } from './app.service'; | |||
| import { EpisodeService } from './episode/episode.service'; | |||
| import { GraphQLModule } from '@nestjs/graphql'; | |||
| import { join } from 'path'; | |||
| import { EpisodeResolver } from './episode/episode.resolver'; | |||
| @Module({ | |||
| imports: [ | |||
| 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 {} | |||
| @@ -0,0 +1,18 @@ | |||
| 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(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,23 @@ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -1,20 +1,26 @@ | |||
| import { Injectable } from '@nestjs/common'; | |||
| import { InjectModel } from '@nestjs/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 { v4 as uuid } from 'uuid'; | |||
| @Injectable() | |||
| export class EpisodeService { | |||
| constructor( | |||
| @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(); | |||
| } | |||
| async findAll(): Promise<Episode[]> { | |||
| return this.episodeModel.find().exec(); | |||
| } | |||
| async findOneById(): Promise<Episode[]> { | |||
| return this.episodeModel.find().exec(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,87 @@ | |||
| # ------------------------------------------------------ | |||
| # 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 | |||
| } | |||