浏览代码

add mutations

dev
父节点
当前提交
650bab38ef
共有 22 个文件被更改,包括 629 次插入19 次删除
  1. +1
    -1
      README.md
  2. +226
    -1
      backend/graphql.schema.json
  3. +23
    -3
      backend/package-lock.json
  4. +3
    -1
      backend/package.json
  5. +1
    -1
      backend/src/data/pizza-list.ts
  6. +1
    -1
      backend/src/data/topping-list.ts
  7. +53
    -0
      backend/src/generated/graphql.ts
  8. +15
    -1
      backend/src/index.ts
  9. +33
    -1
      backend/src/resolver/pizza-resolver.ts
  10. +25
    -1
      backend/src/resolver/topping-resolver.ts
  11. +23
    -1
      backend/src/schema/pizza.ts
  12. 二进制
      presentation/assets/apollo-logo.png
  13. 二进制
      presentation/assets/graphql-architecture.png
  14. 二进制
      presentation/assets/pizza-graphql.png
  15. 二进制
      presentation/assets/pizza-rest.png
  16. +1
    -1
      presentation/assets/pizza.xml
  17. 二进制
      presentation/assets/pros-cons.png
  18. 二进制
      presentation/assets/restful-architecture.png
  19. 二进制
      presentation/assets/self-doku.png
  20. +6
    -0
      presentation/css/layout.scss
  21. +1
    -1
      presentation/dist/reveal.css
  22. +217
    -5
      presentation/index.html

+ 1
- 1
README.md 查看文件

