@@ -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 | |||
} |