| @@ -54,7 +54,7 @@ npx tsc --init --rootDir src --outDir build \ | |||
| ``` | |||
| ``` | |||
| npm install --save-dev ts-node nodemon rimraf uuid @types/uuid | |||
| npm install --save-dev ts-node nodemon rimraf uuid @types/uuid apollo-link-ws | |||
| ``` | |||
| @@ -7,7 +7,9 @@ | |||
| "mutationType": { | |||
| "name": "Mutation" | |||
| }, | |||
| "subscriptionType": null, | |||
| "subscriptionType": { | |||
| "name": "Subscription" | |||
| }, | |||
| "types": [ | |||
| { | |||
| "kind": "OBJECT", | |||
| @@ -547,6 +549,57 @@ | |||
| "enumValues": null, | |||
| "possibleTypes": null | |||
| }, | |||
| { | |||
| "kind": "OBJECT", | |||
| "name": "Subscription", | |||
| "description": null, | |||
| "fields": [ | |||
| { | |||
| "name": "pizzasChanged", | |||
| "description": null, | |||
| "args": [], | |||
| "type": { | |||
| "kind": "NON_NULL", | |||
| "name": null, | |||
| "ofType": { | |||
| "kind": "LIST", | |||
| "name": null, | |||
| "ofType": { | |||
| "kind": "OBJECT", | |||
| "name": "Pizza", | |||
| "ofType": null | |||
| } | |||
| } | |||
| }, | |||
| "isDeprecated": false, | |||
| "deprecationReason": null | |||
| }, | |||
| { | |||
| "name": "toppingsChanged", | |||
| "description": null, | |||
| "args": [], | |||
| "type": { | |||
| "kind": "NON_NULL", | |||
| "name": null, | |||
| "ofType": { | |||
| "kind": "LIST", | |||
| "name": null, | |||
| "ofType": { | |||
| "kind": "OBJECT", | |||
| "name": "Topping", | |||
| "ofType": null | |||
| } | |||
| } | |||
| }, | |||
| "isDeprecated": false, | |||
| "deprecationReason": null | |||
| } | |||
| ], | |||
| "inputFields": null, | |||
| "interfaces": [], | |||
| "enumValues": null, | |||
| "possibleTypes": null | |||
| }, | |||
| { | |||
| "kind": "OBJECT", | |||
| "name": "__Schema", | |||
| @@ -96,6 +96,12 @@ export type MutationUpdateToppingArgs = { | |||
| updatedToppingDto: ChangeToppingDto; | |||
| }; | |||
| export type Subscription = { | |||
| __typename?: 'Subscription'; | |||
| pizzasChanged: Array<Maybe<Pizza>>; | |||
| toppingsChanged: Array<Maybe<Topping>>; | |||
| }; | |||
| export type ResolverTypeWrapper<T> = Promise<T> | T; | |||
| @@ -183,6 +189,7 @@ export type ResolversTypes = { | |||
| ChangePizzaDto: ChangePizzaDto; | |||
| ChangeToppingDto: ChangeToppingDto; | |||
| Mutation: ResolverTypeWrapper<{}>; | |||
| Subscription: ResolverTypeWrapper<{}>; | |||
| }; | |||
| /** Mapping between all available schema types and the resolvers parents */ | |||
| @@ -196,6 +203,7 @@ export type ResolversParentTypes = { | |||
| ChangePizzaDto: ChangePizzaDto; | |||
| ChangeToppingDto: ChangeToppingDto; | |||
| Mutation: {}; | |||
| Subscription: {}; | |||
| }; | |||
| export type PizzaResolvers<ContextType = any, ParentType extends ResolversParentTypes['Pizza'] = ResolversParentTypes['Pizza']> = { | |||
| @@ -227,11 +235,17 @@ export type MutationResolvers<ContextType = any, ParentType extends ResolversPar | |||
| updateTopping?: Resolver<ResolversTypes['Topping'], ParentType, ContextType, RequireFields<MutationUpdateToppingArgs, 'toppingId' | 'updatedToppingDto'>>; | |||
| }; | |||
| export type SubscriptionResolvers<ContextType = any, ParentType extends ResolversParentTypes['Subscription'] = ResolversParentTypes['Subscription']> = { | |||
| pizzasChanged?: SubscriptionResolver<Array<Maybe<ResolversTypes['Pizza']>>, "pizzasChanged", ParentType, ContextType>; | |||
| toppingsChanged?: SubscriptionResolver<Array<Maybe<ResolversTypes['Topping']>>, "toppingsChanged", ParentType, ContextType>; | |||
| }; | |||
| export type Resolvers<ContextType = any> = { | |||
| Pizza?: PizzaResolvers<ContextType>; | |||
| Topping?: ToppingResolvers<ContextType>; | |||
| Query?: QueryResolvers<ContextType>; | |||
| Mutation?: MutationResolvers<ContextType>; | |||
| Subscription?: SubscriptionResolvers<ContextType>; | |||
| }; | |||
| @@ -1,11 +1,9 @@ | |||
| import { ApolloServer } from "apollo-server/dist"; | |||
| import { ApolloServer, PubSub } from "apollo-server/dist"; | |||
| import { Resolvers, ChangePizzaDto, ChangeToppingDto } from "./generated/graphql"; | |||
| import { pizzaSchema } from "./schema/pizza"; | |||
| import { PizzaResolver } from "./resolver/pizza-resolver"; | |||
| import { ToppingResolver } from "./resolver/topping-resolver"; | |||
| const resolvers: Resolvers = { | |||
| Topping: { | |||
| id: (root, args, context) => { | |||
| @@ -59,14 +57,25 @@ const resolvers: Resolvers = { | |||
| updateTopping: (root, args, context) => { | |||
| return ToppingResolver.update(args.toppingId, args.updatedToppingDto as ChangeToppingDto); | |||
| }, | |||
| }, | |||
| Subscription: { | |||
| pizzasChanged: { | |||
| subscribe: () => PizzaResolver.pizzaEvent.asyncIterator(PizzaResolver.events.PIZZA_CHANGED) | |||
| }, | |||
| toppingsChanged: { | |||
| subscribe: () => ToppingResolver.toppingEvent.asyncIterator(ToppingResolver.events.TOPPING_CHANGED) | |||
| }, | |||
| } | |||
| } | |||
| const server = new ApolloServer({ | |||
| typeDefs: pizzaSchema, | |||
| resolvers: resolvers as any | |||
| }); | |||
| resolvers: resolvers as any, | |||
| } | |||
| ); | |||
| server.listen().then(({ url }) => { | |||
| server.listen().then(({ url, subscriptionsUrl }) => { | |||
| console.log(`🚀 Server ready at ${url}`) | |||
| console.log(`🚀 Subscriptions ready at ${subscriptionsUrl}`); | |||
| }); | |||
| @@ -2,9 +2,15 @@ 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"; | |||
| import { PubSub } from "apollo-server"; | |||
| export class PizzaResolver { | |||
| static pizzaEvent = new PubSub(); | |||
| static events = { | |||
| PIZZA_CHANGED: "pizzaChanged", | |||
| } | |||
| static getById = (id: string): Pizza => { | |||
| return PizzaList.filter(pizza => pizza.id === id)[0]; | |||
| }; | |||
| @@ -18,14 +24,21 @@ export class PizzaResolver { | |||
| }; | |||
| static create = (pizzaCreateDto: ChangePizzaDto): Pizza => { | |||
| // Get Topping Objects by Ids | |||
| const toppings: Topping[] = pizzaCreateDto.toppingIds | |||
| .map<string>((toppingId) => toppingId as string) | |||
| .map<Topping>((toppingId) => ToppingResolver.getById(toppingId)); | |||
| // Init new Pizza Object | |||
| const pizza: Pizza = { | |||
| id: uuidv4(), | |||
| name: pizzaCreateDto.name, | |||
| toppings: toppings | |||
| } | |||
| // Add Pizza to 'Database' | |||
| PizzaList.push(pizza); | |||
| PizzaResolver.pizzaEvent.publish(PizzaResolver.events.PIZZA_CHANGED, { | |||
| pizzasChanged: PizzaList | |||
| }); | |||
| console.log(`Create Pizza ...`, pizza) | |||
| return pizza; | |||
| }; | |||
| @@ -42,7 +55,12 @@ export class PizzaResolver { | |||
| .map<Topping>((toppingId: string) => ToppingResolver.getById(toppingId)); | |||
| pizza.name = pizzaUpdateDto.name; | |||
| pizza.toppings = toppings; | |||
| const pizzaIndex = PizzaList.indexOf(pizza); | |||
| PizzaList[pizzaIndex] = pizza; | |||
| console.log(`Update Pizza ...`, pizza) | |||
| PizzaResolver.pizzaEvent.publish(PizzaResolver.events.PIZZA_CHANGED, { | |||
| pizzasChanged: PizzaList | |||
| }); | |||
| return pizza; | |||
| }; | |||
| @@ -2,7 +2,16 @@ import { Topping, ChangeToppingDto, } from "../generated/graphql"; | |||
| import { ToppingList } from "../data/topping-list"; | |||
| import { v4 as uuidv4 } from 'uuid'; | |||
| import { EEXIST } from "constants"; | |||
| import { PubSub } from "apollo-server"; | |||
| export class ToppingResolver { | |||
| static toppingEvent = new PubSub(); | |||
| static events = { | |||
| TOPPING_CHANGED: "toppingChanged", | |||
| } | |||
| static getById = (id: string): Topping => { | |||
| return ToppingList.filter(topping => topping.id === id)[0]; | |||
| }; | |||
| @@ -20,7 +29,11 @@ export class ToppingResolver { | |||
| id: uuidv4(), | |||
| name: toppingCreateDto.name, | |||
| } | |||
| console.log(`Create Topping ...`, topping) | |||
| ToppingList.push(topping); | |||
| console.log(`Create Topping ...`, topping); | |||
| ToppingResolver.toppingEvent.publish(ToppingResolver.events.TOPPING_CHANGED, { | |||
| toppingsChanged: ToppingList | |||
| }); | |||
| return topping; | |||
| }; | |||
| @@ -32,7 +45,12 @@ export class ToppingResolver { | |||
| `No Topping found with id ${toppingId}` | |||
| ) | |||
| } | |||
| topping.name = toppingUpdateDto.name; | |||
| topping.name = toppingUpdateDto.name; | |||
| const toppingIndex = ToppingList.indexOf(topping); | |||
| ToppingList[toppingIndex] = topping; | |||
| ToppingResolver.toppingEvent.publish(ToppingResolver.events.TOPPING_CHANGED, { | |||
| toppingsChanged: ToppingList | |||
| }); | |||
| console.log(`Update Topping ...`, topping) | |||
| return topping; | |||
| }; | |||
| @@ -43,4 +43,9 @@ export const pizzaSchema = gql` | |||
| createTopping(createToppingDto: ChangeToppingDto): Topping! | |||
| updateTopping(toppingId: ID!, updatedToppingDto: ChangeToppingDto!): Topping! | |||
| } | |||
| type Subscription { | |||
| pizzasChanged: [Pizza]! | |||
| toppingsChanged: [Topping]! | |||
| } | |||
| `; | |||
| @@ -7,7 +7,7 @@ | |||
| ] /* Specify library files to be included in the compilation. */, | |||
| "allowJs": true /* Allow javascript files to be compiled. */, | |||
| "outDir": "build" /* Redirect output structure to the directory. */, | |||
| "rootDir": "../srceal.js/assets/src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | |||
| "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | |||
| "strict": true /* Enable all strict type-checking options. */, | |||
| "skipLibCheck": true, | |||
| "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, | |||
| @@ -3281,6 +3281,22 @@ | |||
| } | |||
| } | |||
| }, | |||
| "apollo-link-ws": { | |||
| "version": "1.0.20", | |||
| "resolved": "https://registry.npmjs.org/apollo-link-ws/-/apollo-link-ws-1.0.20.tgz", | |||
| "integrity": "sha512-mjSFPlQxmoLArpHBeUb2Xj+2HDYeTaJqFGOqQ+I8NVJxgL9lJe84PDWcPah/yMLv3rB7QgBDSuZ0xoRFBPlySw==", | |||
| "requires": { | |||
| "apollo-link": "^1.2.14", | |||
| "tslib": "^1.9.3" | |||
| }, | |||
| "dependencies": { | |||
| "tslib": { | |||
| "version": "1.13.0", | |||
| "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", | |||
| "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" | |||
| } | |||
| } | |||
| }, | |||
| "apollo-utilities": { | |||
| "version": "1.3.4", | |||
| "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz", | |||
| @@ -3486,8 +3502,7 @@ | |||
| "async-limiter": { | |||
| "version": "1.0.1", | |||
| "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", | |||
| "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", | |||
| "dev": true | |||
| "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" | |||
| }, | |||
| "asynckit": { | |||
| "version": "0.4.0", | |||
| @@ -3648,8 +3663,7 @@ | |||
| "backo2": { | |||
| "version": "1.0.2", | |||
| "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", | |||
| "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", | |||
| "dev": true | |||
| "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" | |||
| }, | |||
| "balanced-match": { | |||
| "version": "1.0.0", | |||
| @@ -8240,8 +8254,7 @@ | |||
| "iterall": { | |||
| "version": "1.3.0", | |||
| "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", | |||
| "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==", | |||
| "dev": true | |||
| "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" | |||
| }, | |||
| "jasmine": { | |||
| "version": "2.8.0", | |||
| @@ -14214,7 +14227,6 @@ | |||
| "version": "0.9.17", | |||
| "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.17.tgz", | |||
| "integrity": "sha512-hNHi2N80PBz4T0V0QhnnsMGvG3XDFDS9mS6BhZ3R12T6EBywC8d/uJscsga0cVO4DKtXCkCRrWm2sOYrbOdhEA==", | |||
| "dev": true, | |||
| "requires": { | |||
| "backo2": "^1.0.2", | |||
| "eventemitter3": "^3.1.0", | |||
| @@ -14226,14 +14238,12 @@ | |||
| "eventemitter3": { | |||
| "version": "3.1.2", | |||
| "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", | |||
| "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", | |||
| "dev": true | |||
| "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" | |||
| }, | |||
| "ws": { | |||
| "version": "5.2.2", | |||
| "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", | |||
| "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", | |||
| "dev": true, | |||
| "requires": { | |||
| "async-limiter": "~1.0.0" | |||
| } | |||
| @@ -27,9 +27,11 @@ | |||
| "apollo-cache-inmemory": "^1.6.0", | |||
| "apollo-client": "^2.6.0", | |||
| "apollo-link": "^1.2.11", | |||
| "apollo-link-ws": "^1.0.20", | |||
| "graphql": "^15.3.0", | |||
| "graphql-tag": "^2.10.0", | |||
| "rxjs": "~6.5.5", | |||
| "subscriptions-transport-ws": "^0.9.17", | |||
| "tslib": "^2.0.0", | |||
| "zone.js": "~0.10.3" | |||
| }, | |||
| @@ -15,6 +15,12 @@ | |||
| <mat-sidenav-content> | |||
| <app-pizza-list></app-pizza-list> | |||
| <app-pizza-list-with-topping></app-pizza-list-with-topping> | |||
| <div class=row> | |||
| <app-pizza-create class="col-6"></app-pizza-create> | |||
| <app-topping-create class="col-6"></app-topping-create> | |||
| </div> | |||
| <mat-toolbar class="footer"></mat-toolbar> | |||
| </mat-sidenav-content> | |||
| </mat-sidenav-container> | |||
| @@ -1,32 +1,46 @@ | |||
| import { BrowserModule } from '@angular/platform-browser'; | |||
| import { CommonModule } from '@angular/common'; | |||
| import { HttpClientModule } from '@angular/common/http'; | |||
| import { NgModule } from '@angular/core'; | |||
| import { ReactiveFormsModule } from '@angular/forms'; | |||
| import { MatButtonModule } from '@angular/material/button'; | |||
| import { MatCardModule } from '@angular/material/card'; | |||
| import { MatCheckboxModule } from '@angular/material/checkbox'; | |||
| import { MatFormFieldModule } from '@angular/material/form-field'; | |||
| import { MatIconModule } from '@angular/material/icon'; | |||
| import { MatInputModule } from '@angular/material/input'; | |||
| import { MatListModule } from '@angular/material/list'; | |||
| import { MatMenuModule } from '@angular/material/menu'; | |||
| import { MatSelectModule } from '@angular/material/select'; | |||
| import { MatSidenavModule } from '@angular/material/sidenav'; | |||
| import { MatTableModule } from '@angular/material/table'; | |||
| import { MatTabsModule } from '@angular/material/tabs'; | |||
| import { MatToolbarModule } from '@angular/material/toolbar'; | |||
| import { MatButtonModule } from '@angular/material/button'; | |||
| import { AppComponent } from './app.component'; | |||
| import { GraphQLModule } from '../graphql.module'; | |||
| import { HttpClientModule } from '@angular/common/http'; | |||
| import { MatSnackBarModule } from '@angular/material/snack-bar'; | |||
| import { BrowserModule } from '@angular/platform-browser'; | |||
| import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | |||
| import { PizzaListComponent } from './pizza-list/pizza-list.component'; | |||
| import { Apollo } from 'apollo-angular'; | |||
| import { HttpLink } from 'apollo-angular-link-http'; | |||
| import { GraphQLModule } from '../graphql.module'; | |||
| import { AppComponent } from './app.component'; | |||
| import { PizzaCreateComponent } from './pizza-create/pizza-create.component'; | |||
| import { PizzaListWithToppingComponent } from './pizza-list-with-topping/pizza-list-with-topping.component'; | |||
| import { CommonModule } from '@angular/common'; | |||
| import { FormsModule } from '@angular/forms'; | |||
| import { PizzaListComponent } from './pizza-list/pizza-list.component'; | |||
| import { ToppingCreateComponent } from './topping-create/topping-create.component'; | |||
| @NgModule({ | |||
| declarations: [ | |||
| AppComponent, | |||
| PizzaListComponent, | |||
| PizzaListWithToppingComponent, | |||
| PizzaCreateComponent, | |||
| ToppingCreateComponent, | |||
| ], | |||
| imports: [ | |||
| MatSnackBarModule, | |||
| MatSelectModule, | |||
| MatFormFieldModule, | |||
| MatCheckboxModule, | |||
| MatCardModule, | |||
| MatInputModule, | |||
| @@ -41,7 +55,7 @@ import { FormsModule } from '@angular/forms'; | |||
| BrowserModule, | |||
| HttpClientModule, | |||
| CommonModule, | |||
| FormsModule, | |||
| ReactiveFormsModule, | |||
| GraphQLModule, | |||
| BrowserModule, | |||
| GraphQLModule, | |||
| @@ -51,4 +65,40 @@ import { FormsModule } from '@angular/forms'; | |||
| providers: [], | |||
| bootstrap: [AppComponent] | |||
| }) | |||
| export class AppModule { } | |||
| export class AppModule { | |||
| constructor( | |||
| apollo: Apollo, | |||
| httpLink: HttpLink | |||
| ) { | |||
| // // Create an http link: | |||
| // const http = httpLink.create({ | |||
| // uri: 'http://localhost:4000' | |||
| // }); | |||
| // // Create a WebSocket link: | |||
| // const ws = new WebSocketLink({ | |||
| // uri: `ws://localhost:4000/`, | |||
| // options: { | |||
| // reconnect: true | |||
| // } | |||
| // }); | |||
| // // using the ability to split links, you can send data to each link | |||
| // // depending on what kind of operation is being sent | |||
| // const link = split( | |||
| // // split based on operation type | |||
| // ({ query }) => { | |||
| // const { kind } = getMainDefinition(query); | |||
| // return kind === 'OperationDefinition'; | |||
| // }, | |||
| // ws, | |||
| // http, | |||
| // ); | |||
| // apollo.create({ | |||
| // link, | |||
| // cache: null | |||
| // }); | |||
| } | |||
| } | |||
| @@ -0,0 +1,6 @@ | |||
| subscription ToppingChanged { | |||
| toppingsChanged { | |||
| id | |||
| name | |||
| } | |||
| } | |||
| @@ -0,0 +1,6 @@ | |||
| query ListToppingsForPizzaCreate { | |||
| listTopping { | |||
| id | |||
| name | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| <div class="container"> | |||
| <form [formGroup]="pizzaCreateForm" (ngSubmit)="create()" class="form"> | |||
| <h1>Create Pizza</h1> | |||
| <mat-form-field class="form-element"> | |||
| <mat-label> | |||
| Name | |||
| </mat-label> | |||
| <input matInput type="text" formControlName="pizzaName" /> | |||
| </mat-form-field> | |||
| <mat-form-field appearance="fill"> | |||
| <mat-label>Toppings</mat-label> | |||
| <mat-select | |||
| name="selectedToppings" | |||
| [formControl]="pizzaToppings" | |||
| multiple | |||
| > | |||
| <mat-select-trigger> | |||
| {{ pizzaToppings.value ? pizzaToppings.value[0]?.name : "" }} | |||
| <span | |||
| *ngIf="pizzaToppings.value?.length > 1" | |||
| class="example-additional-selection" | |||
| > | |||
| (+{{ pizzaToppings.value?.length - 1 }} | |||
| {{ pizzaToppings.value?.length === 2 ? "other" : "others" }}) | |||
| </span> | |||
| </mat-select-trigger> | |||
| <mat-option *ngFor="let topping of toppings" [value]="topping">{{ | |||
| topping.name | |||
| }}</mat-option> | |||
| </mat-select> | |||
| </mat-form-field> | |||
| <br> | |||
| <button mat-button type="submit">Submit</button> | |||
| </form> | |||
| </div> | |||
| @@ -0,0 +1,25 @@ | |||
| import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { PizzaCreateComponent } from './pizza-create.component'; | |||
| describe('PizzaCreateComponent', () => { | |||
| let component: PizzaCreateComponent; | |||
| let fixture: ComponentFixture<PizzaCreateComponent>; | |||
| beforeEach(async(() => { | |||
| TestBed.configureTestingModule({ | |||
| declarations: [ PizzaCreateComponent ] | |||
| }) | |||
| .compileComponents(); | |||
| })); | |||
| beforeEach(() => { | |||
| fixture = TestBed.createComponent(PizzaCreateComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,69 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import { FormControl, FormGroup } from '@angular/forms'; | |||
| import { map } from 'rxjs/operators'; | |||
| import { ChangePizzaDto, CreatePizzaGQL, ListToppingsForPizzaCreateGQL, Topping, ToppingChangedGQL } from 'src/generated/graphql'; | |||
| import { MatSnackBar } from '@angular/material/snack-bar'; | |||
| @Component({ | |||
| selector: 'app-pizza-create', | |||
| templateUrl: './pizza-create.component.html', | |||
| styleUrls: ['./pizza-create.component.scss'] | |||
| }) | |||
| export class PizzaCreateComponent implements OnInit { | |||
| pizzaName = new FormControl(''); | |||
| pizzaToppings = new FormControl([]); | |||
| pizzaCreateForm = new FormGroup({ | |||
| pizzaName: this.pizzaName, | |||
| pizzaToppings: this.pizzaToppings, | |||
| }); | |||
| toppings: Topping[]; | |||
| create(): void { | |||
| if (!this.pizzaCreateForm.valid) { | |||
| return; | |||
| } | |||
| const createPizzaDto: ChangePizzaDto = { | |||
| name: this.pizzaName.value, | |||
| toppingIds: this.pizzaToppings.value.map((topping: Topping) => topping.id) | |||
| }; | |||
| this.createPizza | |||
| .mutate({ createPizzaDto }) | |||
| .subscribe((res) => { | |||
| this.snackBar.open( | |||
| `created Pizza ${res.data.createPizza.name}`, | |||
| 'ok', | |||
| { | |||
| duration: 1000 | |||
| } | |||
| ); | |||
| }); | |||
| this.pizzaName.setValue(null); | |||
| this.pizzaToppings.setValue([]); | |||
| } | |||
| ngOnInit(): void { | |||
| } | |||
| constructor( | |||
| listToppings: ListToppingsForPizzaCreateGQL, | |||
| protected createPizza: CreatePizzaGQL, | |||
| toppingChanged: ToppingChangedGQL, | |||
| private snackBar: MatSnackBar | |||
| ) { | |||
| listToppings | |||
| .watch() | |||
| .valueChanges | |||
| .pipe(map(result => result.data.listTopping as Topping[])) | |||
| .subscribe(toppings => this.toppings = toppings); | |||
| toppingChanged | |||
| .subscribe() | |||
| .pipe(map(event => event.data.toppingsChanged as Topping[])) | |||
| .subscribe(toppings => this.toppings = toppings); | |||
| } | |||
| } | |||
| @@ -0,0 +1,5 @@ | |||
| mutation CreatePizza($createPizzaDto: ChangePizzaDto) { | |||
| createPizza (createPizzaDto: $createPizzaDto) { | |||
| name | |||
| } | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| subscription PizzaWithToppingChanged { | |||
| pizzasChanged { | |||
| id, | |||
| name | |||
| toppings { | |||
| name | |||
| } | |||
| } | |||
| } | |||
| @@ -1,34 +1,36 @@ | |||
| <mat-toolbar> | |||
| <mat-toolbar-row> | |||
| <h1>Pizzas With topping</h1> | |||
| </mat-toolbar-row> | |||
| </mat-toolbar> | |||
| <div class="container"> | |||
| <mat-toolbar> | |||
| <mat-toolbar-row> | |||
| <h1>Pizzas With topping</h1> | |||
| </mat-toolbar-row> | |||
| </mat-toolbar> | |||
| <table mat-table [dataSource]="pizzas"> | |||
| <ng-container matColumnDef="id"> | |||
| <th class="id" mat-header-cell *matHeaderCellDef>ID</th> | |||
| <td class="id" mat-cell *matCellDef="let pizza; let i = index"> | |||
| {{ pizza.id }} | |||
| </td> | |||
| </ng-container> | |||
| <table mat-table [dataSource]="pizzas"> | |||
| <ng-container matColumnDef="id"> | |||
| <th class="id" mat-header-cell *matHeaderCellDef>ID</th> | |||
| <td class="id" mat-cell *matCellDef="let pizza; let i = index"> | |||
| {{ pizza.id }} | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="name"> | |||
| <th class="name" mat-header-cell *matHeaderCellDef>Name</th> | |||
| <td class="name" mat-cell *matCellDef="let pizza; let i = index"> | |||
| {{ pizza.name }} | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="name"> | |||
| <th class="name" mat-header-cell *matHeaderCellDef>Name</th> | |||
| <td class="name" mat-cell *matCellDef="let pizza; let i = index"> | |||
| {{ pizza.name }} | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="toppings"> | |||
| <th class="toppings" mat-header-cell *matHeaderCellDef>Name</th> | |||
| <td class="toppings" mat-cell *matCellDef="let pizza; let i = index"> | |||
| {{ getToppingListString(pizza.toppings) }} | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="toppings"> | |||
| <th class="toppings" mat-header-cell *matHeaderCellDef>Name</th> | |||
| <td class="toppings" mat-cell *matCellDef="let pizza; let i = index"> | |||
| {{ getToppingListString(pizza.toppings) }} | |||
| </td> | |||
| </ng-container> | |||
| <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |||
| <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr> | |||
| </table> | |||
| <ng-template #nopizzaSelected> | |||
| no pizza selected, please select a pizza to see events! | |||
| </ng-template> | |||
| <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |||
| <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr> | |||
| </table> | |||
| <ng-template #nopizzaSelected> | |||
| no pizza selected, please select a pizza to see events! | |||
| </ng-template> | |||
| </div> | |||
| @@ -1,7 +1,7 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import { Pizza, ListPizzaGQL, ListPizzaWithToppingGQL, Topping } from 'src/generated/graphql'; | |||
| import { Observable } from 'rxjs'; | |||
| import { Subject } from 'rxjs'; | |||
| import { map } from 'rxjs/operators'; | |||
| import { ListPizzaWithToppingGQL, Pizza, PizzaWithToppingChangedGQL, Topping } from 'src/generated/graphql'; | |||
| @Component({ | |||
| @@ -18,15 +18,17 @@ export class PizzaListWithToppingComponent implements OnInit { | |||
| 'toppings' | |||
| ]; | |||
| pizzas: Observable<Pizza[]>; | |||
| pizzas = new Subject<Pizza[]>(); | |||
| constructor(listPizzaWithToppings: ListPizzaWithToppingGQL) { | |||
| this.pizzas = listPizzaWithToppings | |||
| constructor(listPizzaWithToppings: ListPizzaWithToppingGQL, pizzaChanged: PizzaWithToppingChangedGQL) { | |||
| listPizzaWithToppings | |||
| .watch() | |||
| .valueChanges | |||
| .pipe(map(result => result.data.listPizza as Pizza[])); | |||
| .pipe(map(result => result.data.listPizza as Pizza[])) | |||
| .subscribe((pizzas => this.pizzas.next(pizzas))); | |||
| pizzaChanged.subscribe().pipe(map(event => event.data.pizzasChanged as Pizza[])) | |||
| .subscribe((pizzas => this.pizzas.next(pizzas))); | |||
| } | |||
| ngOnInit(): void { | |||
| @@ -0,0 +1,6 @@ | |||
| subscription PizzaChanged { | |||
| pizzasChanged { | |||
| id | |||
| name | |||
| } | |||
| } | |||
| @@ -1,27 +1,29 @@ | |||
| <mat-toolbar> | |||
| <mat-toolbar-row> | |||
| <h1>Pizza</h1> | |||
| </mat-toolbar-row> | |||
| </mat-toolbar> | |||
| <div class="container"> | |||
| <mat-toolbar> | |||
| <mat-toolbar-row> | |||
| <h1>Pizza</h1> | |||
| </mat-toolbar-row> | |||
| </mat-toolbar> | |||
| <table mat-table [dataSource]="pizzas"> | |||
| <ng-container matColumnDef="id"> | |||
| <th class="id" mat-header-cell *matHeaderCellDef>ID</th> | |||
| <td class="id" mat-cell *matCellDef="let pizza; let i = index"> | |||
| {{ pizza.id }} | |||
| </td> | |||
| </ng-container> | |||
| <table mat-table [dataSource]="pizzas"> | |||
| <ng-container matColumnDef="id"> | |||
| <th class="id" mat-header-cell *matHeaderCellDef>ID</th> | |||
| <td class="id" mat-cell *matCellDef="let pizza; let i = index"> | |||
| {{ pizza.id }} | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="name"> | |||
| <th class="name" mat-header-cell *matHeaderCellDef>Name</th> | |||
| <td class="name" mat-cell *matCellDef="let pizza; let i = index"> | |||
| {{ pizza.name }} | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="name"> | |||
| <th class="name" mat-header-cell *matHeaderCellDef>Name</th> | |||
| <td class="name" mat-cell *matCellDef="let pizza; let i = index"> | |||
| {{ pizza.name }} | |||
| </td> | |||
| </ng-container> | |||
| <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |||
| <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr> | |||
| </table> | |||
| <ng-template #nopizzaSelected> | |||
| no pizza selected, please select a pizza to see events! | |||
| </ng-template> | |||
| <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |||
| <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr> | |||
| </table> | |||
| <ng-template #nopizzaSelected> | |||
| no pizza selected, please select a pizza to see events! | |||
| </ng-template> | |||
| </div> | |||
| @@ -1,6 +1,6 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import { Pizza, ListPizzaGQL } from 'src/generated/graphql'; | |||
| import { Observable } from 'rxjs'; | |||
| import { Component, OnInit, DebugElement } from '@angular/core'; | |||
| import { Pizza, ListPizzaGQL, PizzaChangedGQL } from 'src/generated/graphql'; | |||
| import { Observable, race, combineLatest, concat, Subject } from 'rxjs'; | |||
| import { map } from 'rxjs/operators'; | |||
| @@ -17,13 +17,17 @@ export class PizzaListComponent implements OnInit { | |||
| 'name', | |||
| ]; | |||
| pizzas: Observable<Pizza[]>; | |||
| pizzas = new Subject<Pizza[]>(); | |||
| constructor(listPizza: ListPizzaGQL) { | |||
| this.pizzas = listPizza | |||
| .watch() | |||
| .valueChanges | |||
| .pipe(map(result => result.data.listPizza as Pizza[])); | |||
| constructor(listPizza: ListPizzaGQL, pizzaChanged: PizzaChangedGQL) { | |||
| listPizza | |||
| .watch() | |||
| .valueChanges | |||
| .pipe(map(result => result.data.listPizza as Pizza[])) | |||
| .subscribe((pizzas => this.pizzas.next(pizzas))); | |||
| pizzaChanged.subscribe().pipe(map(event => event.data.pizzasChanged as Pizza[])) | |||
| .subscribe((pizzas => this.pizzas.next(pizzas))); | |||
| } | |||
| ngOnInit(): void { | |||
| @@ -0,0 +1,13 @@ | |||
| <div class="container"> | |||
| <form [formGroup]="toppingCreateForm" (ngSubmit)="create()" class="form"> | |||
| <h1>Create Topping</h1> | |||
| <mat-form-field class="form-element"> | |||
| <mat-label> | |||
| Name | |||
| </mat-label> | |||
| <input matInput type="text" formControlName="toppingName" /> | |||
| </mat-form-field> | |||
| <br> | |||
| <button mat-button type="submit">Submit</button> | |||
| </form> | |||
| </div> | |||
| @@ -0,0 +1,25 @@ | |||
| import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { ToppingCreateComponent } from './topping-create.component'; | |||
| describe('ToppingCreateComponent', () => { | |||
| let component: ToppingCreateComponent; | |||
| let fixture: ComponentFixture<ToppingCreateComponent>; | |||
| beforeEach(async(() => { | |||
| TestBed.configureTestingModule({ | |||
| declarations: [ ToppingCreateComponent ] | |||
| }) | |||
| .compileComponents(); | |||
| })); | |||
| beforeEach(() => { | |||
| fixture = TestBed.createComponent(ToppingCreateComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,49 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import { FormControl, FormGroup } from '@angular/forms'; | |||
| import { ChangeToppingDto, CreateToppingGQL } from 'src/generated/graphql'; | |||
| import { MatSnackBar } from '@angular/material/snack-bar'; | |||
| @Component({ | |||
| selector: 'app-topping-create', | |||
| templateUrl: './topping-create.component.html', | |||
| styleUrls: ['./topping-create.component.scss'] | |||
| }) | |||
| export class ToppingCreateComponent implements OnInit { | |||
| toppingName = new FormControl(''); | |||
| toppingCreateForm = new FormGroup({ | |||
| toppingName: this.toppingName, | |||
| }); | |||
| create(): void { | |||
| if (!this.toppingCreateForm.valid) { | |||
| return; | |||
| } | |||
| const createToppingDto: ChangeToppingDto = { | |||
| name: this.toppingName.value, | |||
| }; | |||
| this.createToping | |||
| .mutate({ createToppingDto }) | |||
| .subscribe((res) => { | |||
| this.snackBar.open( | |||
| `created Topping ${res.data.createTopping.name}`, | |||
| 'ok', | |||
| { | |||
| duration: 1000 | |||
| } | |||
| ); | |||
| }); | |||
| this.toppingName.setValue(null); | |||
| } | |||
| ngOnInit(): void { | |||
| } | |||
| constructor( | |||
| protected createToping: CreateToppingGQL, | |||
| private snackBar: MatSnackBar | |||
| ) { | |||
| } | |||
| } | |||
| @@ -0,0 +1,5 @@ | |||
| mutation CreateTopping($createToppingDto: ChangeToppingDto) { | |||
| createTopping (createToppingDto: $createToppingDto) { | |||
| name | |||
| } | |||
| } | |||
| @@ -59,12 +59,107 @@ 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 Subscription = { | |||
| __typename?: 'Subscription'; | |||
| pizzasChanged: Array<Maybe<Pizza>>; | |||
| toppingsChanged: Array<Maybe<Topping>>; | |||
| }; | |||
| export enum CacheControlScope { | |||
| Public = 'PUBLIC', | |||
| Private = 'PRIVATE' | |||
| } | |||
| export type ToppingChangedSubscriptionVariables = Exact<{ [key: string]: never; }>; | |||
| export type ToppingChangedSubscription = ( | |||
| { __typename?: 'Subscription' } | |||
| & { toppingsChanged: Array<Maybe<( | |||
| { __typename?: 'Topping' } | |||
| & Pick<Topping, 'id' | 'name'> | |||
| )>> } | |||
| ); | |||
| export type ListToppingsForPizzaCreateQueryVariables = Exact<{ [key: string]: never; }>; | |||
| export type ListToppingsForPizzaCreateQuery = ( | |||
| { __typename?: 'Query' } | |||
| & { listTopping: Array<Maybe<( | |||
| { __typename?: 'Topping' } | |||
| & Pick<Topping, 'id' | 'name'> | |||
| )>> } | |||
| ); | |||
| export type CreatePizzaMutationVariables = Exact<{ | |||
| createPizzaDto?: Maybe<ChangePizzaDto>; | |||
| }>; | |||
| export type CreatePizzaMutation = ( | |||
| { __typename?: 'Mutation' } | |||
| & { createPizza: ( | |||
| { __typename?: 'Pizza' } | |||
| & Pick<Pizza, 'name'> | |||
| ) } | |||
| ); | |||
| export type PizzaWithToppingChangedSubscriptionVariables = Exact<{ [key: string]: never; }>; | |||
| export type PizzaWithToppingChangedSubscription = ( | |||
| { __typename?: 'Subscription' } | |||
| & { pizzasChanged: Array<Maybe<( | |||
| { __typename?: 'Pizza' } | |||
| & Pick<Pizza, 'id' | 'name'> | |||
| & { toppings: Array<( | |||
| { __typename?: 'Topping' } | |||
| & Pick<Topping, 'name'> | |||
| )> } | |||
| )>> } | |||
| ); | |||
| export type ListPizzaWithToppingQueryVariables = Exact<{ [key: string]: never; }>; | |||
| @@ -80,6 +175,17 @@ export type ListPizzaWithToppingQuery = ( | |||
| )>> } | |||
| ); | |||
| export type PizzaChangedSubscriptionVariables = Exact<{ [key: string]: never; }>; | |||
| export type PizzaChangedSubscription = ( | |||
| { __typename?: 'Subscription' } | |||
| & { pizzasChanged: Array<Maybe<( | |||
| { __typename?: 'Pizza' } | |||
| & Pick<Pizza, 'id' | 'name'> | |||
| )>> } | |||
| ); | |||
| export type ListPizzaQueryVariables = Exact<{ [key: string]: never; }>; | |||
| @@ -91,6 +197,85 @@ export type ListPizzaQuery = ( | |||
| )>> } | |||
| ); | |||
| export type CreateToppingMutationVariables = Exact<{ | |||
| createToppingDto?: Maybe<ChangeToppingDto>; | |||
| }>; | |||
| export type CreateToppingMutation = ( | |||
| { __typename?: 'Mutation' } | |||
| & { createTopping: ( | |||
| { __typename?: 'Topping' } | |||
| & Pick<Topping, 'name'> | |||
| ) } | |||
| ); | |||
| export const ToppingChangedDocument = gql` | |||
| subscription ToppingChanged { | |||
| toppingsChanged { | |||
| id | |||
| name | |||
| } | |||
| } | |||
| `; | |||
| @Injectable({ | |||
| providedIn: 'root' | |||
| }) | |||
| export class ToppingChangedGQL extends Apollo.Subscription<ToppingChangedSubscription, ToppingChangedSubscriptionVariables> { | |||
| document = ToppingChangedDocument; | |||
| } | |||
| export const ListToppingsForPizzaCreateDocument = gql` | |||
| query ListToppingsForPizzaCreate { | |||
| listTopping { | |||
| id | |||
| name | |||
| } | |||
| } | |||
| `; | |||
| @Injectable({ | |||
| providedIn: 'root' | |||
| }) | |||
| export class ListToppingsForPizzaCreateGQL extends Apollo.Query<ListToppingsForPizzaCreateQuery, ListToppingsForPizzaCreateQueryVariables> { | |||
| document = ListToppingsForPizzaCreateDocument; | |||
| } | |||
| export const CreatePizzaDocument = gql` | |||
| mutation CreatePizza($createPizzaDto: ChangePizzaDto) { | |||
| createPizza(createPizzaDto: $createPizzaDto) { | |||
| name | |||
| } | |||
| } | |||
| `; | |||
| @Injectable({ | |||
| providedIn: 'root' | |||
| }) | |||
| export class CreatePizzaGQL extends Apollo.Mutation<CreatePizzaMutation, CreatePizzaMutationVariables> { | |||
| document = CreatePizzaDocument; | |||
| } | |||
| export const PizzaWithToppingChangedDocument = gql` | |||
| subscription PizzaWithToppingChanged { | |||
| pizzasChanged { | |||
| id | |||
| name | |||
| toppings { | |||
| name | |||
| } | |||
| } | |||
| } | |||
| `; | |||
| @Injectable({ | |||
| providedIn: 'root' | |||
| }) | |||
| export class PizzaWithToppingChangedGQL extends Apollo.Subscription<PizzaWithToppingChangedSubscription, PizzaWithToppingChangedSubscriptionVariables> { | |||
| document = PizzaWithToppingChangedDocument; | |||
| } | |||
| export const ListPizzaWithToppingDocument = gql` | |||
| query ListPizzaWithTopping { | |||
| listPizza { | |||
| @@ -109,6 +294,22 @@ export const ListPizzaWithToppingDocument = gql` | |||
| export class ListPizzaWithToppingGQL extends Apollo.Query<ListPizzaWithToppingQuery, ListPizzaWithToppingQueryVariables> { | |||
| document = ListPizzaWithToppingDocument; | |||
| } | |||
| export const PizzaChangedDocument = gql` | |||
| subscription PizzaChanged { | |||
| pizzasChanged { | |||
| id | |||
| name | |||
| } | |||
| } | |||
| `; | |||
| @Injectable({ | |||
| providedIn: 'root' | |||
| }) | |||
| export class PizzaChangedGQL extends Apollo.Subscription<PizzaChangedSubscription, PizzaChangedSubscriptionVariables> { | |||
| document = PizzaChangedDocument; | |||
| } | |||
| export const ListPizzaDocument = gql` | |||
| query ListPizza { | |||
| @@ -125,4 +326,19 @@ export const ListPizzaDocument = gql` | |||
| export class ListPizzaGQL extends Apollo.Query<ListPizzaQuery, ListPizzaQueryVariables> { | |||
| document = ListPizzaDocument; | |||
| } | |||
| export const CreateToppingDocument = gql` | |||
| mutation CreateTopping($createToppingDto: ChangeToppingDto) { | |||
| createTopping(createToppingDto: $createToppingDto) { | |||
| name | |||
| } | |||
| } | |||
| `; | |||
| @Injectable({ | |||
| providedIn: 'root' | |||
| }) | |||
| export class CreateToppingGQL extends Apollo.Mutation<CreateToppingMutation, CreateToppingMutationVariables> { | |||
| document = CreateToppingDocument; | |||
| } | |||
| @@ -1,13 +1,43 @@ | |||
| import {NgModule} from '@angular/core'; | |||
| import {ApolloModule, APOLLO_OPTIONS} from 'apollo-angular'; | |||
| import {HttpLinkModule, HttpLink} from 'apollo-angular-link-http'; | |||
| import {InMemoryCache} from 'apollo-cache-inmemory'; | |||
| import { NgModule } from '@angular/core'; | |||
| import { ApolloModule, APOLLO_OPTIONS, Apollo } from 'apollo-angular'; | |||
| import { HttpLink, HttpLinkModule } from 'apollo-angular-link-http'; | |||
| import { InMemoryCache } from 'apollo-cache-inmemory'; | |||
| import { WebSocketLink } from 'apollo-link-ws'; | |||
| import { split } from 'apollo-link'; | |||
| import { getMainDefinition } from 'apollo-utilities'; | |||
| import { OperationDefinitionNode } from 'graphql'; | |||
| const uri = 'http://localhost:4000'; // <-- add the URL of the GraphQL server here | |||
| export function createApollo(httpLink: HttpLink) { | |||
| const wsUri = 'ws://localhost:4000/graphql'; // <-- add the URL of the GraphQL server here | |||
| export function createApollo( | |||
| httpLink: HttpLink | |||
| ) { | |||
| const http = httpLink.create({ | |||
| uri | |||
| }); | |||
| const ws = new WebSocketLink({ | |||
| uri: wsUri, | |||
| options: { | |||
| reconnect: true, | |||
| }, | |||
| }); | |||
| const link = split( | |||
| // split based on operation type | |||
| ({ query }) => { | |||
| const { kind, operation } = getMainDefinition(query) as OperationDefinitionNode; | |||
| return kind === 'OperationDefinition' && operation === 'subscription'; | |||
| }, | |||
| ws, | |||
| http, | |||
| ); | |||
| return { | |||
| link: httpLink.create({uri}), | |||
| link, | |||
| cache: new InMemoryCache(), | |||
| }; | |||
| } | |||
| @@ -21,4 +51,4 @@ export function createApollo(httpLink: HttpLink) { | |||
| }, | |||
| ], | |||
| }) | |||
| export class GraphQLModule {} | |||
| export class GraphQLModule { } | |||
| @@ -318,3 +318,19 @@ h1.main-app-name { | |||
| table { | |||
| width: 100%; | |||
| } | |||
| .container { | |||
| padding: 10px; | |||
| } | |||
| .form { | |||
| min-width: 150px; | |||
| max-width: 500px; | |||
| width: 100%; | |||
| } | |||
| .form-element { | |||
| padding: 5px 0px 25px 2px; | |||
| width: 100%; | |||
| } | |||
| @@ -272,7 +272,8 @@ | |||
| </code> | |||
| </pre> | |||
| </section> | |||
| </section> | |||
| <section> | |||
| <section> | |||
| <img | |||
| src="/assets/apollo-logo.png" | |||