@@ -54,7 +54,7 @@ npx tsc --init --rootDir src --outDir build \
```

```
npm install --save-dev ts-node nodemon rimraf
npm install --save-dev ts-node nodemon rimraf uuid @types/uuid
```



+ 226
- 1
backend/graphql.schema.json 查看文件

@@ -4,7 +4,9 @@
"queryType": {
"name": "Query"
},
"mutationType": null,
"mutationType": {
"name": "Mutation"
},
"subscriptionType": null,
"types": [
{
@@ -322,6 +324,229 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "ChangePizzaDto",
"description": null,
"fields": null,
"inputFields": [
{
"name": "name",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "toppingIds",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
}
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "ChangeToppingDto",
"description": null,
"fields": null,
"inputFields": [
{
"name": "name",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Mutation",
"description": null,
"fields": [
{
"name": "createPizza",
"description": null,
"args": [
{
"name": "createPizzaDto",
"description": null,
"type": {
"kind": "INPUT_OBJECT",
"name": "ChangePizzaDto",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Pizza",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updatePizza",
"description": null,
"args": [
{
"name": "pizzaId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "updatedPizzaDto",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "ChangePizzaDto",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Pizza",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "createTopping",
"description": null,
"args": [
{
"name": "createToppingDto",
"description": null,
"type": {
"kind": "INPUT_OBJECT",
"name": "ChangeToppingDto",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Topping",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateTopping",
"description": null,
"args": [
{
"name": "toppingId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "updatedToppingDto",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "ChangeToppingDto",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Topping",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "__Schema",

+ 23
- 3
backend/package-lock.json 查看文件

@@ -1303,6 +1303,11 @@
"@types/mime": "*"
}
},
"@types/uuid": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.0.0.tgz",
"integrity": "sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw=="
},
"@types/websocket": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.0.tgz",
@@ -3779,6 +3784,13 @@
"deprecated-decorator": "^0.1.6",
"iterall": "^1.1.3",
"uuid": "^3.1.0"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
}
}
},
"graphql-upload": {
@@ -6256,6 +6268,14 @@
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
}
},
"require-directory": {
@@ -7315,9 +7335,9 @@
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz",
"integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q=="
},
"valid-url": {
"version": "1.0.9",

+ 3
- 1
backend/package.json 查看文件

@@ -4,9 +4,11 @@
"description": "",
"main": "build/index.js",
"dependencies": {
"@types/uuid": "^8.0.0",
"apollo-server": "^2.15.1",
"graphql": "^15.3.0",
"typescript": "^3.9.6"
"typescript": "^3.9.6",
"uuid": "^8.2.0"
},
"devDependencies": {
"@graphql-codegen/cli": "1.16.3",

+ 1
- 1
backend/src/data/pizza-list.ts 查看文件

@@ -1,5 +1,4 @@
import { Pizza } from "../generated/graphql";
import { ToppingList } from "./topping-list";
import { ToppingResolver } from "../resolver/topping-resolver";

export const PizzaList: Pizza[] = [
@@ -23,6 +22,7 @@ export const PizzaList: Pizza[] = [
name: "420",
toppings: [
ToppingResolver.getByName("Pineapple"),
ToppingResolver.getByName("Fish_sticks"),
]
}
]

+ 1
- 1
backend/src/data/topping-list.ts 查看文件

@@ -19,7 +19,7 @@ export const ToppingList: Topping[] = [
},
{
id: "5",
name: "fish sticks",
name: "Fish_sticks",
}


+ 53
- 0
backend/src/generated/graphql.ts 查看文件

@@ -57,6 +57,45 @@ export type QueryGetToppingByNameArgs = {
toppingName: Scalars['ID'];
};

export type ChangePizzaDto = {
name: Scalars['String'];
toppingIds: Array<Maybe<Scalars['ID']>>;
};

export type ChangeToppingDto = {
name: Scalars['String'];
};

export type Mutation = {
__typename?: 'Mutation';
createPizza: Pizza;
updatePizza: Pizza;
createTopping: Topping;
updateTopping: Topping;
};


export type MutationCreatePizzaArgs = {
createPizzaDto?: Maybe<ChangePizzaDto>;
};


export type MutationUpdatePizzaArgs = {
pizzaId: Scalars['ID'];
updatedPizzaDto: ChangePizzaDto;
};


export type MutationCreateToppingArgs = {
createToppingDto?: Maybe<ChangeToppingDto>;
};


export type MutationUpdateToppingArgs = {
toppingId: Scalars['ID'];
updatedToppingDto: ChangeToppingDto;
};



export type ResolverTypeWrapper<T> = Promise<T> | T;
@@ -141,6 +180,9 @@ export type ResolversTypes = {
Topping: ResolverTypeWrapper<Topping>;
Query: ResolverTypeWrapper<{}>;
Boolean: ResolverTypeWrapper<Scalars['Boolean']>;
ChangePizzaDto: ChangePizzaDto;
ChangeToppingDto: ChangeToppingDto;
Mutation: ResolverTypeWrapper<{}>;
};

/** Mapping between all available schema types and the resolvers parents */
@@ -151,6 +193,9 @@ export type ResolversParentTypes = {
Topping: Topping;
Query: {};
Boolean: Scalars['Boolean'];
ChangePizzaDto: ChangePizzaDto;
ChangeToppingDto: ChangeToppingDto;
Mutation: {};
};

export type PizzaResolvers<ContextType = any, ParentType extends ResolversParentTypes['Pizza'] = ResolversParentTypes['Pizza']> = {
@@ -175,10 +220,18 @@ export type QueryResolvers<ContextType = any, ParentType extends ResolversParent
listTopping?: Resolver<Array<Maybe<ResolversTypes['Topping']>>, ParentType, ContextType>;
};

export type MutationResolvers<ContextType = any, ParentType extends ResolversParentTypes['Mutation'] = ResolversParentTypes['Mutation']> = {
createPizza?: Resolver<ResolversTypes['Pizza'], ParentType, ContextType, RequireFields<MutationCreatePizzaArgs, never>>;
updatePizza?: Resolver<ResolversTypes['Pizza'], ParentType, ContextType, RequireFields<MutationUpdatePizzaArgs, 'pizzaId' | 'updatedPizzaDto'>>;
createTopping?: Resolver<ResolversTypes['Topping'], ParentType, ContextType, RequireFields<MutationCreateToppingArgs, never>>;
updateTopping?: Resolver<ResolversTypes['Topping'], ParentType, ContextType, RequireFields<MutationUpdateToppingArgs, 'toppingId' | 'updatedToppingDto'>>;
};

export type Resolvers<ContextType = any> = {
Pizza?: PizzaResolvers<ContextType>;
Topping?: ToppingResolvers<ContextType>;
Query?: QueryResolvers<ContextType>;
Mutation?: MutationResolvers<ContextType>;
};



+ 15
- 1
backend/src/index.ts 查看文件

@@ -1,5 +1,5 @@
import { ApolloServer } from "apollo-server/dist";
import { Resolvers } from "./generated/graphql";
import { Resolvers, ChangePizzaDto, ChangeToppingDto } from "./generated/graphql";
import { pizzaSchema } from "./schema/pizza";
import { PizzaResolver } from "./resolver/pizza-resolver";
import { ToppingResolver } from "./resolver/topping-resolver";
@@ -45,6 +45,20 @@ const resolvers: Resolvers = {
listTopping: (root, args, context) => {
return ToppingResolver.list();
},
},
Mutation: {
createPizza: (root, args, context) => {
return PizzaResolver.create(args.createPizzaDto as ChangePizzaDto);
},
updatePizza: (root, args, context) => {
return PizzaResolver.update(args.pizzaId, args.updatedPizzaDto as ChangePizzaDto);
},
createTopping: (root, args, context) => {
return ToppingResolver.create(args.createToppingDto as ChangeToppingDto);
},
updateTopping: (root, args, context) => {
return ToppingResolver.update(args.toppingId, args.updatedToppingDto as ChangeToppingDto);
},
}
}


+ 33
- 1
backend/src/resolver/pizza-resolver.ts 查看文件

@@ -1,5 +1,8 @@
import { Topping, Pizza, } from "../generated/graphql";
import { Topping, Pizza, ChangePizzaDto, Maybe } from "../generated/graphql";
import { PizzaList } from "../data/pizza-list";
import { v4 as uuidv4 } from 'uuid';
import { ToppingResolver } from "./topping-resolver";

export class PizzaResolver {

static getById = (id: string): Pizza => {
@@ -14,4 +17,33 @@ export class PizzaResolver {
return PizzaList.filter(pizza => pizza.name === name)[0];
};

static create = (pizzaCreateDto: ChangePizzaDto): Pizza => {
const toppings: Topping[] = pizzaCreateDto.toppingIds
.map<string>((toppingId) => toppingId as string)
.map<Topping>((toppingId) => ToppingResolver.getById(toppingId));
const pizza: Pizza = {
id: uuidv4(),
name: pizzaCreateDto.name,
toppings: toppings
}
console.log(`Create Pizza ...`, pizza)
return pizza;
};

static update = (pizzaId: string, pizzaUpdateDto: ChangePizzaDto): Pizza => {
const pizza = PizzaResolver.getById(pizzaId);
if (!pizza) {
throw new Error(
`No Pizza found with id ${pizzaId}`
)
}
const toppings: Topping[] = pizzaUpdateDto.toppingIds
.map<string>((toppingId: Maybe<string>) => toppingId as string)
.map<Topping>((toppingId: string) => ToppingResolver.getById(toppingId));
pizza.name = pizzaUpdateDto.name;
pizza.toppings = toppings;
console.log(`Update Pizza ...`, pizza)
return pizza;
};

}

+ 25
- 1
backend/src/resolver/topping-resolver.ts 查看文件

@@ -1,5 +1,7 @@
import { Topping, } from "../generated/graphql";
import { Topping, ChangeToppingDto, } from "../generated/graphql";
import { ToppingList } from "../data/topping-list";
import { v4 as uuidv4 } from 'uuid';
import { EEXIST } from "constants";
export class ToppingResolver {
static getById = (id: string): Topping => {
return ToppingList.filter(topping => topping.id === id)[0];
@@ -13,4 +15,26 @@ export class ToppingResolver {
return ToppingList;
};

static create = (toppingCreateDto: ChangeToppingDto): Topping => {
const topping: Topping = {
id: uuidv4(),
name: toppingCreateDto.name,
}
console.log(`Create Topping ...`, topping)
return topping;
};


static update = (toppingId: string, toppingUpdateDto: ChangeToppingDto): Topping => {
const topping = ToppingResolver.getById(toppingId);
if (!topping) {
throw new Error(
`No Topping found with id ${toppingId}`
)
}
topping.name = toppingUpdateDto.name;
console.log(`Update Topping ...`, topping)
return topping;
};

}

+ 23
- 1
backend/src/schema/pizza.ts 查看文件

@@ -21,4 +21,26 @@ export const pizzaSchema = gql`
listPizza: [Pizza]!
listTopping: [Topping]!
}
`;

interface MutationResponse {
code: String!
success: Boolean!
message: String!
}

input ChangePizzaDto {
name: String!
toppingIds: [ID]!
}

input ChangeToppingDto {
name: String!
}

type Mutation {
createPizza(createPizzaDto: ChangePizzaDto): Pizza!
updatePizza(pizzaId: ID!, updatedPizzaDto: ChangePizzaDto!): Pizza!
createTopping(createToppingDto: ChangeToppingDto): Topping!
updateTopping(toppingId: ID!, updatedToppingDto: ChangeToppingDto!): Topping!
}
`;

