浏览代码

add graphql and episodes provider

feature/add-graphql
父节点
当前提交
fd8fbe0e81
共有 14 个文件被更改,包括 17198 次插入10041 次删除
  1. +4
    -1
      nest-cli.json
  2. +16883
    -10024
      package-lock.json
  3. +7
    -1
      package.json
  4. +0
    -5
      schemas/dto/create-episode.dto.ts
  5. +69
    -0
      schemas/dto/episode-create.dto.ts
  6. +16
    -0
      schemas/dto/episode-enclosure-create.dto.ts
  7. +13
    -0
      schemas/entity/episode-enclosure.entity.ts
  8. +48
    -1
      schemas/entity/episode.entity.ts
  9. +10
    -4
      schemas/enum/itunes-episode-type.enum.ts
  10. +11
    -2
      src/app.module.ts
  11. +18
    -0
      src/episode/episode.resolver.spec.ts
  12. +23
    -0
      src/episode/episode.resolver.ts
  13. +9
    -3
      src/episode/episode.service.ts
  14. +87
    -0
      src/schema.gql

+ 4
- 1
nest-cli.json 查看文件

{ {
"collection": "@nestjs/schematics", "collection": "@nestjs/schematics",
"sourceRoot": "src"
"sourceRoot": "src",
"compilerOptions": {
"plugins": ["@nestjs/graphql/plugin"]
}
} }

+ 16883
- 10024
package-lock.json
文件差异内容过多而无法显示
查看文件


+ 7
- 1
package.json 查看文件

"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",

+ 0
- 5
schemas/dto/create-episode.dto.ts 查看文件

import { Episode } from "schemas/entity/episode.entity";

export class CreateEpisodeDto extends Episode {

}

+ 69
- 0
schemas/dto/episode-create.dto.ts 查看文件

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

+ 16
- 0
schemas/dto/episode-enclosure-create.dto.ts 查看文件

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

schemas/entity/episode-enclosure.ts → schemas/entity/episode-enclosure.entity.ts 查看文件

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



+ 48
- 1
schemas/entity/episode.entity.ts 查看文件

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



+ 10
- 4
schemas/enum/itunes-episode-type.enum.ts 查看文件

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',
});

+ 11
- 2
src/app.module.ts 查看文件

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

+ 18
- 0
src/episode/episode.resolver.spec.ts 查看文件

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();
});
});

+ 23
- 0
src/episode/episode.resolver.ts 查看文件

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

}

+ 9
- 3
src/episode/episode.service.ts 查看文件

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();
}
} }

+ 87
- 0
src/schema.gql 查看文件

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

正在加载...
取消
保存