二进制
presentation/assets/apollo-logo.png 查看文件

之前 之后
宽度: 579  |  高度: 200  |  大小: 9.5KB

二进制
presentation/assets/graphql-architecture.png 查看文件

之前 之后
宽度: 831  |  高度: 723  |  大小: 36KB

二进制
presentation/assets/pizza-graphql.png 查看文件

之前 之后
宽度: 1149  |  高度: 391  |  大小: 39KB 宽度: 1149  |  高度: 392  |  大小: 39KB

二进制
presentation/assets/pizza-rest.png 查看文件

之前 之后
宽度: 1149  |  高度: 551  |  大小: 57KB 宽度: 1149  |  高度: 551  |  大小: 63KB

+ 1
- 1
presentation/assets/pizza.xml
文件差异内容过多而无法显示
查看文件


二进制
presentation/assets/pros-cons.png 查看文件

之前 之后
宽度: 696  |  高度: 930  |  大小: 73KB

二进制
presentation/assets/restful-architecture.png 查看文件

之前 之后
宽度: 831  |  高度: 723  |  大小: 43KB

二进制
presentation/assets/self-doku.png 查看文件

之前 之后
宽度: 623  |  高度: 579  |  大小: 42KB

+ 6
- 0
presentation/css/layout.scss 查看文件

@@ -55,3 +55,9 @@
.reveal .justify-start { justify-content: flex-start; }
.reveal .justify-center { justify-content: center; }
.reveal .justify-end { justify-content: flex-end; }


.scrollable-slide {
height: 800px;
overflow-y: auto !important;
}

+ 1
- 1
presentation/dist/reveal.css
文件差异内容过多而无法显示
查看文件


+ 217
- 5
presentation/index.html 查看文件

@@ -49,6 +49,22 @@
<section>
<h2>Pizza service</h2>
</section>
<section class="scrollable-slide">
<img
src="/assets/restful-architecture.png"
alt="rest-architecture"
style="background: transparent;"
class="demo-logo"
/>
</section>
<section class="scrollable-slide">
<img
src="/assets/graphql-architecture.png"
alt="graphql-architecture"
style="background: transparent;"
class="demo-logo"
/>
</section>
<section>
<h3>Rest Service</h3>
<img
@@ -67,11 +83,207 @@
class="demo-logo"
/>
</section>
</section>
<section>
</section>
</section>
<section>
<section>
<h1>benefits</h1>
</section>
<section>
<h2>No more Over- and Underfetching</h2>
fixed data structures provide to much or not enough data
</section>
<section>
<h2>Type/Schema System</h2>
<ul>
<li>Graphql uses an strict Type and Schema</li>
<li>The Client can be sure about what it get</li>
<li>aswell as the server</li>
</ul>
</section>
<section>
<h2>Growing Community and Integrations</h2>
<ul>
<li>
Good Client side Integrations Angular/React/VueJs and more.
</li>
<li>Server side Integrations in Typescript/Java and more.</li>
<li>Well working code-generator for client and server</li>
<li>Alot of well writen Documentation</li>
</ul>
</section>
<section>
<h2>Self Documenting</h2>
<img
src="/assets/self-doku.png"
alt="self-doku example"
style="background: transparent;"
class="demo-logo"
/>
</section>
<section>
<h2>Faster API Development</h2>
</section>
<section class="scrollable-slide">
<h2>pros/cons</h2>
<a
style="font-size: 14px;"
href="https://www.altexsoft.com/blog/engineering/graphql-core-features-architecture-pros-and-cons/"
>
<img
src="/assets/pros-cons.png"
alt="pros-cons"
style="background: transparent;"
class="demo-logo"
/>
</a>
</section>
</section>
<section>
<section>
<h1>disadvantages</h1>
</section>
<section>
<h3>more complex</h3>
</section>
<section>
<h3>no file uploading</h3>
</section>
</section>
<section>
<section>
<h1>Demo</h1>
</section>
<section>
<h2>Basics</h2>
</section>
<section>
<ul>
<li>
Types
</li>
<li>
Querys
</li>
<li>
Mutations
</li>
<li>
Variables
</li>
</ul>
</section>
<section>
<h2>Types</h2>
</section>
<section>
<pre><code data-trim data-noescape>
type Pizza {
id: ID!
name: String!
toppings: [Topping!]!
}

type Topping {
id: ID!
name: String!
}
</code>
</pre>
</section>
<section>
<h2>Querys</h2>
</section>
<section>
<h3>Define Querys</h3>
<pre>
<code data-trim data-noescape>
type Query {
getPizzaById(pizzaId: ID!): Pizza!
getToppingById(toppingId: ID!): Topping!
getPizzaByName(pizzaName: ID!): Pizza!
getToppingByName(toppingName: ID!): Topping!
listPizza: [Pizza]!
listTopping: [Topping]!
}
</code>
</pre>
</section>
<section>
<h3>Simple Query</h3>
<pre><code data-trim data-noescape>
{
listPizza {
id
name
}
}
</code>
</pre>
</section>
<section>
<h3>More Complex Query</h3>
<pre>
<code data-trim data-noescape>
{
getPizzaById(pizzaId: "0") {
id
name
toppings {
name
}
}
}
</code>
</pre>
</section>

<section>
<h2>Mutations</h2>
</section>
<section>
<h3>Define Mutations</h3>
<pre><code data-trim data-noescape>
type Mutation {
createPizza(createPizzaDto: ChangePizzaDto): Pizza!
updatePizza(pizzaId: ID!, updatedPizzaDto: ChangePizzaDto!): Pizza!
createTopping(createToppingDto: ChangeToppingDto): Topping!
updateTopping(toppingId: ID!, updatedToppingDto: ChangeToppingDto!): Topping!
}
</code>
</pre>
</section>
<section>
<h3>Mutation</h3>
<pre><code data-trim data-noescape>
mutation {
createPizza (createPizzaDto: {
name: "My Awesome Pizza"
toppingIds: "1"
}) {
id
name
toppings {
id
name
}
}
}
</code>
</pre>
</section>

<section>
<img
src="/assets/apollo-logo.png"
alt="apollo logo"
style="background: transparent;"
class="demo-logo"
/>
<p>graphql-codegen</p>
<p>apollo-angular</p>
</section>
</section>
</div>
</div>


正在加载...
取消
保存