├── .editorconfig ├── .gitignore ├── README.md ├── angular-cli.json ├── config ├── karma.conf.js └── protractor.conf.js ├── e2e ├── app.e2e-spec.ts ├── app.po.ts └── tsconfig.json ├── package.json ├── public └── .npmignore ├── src ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.html │ ├── app.module.ts │ ├── app.ts │ ├── boot.ts │ ├── environments │ │ ├── environment.dev.ts │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── formTest.ts │ ├── gql-args-form.html │ ├── gql-args-form.ts │ ├── gql-list.html │ ├── gql-list.ts │ ├── gql-mutation.html │ ├── gql-mutation.ts │ ├── gql-query-links.html │ ├── gql-query-links.spec.ts │ ├── gql-query-links.ts │ ├── gql-scalar.html │ ├── gql-scalar.ts │ ├── graphql_builder.spec.ts │ ├── graphql_builder.ts │ ├── home.ts │ ├── index.ts │ ├── introspection_query.ts │ ├── query.html │ ├── query.ts │ ├── query_type_query.ts │ ├── routes.ts │ ├── schema_service.spec.ts │ ├── schema_service.ts │ └── shared │ │ └── index.ts ├── favicon.ico ├── fixtures │ ├── example_schema.ts │ ├── github_schema.ts │ ├── graphql_hub_schema.ts │ └── swapi_schema.ts ├── index.html ├── main.ts ├── polyfills.ts ├── test.ts ├── tsconfig.json └── typings.d.ts └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | max_line_length = 0 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | *.launch 16 | .settings/ 17 | 18 | # misc 19 | /.sass-cache 20 | /connect.lock 21 | /coverage/* 22 | /libpeerconnection.log 23 | npm-debug.log 24 | testem.log 25 | /typings 26 | 27 | # e2e 28 | /e2e/*.js 29 | /e2e/*.map 30 | 31 | #System Files 32 | .DS_Store 33 | Thumbs.db 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # graphql-admin 2 | 3 | ## This project is now abandoned. Go see [the React version](http://github.com/gaslight/graphql-admin-react) instead. 4 | 5 | This is a spike of dynamically creating a front end for a graphql server using introspection. 6 | So far the following works: 7 | 8 | * Listing all queries 9 | * Executing queries that return a list 10 | * Executing queries that return a scalar value 11 | * with arguments 12 | * Mutations 13 | 14 | This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.11-webpack.2. 15 | 16 | ## setup 17 | 18 | ``` 19 | npm install 20 | ``` 21 | 22 | Then, you may want to modify `src/app/app.module.ts` to configure the graphql client to point to your server. Currently it's set to point at `http://graphqlhub.com/graphql`. Whatever server 23 | you point at will need to support CORS or you will get cross-origin errors. 24 | 25 | ## Development server 26 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 27 | 28 | ## GraphQL server 29 | 30 | Initial developer of this project has been tested using [our fork](https://github.com/gaslight/absinthe_example) the example [Absinthe](http://absinthe-graphql.org/) project. To try it, clone this 31 | project and run it. It already supports CORS so it should "Just Work" (TM). 32 | -------------------------------------------------------------------------------- /angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "project": { 3 | "version": "1.0.0-beta.11-webpack.2", 4 | "name": "angular2-cli-spike" 5 | }, 6 | "apps": [ 7 | { 8 | "main": "src/main.ts", 9 | "tsconfig": "src/tsconfig.json", 10 | "mobile": false 11 | } 12 | ], 13 | "addons": [], 14 | "packages": [], 15 | "e2e": { 16 | "protractor": { 17 | "config": "config/protractor.conf.js" 18 | } 19 | }, 20 | "test": { 21 | "karma": { 22 | "config": "config/karma.conf.js" 23 | } 24 | }, 25 | "defaults": { 26 | "prefix": "app", 27 | "sourceDir": "src", 28 | "styleExt": "css", 29 | "prefixInterfaces": false, 30 | "lazyRoutePrefix": "+" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /config/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '..', 7 | frameworks: ['jasmine', 'angular-cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-phantomjs-launcher'), 12 | require('karma-remap-istanbul'), 13 | require('angular-cli/plugins/karma') 14 | ], 15 | customLaunchers: { 16 | // chrome setup for travis CI using chromium 17 | Chrome_travis_ci: { 18 | base: 'Chrome', 19 | flags: ['--no-sandbox'] 20 | } 21 | }, 22 | files: [ 23 | { pattern: './src/test.ts', watched: false } 24 | ], 25 | preprocessors: { 26 | './src/test.ts': ['angular-cli'] 27 | }, 28 | remapIstanbulReporter: { 29 | reports: { 30 | html: 'coverage' 31 | } 32 | }, 33 | angularCliConfig: './angular-cli.json', 34 | reporters: ['progress', 'karma-remap-istanbul'], 35 | port: 9876, 36 | colors: true, 37 | logLevel: config.LOG_INFO, 38 | autoWatch: true, 39 | browsers: ['PhantomJS'], 40 | singleRun: false 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /config/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/docs/referenceConf.js 3 | 4 | /*global jasmine */ 5 | var SpecReporter = require('jasmine-spec-reporter'); 6 | 7 | exports.config = { 8 | allScriptsTimeout: 11000, 9 | specs: [ 10 | '../e2e/**/*.e2e-spec.ts' 11 | ], 12 | capabilities: { 13 | 'browserName': 'chrome' 14 | }, 15 | directConnect: true, 16 | baseUrl: 'http://localhost:4200/', 17 | framework: 'jasmine', 18 | jasmineNodeOpts: { 19 | showColors: true, 20 | defaultTimeoutInterval: 30000, 21 | print: function() {} 22 | }, 23 | useAllAngular2AppRoots: true, 24 | beforeLaunch: function() { 25 | require('ts-node').register({ 26 | project: 'e2e' 27 | }); 28 | }, 29 | onPrepare: function() { 30 | jasmine.getEnv().addReporter(new SpecReporter()); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Angular2CliSpikePage } from './app.po'; 2 | 3 | describe('angular2-cli-spike App', function() { 4 | let page: Angular2CliSpikePage; 5 | 6 | beforeEach(() => { 7 | page = new Angular2CliSpikePage(); 8 | }); 9 | 10 | it('should display message saying app works', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('app works!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | export class Angular2CliSpikePage { 2 | navigateTo() { 3 | return browser.get('/'); 4 | } 5 | 6 | getParagraphText() { 7 | return element(by.css('app-root h1')).getText(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "outDir": "../dist/out-tsc-e2e", 10 | "sourceMap": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "../node_modules/@types" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular2-cli-spike", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "angular-cli": {}, 6 | "scripts": { 7 | "start": "ng serve", 8 | "lint": "tslint \"src/**/*.ts\"", 9 | "test": "ng test", 10 | "pree2e": "webdriver-manager update", 11 | "e2e": "protractor" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/common": "2.0.0-rc.5", 16 | "@angular/compiler": "2.0.0-rc.5", 17 | "@angular/core": "2.0.0-rc.5", 18 | "@angular/forms": "0.3.0", 19 | "@angular/http": "2.0.0-rc.5", 20 | "@angular/platform-browser": "2.0.0-rc.5", 21 | "@angular/platform-browser-dynamic": "2.0.0-rc.5", 22 | "@angular/router": "3.0.0-rc.1", 23 | "angular2-apollo": "^0.4.2", 24 | "apollo-client": "^0.4.11", 25 | "core-js": "^2.4.0", 26 | "lodash.pickby": "^4.6.0", 27 | "reflect-metadata": "0.1.3", 28 | "rxjs": "5.0.0-beta.6", 29 | "ts-helpers": "^1.1.1", 30 | "zone.js": "0.6.12" 31 | }, 32 | "devDependencies": { 33 | "@types/jasmine": "^2.2.30", 34 | "@types/protractor": "^1.5.16", 35 | "angular-cli": "1.0.0-beta.11-webpack.2", 36 | "codelyzer": "0.0.26", 37 | "jasmine-core": "2.4.1", 38 | "jasmine-spec-reporter": "2.5.0", 39 | "karma": "0.13.22", 40 | "karma-chrome-launcher": "0.2.3", 41 | "karma-jasmine": "0.3.8", 42 | "karma-phantomjs-launcher": "^1.0.2", 43 | "karma-remap-istanbul": "^0.2.1", 44 | "protractor": "3.3.0", 45 | "ts-node": "1.2.1", 46 | "tslint": "3.13.0", 47 | "typescript": "^2.0.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /public/.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/launchscout/graphql-admin/5c2bcc5ed8229b1da33546fa5871153145a110e6/public/.npmignore -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/launchscout/graphql-admin/5c2bcc5ed8229b1da33546fa5871153145a110e6/src/app/app.component.css -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

2 | {{title}} 3 |

4 | 7 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | 3 | import { addProviders, async, inject } from '@angular/core/testing'; 4 | import { AppComponent } from './app.component'; 5 | 6 | describe('App: Angular2CliSpike', () => { 7 | // beforeEach(() => { 8 | // addProviders([AppComponent]); 9 | // }); 10 | // 11 | // it('should create the app', 12 | // inject([AppComponent], (app: AppComponent) => { 13 | // expect(app).toBeTruthy(); 14 | // })); 15 | // 16 | // it('should have as title \'app works!\'', 17 | // inject([AppComponent], (app: AppComponent) => { 18 | // expect(app.title).toEqual('app works!'); 19 | // })); 20 | }); 21 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Angular2Apollo } from 'angular2-apollo'; 3 | import gql from 'graphql-tag'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | templateUrl: 'app.component.html', 8 | styleUrls: ['app.component.css'] 9 | }) 10 | export class AppComponent { 11 | title = 'app works very well!'; 12 | apolloClient: Angular2Apollo; 13 | 14 | constructor(apolloClient: Angular2Apollo) { 15 | this.apolloClient = apolloClient; 16 | this.apolloClient.watchQuery({ 17 | query: gql` 18 | query getPosts { 19 | posts { 20 | title 21 | } 22 | }`, 23 | forceFetch: true 24 | }).subscribe((results) => { 25 | this.posts = results.data.posts; 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/app.html: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule, ApplicationRef } from '@angular/core'; 3 | import { CommonModule } from '@angular/common'; 4 | import { FormsModule } from '@angular/forms'; 5 | import AppComponent from './app'; 6 | import { routing, appRoutingProviders } from './routes'; 7 | import SchemaService from './schema_service'; 8 | import QueryLinks from './gql-query-links'; 9 | 10 | import { 11 | defaultApolloClient, 12 | APOLLO_PROVIDERS 13 | } from 'angular2-apollo'; 14 | 15 | import ApolloClient, { 16 | createNetworkInterface 17 | } from 'apollo-client'; 18 | 19 | const token = 'a78feb423a5933a1c05cdf489d60c236d0435819'; 20 | let networkInterface = createNetworkInterface('https://api.github.com/graphql'); 21 | const client = new ApolloClient({ 22 | // networkInterface: createNetworkInterface('http://localhost:53441/') 23 | networkInterface 24 | // networkInterface: createNetworkInterface('http://localhost:4000/graphql') 25 | // networkInterface: createNetworkInterface('https://www.graphqlhub.com/graphql') 26 | }); 27 | 28 | networkInterface.use([{ 29 | applyMiddleware(req, next) { 30 | if (!req.options.headers) { 31 | req.options.headers = {}; // Create the header object if needed. 32 | } 33 | req.options.headers.authorization = `bearer ${token}`; 34 | next(); 35 | } 36 | }]); 37 | 38 | @NgModule({ 39 | declarations: [ 40 | AppComponent, 41 | QueryLinks 42 | ], 43 | imports: [ 44 | BrowserModule, 45 | CommonModule, 46 | FormsModule, 47 | routing 48 | ], 49 | providers: [ 50 | APOLLO_PROVIDERS, 51 | defaultApolloClient(client), 52 | SchemaService 53 | ], 54 | entryComponents: [AppComponent], 55 | bootstrap: [AppComponent] 56 | }) 57 | export class AppModule { 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/app/app.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | import { ROUTER_DIRECTIVES } from '@angular/router'; 3 | import SchemaService from './schema_service'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | templateUrl: 'app.html' 8 | }) 9 | export default class AppComponent { 10 | schemaLoaded: boolean; 11 | 12 | constructor(public schemaService: SchemaService) { 13 | this.schemaService = schemaService; 14 | this.schemaService.loadSchema().subscribe((schema) => { 15 | this.schemaLoaded = true; 16 | }); 17 | } 18 | 19 | queryType() { 20 | return this.schemaService.getQueryType(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/boot.ts: -------------------------------------------------------------------------------- 1 | import {bootstrap} from '@angular/platform-browser-dynamic'; 2 | import AppComponent from './app'; 3 | import {appRouterProviders} from './routes'; 4 | import {disableDeprecatedForms, provideForms} from '@angular/forms'; 5 | 6 | import { 7 | defaultApolloClient, 8 | APOLLO_PROVIDERS 9 | } from 'angular2-apollo'; 10 | 11 | import ApolloClient, { 12 | createNetworkInterface 13 | } from 'apollo-client'; 14 | import SchemaService from './schema_service'; 15 | 16 | const client = new ApolloClient({ 17 | networkInterface: createNetworkInterface('http://localhost:4000/graphql') 18 | }); 19 | 20 | bootstrap(AppComponent, [ 21 | appRouterProviders, 22 | disableDeprecatedForms(), 23 | provideForms(), 24 | APOLLO_PROVIDERS, 25 | SchemaService, 26 | defaultApolloClient(client) 27 | ]); 28 | -------------------------------------------------------------------------------- /src/app/environments/environment.dev.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false 3 | }; 4 | -------------------------------------------------------------------------------- /src/app/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/app/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file for the current environment will overwrite this one during build. 2 | // Different environments can be found in ./environment.{dev|prod}.ts, and 3 | // you can create your own and use it with the --env flag. 4 | // The build system defaults to the dev environment. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /src/app/formTest.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | @Component({ 3 | selector: "form-test", 4 | template: ` 5 | The Form 6 |
7 | 8 |
9 | {{ theForm.value | json }} 10 | ` 11 | }) 12 | export default class FormTestComponent { 13 | } 14 | -------------------------------------------------------------------------------- /src/app/gql-args-form.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
6 | 7 |
8 | -------------------------------------------------------------------------------- /src/app/gql-args-form.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, Output } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'gql-args-form', 5 | templateUrl: 'gql-args-form.html' 6 | }) 7 | export default class GqlArgsForm { 8 | @Input() args: Array; 9 | @Output() onExecute: EventEmitter = new EventEmitter(); 10 | 11 | execute(argsForm) { 12 | this.onExecute.emit(argsForm); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/gql-list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
{{field.name}}
{{row[field.name]}}
14 | -------------------------------------------------------------------------------- /src/app/gql-list.ts: -------------------------------------------------------------------------------- 1 | import {Component, Input} from '@angular/core'; 2 | import { Angular2Apollo } from 'angular2-apollo'; 3 | import SchemaService from './schema_service'; 4 | import gql from 'graphql-tag'; 5 | import { ActivatedRoute } from '@angular/router'; 6 | import GraphQLBuilder from './graphql_builder'; 7 | 8 | @Component({ 9 | selector: 'gql-list', 10 | templateUrl: 'gql-list.html' 11 | }) 12 | export default class GqlListComponent { 13 | @Input() queryBuilder: GraphQLBuilder; 14 | @Input() fieldPath: Array; 15 | 16 | apolloClient: Angular2Apollo; 17 | queryResults: Array; 18 | 19 | constructor(schemaService: SchemaService, apolloClient: Angular2Apollo) { 20 | this.apolloClient = apolloClient; 21 | } 22 | 23 | executeQuery(args) { 24 | this.apolloClient.watchQuery(this.queryBuilder.buildQuery(args)).subscribe({next: ({data}) => { 25 | this.queryResults = this.queryBuilder.extractResults(data); 26 | }}); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/app/gql-mutation.html: -------------------------------------------------------------------------------- 1 |
2 | {{mutationSchema.name}} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
{{ field.name }}{{ mutationResults[field.name] }}
12 |
13 | -------------------------------------------------------------------------------- /src/app/gql-mutation.ts: -------------------------------------------------------------------------------- 1 | import {Component, Input, OnInit} from '@angular/core'; 2 | import SchemaService from './schema_service'; 3 | import { ActivatedRoute } from '@angular/router'; 4 | import GqlArgsFormComponent from './gql-args-form'; 5 | import { Angular2Apollo } from 'angular2-apollo'; 6 | import GraphQLBuilder from './graphql_builder'; 7 | 8 | @Component({ 9 | templateUrl: 'gql-mutation.html', 10 | directives: [GqlArgsFormComponent] 11 | }) 12 | export default class GqlMutationComponent implements OnInit { 13 | @Input() mutationName: String; 14 | 15 | constructor(private schemaService : SchemaService, public route: ActivatedRoute, private apolloClient: Angular2Apollo) { 16 | } 17 | 18 | ngOnInit() { 19 | this.route.params.subscribe((params) => { 20 | this.mutationName = params.mutationName; 21 | this.schemaService.getMutationSchema(this.mutationName).subscribe((mutationSchema) => { 22 | this.mutationSchema = mutationSchema; 23 | }); 24 | }) 25 | } 26 | 27 | graphQLBuilder() { 28 | if (!this._graphQLBuilder) { 29 | this._graphQLBuilder = new GraphQLBuilder(this.mutationSchema); 30 | } 31 | return this._graphQLBuilder; 32 | } 33 | 34 | executeMutation(argValues) { 35 | this.apolloClient.mutate(this.graphQLBuilder().buildMutation(argValues)).then(({data}) => { 36 | this.mutationResults = data[this.graphQLBuilder().queryName()]; 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/app/gql-query-links.html: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /src/app/gql-query-links.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import SchemaService from './schema_service'; 3 | import schema from '../fixtures/graphql_hub_schema'; 4 | import { routing } from './routes'; 5 | import { AppModule } from './app.module'; 6 | import Query from './query'; 7 | import { CommonModule } from '@angular/common'; 8 | import { FormsModule } from '@angular/forms'; 9 | import GqlQueryLinks, { IGNORE_FIELDS } from './gql-query-links'; 10 | 11 | describe('GqlQueryLinks', () => { 12 | let gqlQueryLinks: GqlQueryLinks; 13 | 14 | beforeEach(() => { 15 | let graphqlSchemaService = new SchemaService(); 16 | graphqlSchemaService.schema = schema.data.__schema; 17 | 18 | gqlQueryLinks = new GqlQueryLinks(graphqlSchemaService); 19 | // // TestBed.configureTestingModule(AppModule); 20 | // TestBed.configureTestingModule({ 21 | // declarations: [GqlQueryLinks, Query], 22 | // imports: [routing, CommonModule, FormsModule], 23 | // providers: [ { provide: SchemaService, useValue: graphqlSchemaService } ] 24 | // }); 25 | }); 26 | 27 | it('builds links', () => { 28 | const queryLinks = gqlQueryLinks.getQueryLinks(); 29 | expect(queryLinks.length).toEqual(7); 30 | expect(queryLinks[0].name).toEqual('hn'); 31 | }); 32 | 33 | it('builds nested links', () => { 34 | const queryLinks = gqlQueryLinks.getQueryLinks(); 35 | const githubLinks = queryLinks[4]; 36 | console.log(githubLinks.queryLinks.map(link => link.name)); 37 | expect(githubLinks.queryLinks.length).toEqual(2); 38 | expect(githubLinks.queryLinks[0].name).toEqual('user'); 39 | expect(githubLinks.queryLinks[0].linkSegments).toEqual(['query', 'github', 'user']); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/app/gql-query-links.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import SchemaService from './schema_service'; 3 | 4 | export const IGNORE_FIELDS = ["relay", "node", "nodes"]; 5 | 6 | @Component({ 7 | selector: 'gql-query-links', 8 | templateUrl: 'gql-query-links.html' 9 | }) 10 | export default class QueryLinks { 11 | 12 | constructor(private schemaService: SchemaService) {} 13 | 14 | @Input() queryType: any; 15 | 16 | @Input() queryLinks: Array; 17 | 18 | @Input() parentFieldPath: Array; 19 | 20 | isQuery(queryField) { 21 | console.log(queryField); 22 | return (queryField.type.kind === 'LIST' || queryField.args.length > 0) 23 | } 24 | 25 | subqueryType(queryField) { 26 | return this.schemaService.getType(queryField.type.name); 27 | } 28 | 29 | hasSubQueries(queryField) { 30 | return IGNORE_FIELDS.indexOf(queryField.name) == -1 && 31 | this.subqueryType(queryField) && 32 | this.subqueryType(queryField).fields && 33 | this.subqueryType(queryField).fields.length > 0 34 | } 35 | 36 | queryLink(queryField) { 37 | return ['/query', ...(this.parentFieldPath || []), queryField.name]; 38 | } 39 | 40 | fieldPath(queryField) { 41 | return [...(this.parentFieldPath || []), queryField.name]; 42 | } 43 | 44 | getQueryLinks() { 45 | return this.queryLinks || this.buildQueryLinks(['query'], 46 | this.schemaService.getQueryType().fields); 47 | } 48 | 49 | buildQueryLinks(paths, queryFields) { 50 | console.log(paths, queryFields); 51 | return queryFields.map((queryField) => { 52 | if(this.isQuery(queryField)) { 53 | return { 54 | name: queryField.name, 55 | linkSegments: paths.concat(queryField.name) 56 | }; 57 | } else if (this.hasSubQueries(queryField)) { 58 | return { 59 | name: queryField.name, 60 | queryLinks: this.buildQueryLinks(paths.concat(queryField.name), 61 | this.subqueryType(queryField).fields) 62 | }; 63 | } 64 | }).filter(link => link); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/app/gql-scalar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
{{ field.name }}{{ queryResults[field.name] }}
10 | -------------------------------------------------------------------------------- /src/app/gql-scalar.ts: -------------------------------------------------------------------------------- 1 | import {Component, Input} from '@angular/core'; 2 | import { Angular2Apollo } from 'angular2-apollo'; 3 | import SchemaService from './schema_service'; 4 | import gql from 'graphql-tag'; 5 | import { ActivatedRoute } from '@angular/router'; 6 | import GqlArgsForm from './gql-args-form'; 7 | import GraphQLBuilder from './graphql_builder'; 8 | 9 | @Component({ 10 | selector: 'gql-scalar', 11 | templateUrl: 'gql-scalar.html', 12 | directives: [GqlArgsForm] 13 | }) 14 | export default class GqlScalarComponent { 15 | @Input() queryBuilder: GraphQLBuilder; 16 | @Input() fieldPath: Array; 17 | 18 | apolloClient: Angular2Apollo; 19 | queryResults: Object; 20 | 21 | constructor(schemaService: SchemaService, apolloClient: Angular2Apollo) { 22 | this.apolloClient = apolloClient; 23 | } 24 | 25 | executeQuery(queryArguments) { 26 | this.apolloClient.watchQuery(this.queryBuilder.buildQuery(queryArguments)).subscribe({next: ({data}) => { 27 | this.queryResults = this.queryBuilder.extractResults(data); 28 | }}); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/app/graphql_builder.spec.ts: -------------------------------------------------------------------------------- 1 | import GraphQLBuilder from './graphql_builder'; 2 | import SchemaService from './schema_service'; 3 | import swapiSchema from '../fixtures/swapi_schema'; 4 | import exampleSchema from '../fixtures/example_schema'; 5 | import graphqlHubSchema from '../fixtures/graphql_hub_schema'; 6 | 7 | describe("GraphQLBuilder", () => { 8 | 9 | let graphQLBuilder: GraphQLBuilder; 10 | let schemaService: SchemaService; 11 | 12 | beforeEach( () => { 13 | schemaService = new SchemaService(); 14 | schemaService.schema = exampleSchema.data.__schema; 15 | graphQLBuilder = new GraphQLBuilder(schemaService, ['posts']); 16 | }); 17 | 18 | it("gets query name", () => { 19 | expect(graphQLBuilder.queryName()).toEqual('posts'); 20 | }); 21 | 22 | it("isList", () => { 23 | expect(graphQLBuilder.isList()).toBeTruthy(); 24 | expect(new GraphQLBuilder(schemaService, ['user']).isList()).toBeFalsy(); 25 | }); 26 | 27 | it("returns query fields", () => { 28 | expect(graphQLBuilder.queryFields().length).toEqual(5); 29 | expect(new GraphQLBuilder(schemaService, ['user']).queryFields().length).toEqual(4); 30 | }); 31 | 32 | it("builds queries", () => { 33 | graphQLBuilder = new GraphQLBuilder(schemaService, ['user']); 34 | const query = graphQLBuilder.buildQuery({id: 1}); 35 | expect(query.variables.id).toEqual(1); 36 | expect(query.query).toBeDefined(); 37 | }); 38 | 39 | it("builds nested queries", () => { 40 | schemaService.schema = graphqlHubSchema.data.__schema; 41 | graphQLBuilder = new GraphQLBuilder(schemaService, ['github', 'user']); 42 | const query = graphQLBuilder.buildQuery({username: 'foo'}); 43 | expect(query.query).toBeDefined(); 44 | expect(query.query.loc.source.body).toContain('github {'); 45 | expect(query.query.loc.source.body).not.toContain('user {'); 46 | expect(query.query.loc.source.body).toContain('user('); 47 | }); 48 | 49 | it("builds queries without arguments", () => { 50 | graphQLBuilder = new GraphQLBuilder(schemaService, ['posts']); 51 | const query = graphQLBuilder.buildQuery(); 52 | expect(query.query).toBeDefined(); 53 | }); 54 | 55 | it('ignores empty arguments', () => { 56 | schemaService.schema = swapiSchema.data.__schema; 57 | graphQLBuilder = new GraphQLBuilder(schemaService, ['allFilms']); 58 | const query = graphQLBuilder.buildQuery({after: "", before: ""}); 59 | expect(query.query.loc.source.body).toContain('allFilms'); 60 | expect(query.query.loc.source.body).not.toContain('$after'); 61 | expect(query.variables.after).not.toBeDefined(); 62 | }); 63 | 64 | it("extracts results", () => { 65 | schemaService.schema = graphqlHubSchema.data.__schema; 66 | graphQLBuilder = new GraphQLBuilder(schemaService, ['github', 'user']); 67 | const data = { 68 | github: { 69 | user: { 70 | name: 'baz' 71 | } 72 | } 73 | }; 74 | expect(graphQLBuilder.extractResults(data).name).toEqual(data.github.user.name); 75 | }); 76 | 77 | // it("builds mutation", () => { 78 | // graphQLBuilder = new GraphQLBuilder(mutationSchema); 79 | // const mutation = graphQLBuilder.buildMutation({title: 'Hi'}); 80 | // expect(mutation.variables.title).toEqual('Hi'); 81 | // expect(mutation.mutation).toBeDefined(); 82 | // }); 83 | 84 | describe('relay pagination', () => { 85 | beforeEach(() => { 86 | schemaService.schema = swapiSchema.data.__schema; 87 | graphQLBuilder = new GraphQLBuilder(schemaService, ['allFilms']); 88 | }); 89 | 90 | it('sees connection types as list', () => { 91 | expect(graphQLBuilder.isList()).toBeTruthy(); 92 | }); 93 | 94 | it('builds query', () => { 95 | const query = graphQLBuilder.buildQuery(); 96 | expect(query.query).toBeDefined(); 97 | expect(query.query.loc.source.body).toContain('allFilms'); 98 | expect(query.query.loc.source.body).toContain('edges {'); 99 | expect(query.query.loc.source.body).toContain('node {'); 100 | expect(query.query.loc.source.body).toContain('title'); 101 | }); 102 | 103 | it('extracts results', () => { 104 | const data = { 105 | allFilms: { 106 | edges: [ 107 | { 108 | node: { 109 | title: 'A new hope' 110 | } 111 | } 112 | ] 113 | } 114 | }; 115 | expect(graphQLBuilder.extractResults(data)[0].title).toEqual(data.allFilms.edges[0].node.title); 116 | }); 117 | 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /src/app/graphql_builder.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | import { pickBy } from 'lodash'; 3 | 4 | export default class GraphQLBuilder { 5 | 6 | querySchema; 7 | 8 | constructor(private schemaService: any, private fieldPath: Array) { 9 | this.querySchema = this.schemaService.getQuerySchema(fieldPath); 10 | } 11 | 12 | queryName() { 13 | return this.querySchema.name; 14 | } 15 | 16 | args() { 17 | return this.querySchema.args; 18 | } 19 | 20 | isList() { 21 | return this.querySchema.type.kind === 'LIST' || 22 | (this.querySchema.type.kind == 'OBJECT' && this.isConnectionQuery()); 23 | } 24 | 25 | isConnectionQuery() { 26 | return this.querySchema.type.name && this.querySchema.type.name.match(/Connection$/) 27 | } 28 | 29 | queryFields() { 30 | if (this.isConnectionQuery()) { 31 | return this.nodeType().fields; 32 | } else if (this.isList()) { 33 | return this.querySchema.type.ofType.fields; 34 | } else { 35 | return this.querySchema.type.fields; 36 | } 37 | } 38 | 39 | nodeType() { 40 | return this.schemaService.getQuerySchema(this.fieldPath.concat(['edges', 'node'])).type; 41 | } 42 | 43 | argsWithValues(argValues) { 44 | return (this.querySchema.args || []) 45 | .filter( arg => argValues[arg.name] && argValues[arg.name] != ''); 46 | } 47 | 48 | declareArgumentVariables(argValues = {}) { 49 | let declarations = this.argsWithValues(argValues).map((arg) => { 50 | return `$${arg.name}: ${arg.type.kind === 'NON_NULL' ? `${arg.type.ofType.name}!` : arg.type.name}` 51 | }); 52 | return declarations.length > 0 ? `(${declarations.join(', ')})` : ''; 53 | } 54 | 55 | argumentVariables(argValues = {}) { 56 | const argVariables = this.argsWithValues(argValues) 57 | .map((arg) => `${arg.name}: $${arg.name}`); 58 | return argVariables.length > 0 ? `(${argVariables.join(', ')})` : ''; 59 | } 60 | 61 | buildInnerQuery(fieldPath: Array, argValues: Object) { 62 | if (fieldPath && fieldPath.length > 0) { 63 | return ` 64 | ${fieldPath[0]} { 65 | ${this.buildInnerQuery(fieldPath.slice(1), argValues)} 66 | } 67 | `; 68 | } 69 | return ` 70 | ${this.queryName()}${this.argumentVariables(argValues)} { 71 | ${ this.fieldQuery() } 72 | } 73 | `; 74 | } 75 | 76 | fieldQuery() { 77 | return this.isConnectionQuery() ? 78 | this.buildConnectionFieldsQuery() : 79 | this.buildScalarFieldsQuery(); 80 | } 81 | 82 | buildConnectionFieldsQuery() { 83 | return ` 84 | edges { 85 | node { 86 | ${this.buildScalarFieldsQuery()} 87 | } 88 | } 89 | ` 90 | } 91 | 92 | buildScalarFieldsQuery() { 93 | return this.scalarFields().map((field) => field.name).join(', '); 94 | } 95 | 96 | buildQuery(argValues?) { 97 | const queryString = ` 98 | query getResults${this.declareArgumentVariables(argValues)} { 99 | ${this.buildInnerQuery(this.fieldPath.slice(0, -1), argValues)} 100 | }`; 101 | return { 102 | query: gql`${queryString}`, 103 | variables: this.buildVariables(argValues), 104 | forceFetch: true 105 | }; 106 | 107 | } 108 | 109 | buildVariables(argValues) { 110 | return pickBy(argValues, (value, key) => value && value !== ''); 111 | } 112 | 113 | extractResults(payload) { 114 | const queryResults = this.findResults(payload[this.fieldPath[0]], this.fieldPath.slice(1)); 115 | if (this.isConnectionQuery()) { 116 | return queryResults.edges.map( edge => edge.node ); 117 | } else { 118 | return queryResults; 119 | } 120 | } 121 | 122 | findResults(payload, paths) { 123 | if (paths.length > 0) { 124 | return this.findResults(payload[paths[0]], paths.slice(1)); 125 | } else { 126 | return payload; 127 | } 128 | } 129 | buildMutation(args) { 130 | return { 131 | mutation: gql` 132 | mutation ${this.queryName()}${this.declareArgumentVariables()} { 133 | ${this.queryName()}${this.argumentVariables()} { 134 | ${this.scalarFields().map((field) => field.name).join(', ')} 135 | } 136 | } 137 | `, 138 | variables: args, 139 | }; 140 | } 141 | 142 | scalarFields() { 143 | return this.queryFields() ? this.queryFields().filter((field) => field.type.kind === "SCALAR") : []; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/app/home.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | @Component({ 3 | selector: 'graphql-admin-home', 4 | template: `This is the theme to Gary's show` 5 | }) 6 | export default class HomeComponent { 7 | } 8 | -------------------------------------------------------------------------------- /src/app/index.ts: -------------------------------------------------------------------------------- 1 | export * from './environments/environment'; 2 | export * from './app.component'; 3 | export * from './app.module'; 4 | -------------------------------------------------------------------------------- /src/app/introspection_query.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | const introspectionQuery = gql` 3 | query IntrospectionQuery { 4 | 5 | __schema { 6 | queryType { name } 7 | mutationType { name } 8 | types { 9 | ...FullType 10 | } 11 | } 12 | } 13 | 14 | fragment FullType on __Type { 15 | kind 16 | name 17 | description 18 | fields(includeDeprecated: true) { 19 | name 20 | description 21 | args { 22 | ...InputValue 23 | } 24 | type { 25 | ...TypeRef 26 | } 27 | isDeprecated 28 | deprecationReason 29 | } 30 | inputFields { 31 | ...InputValue 32 | } 33 | interfaces { 34 | ...TypeRef 35 | } 36 | enumValues(includeDeprecated: true) { 37 | name 38 | description 39 | isDeprecated 40 | deprecationReason 41 | } 42 | possibleTypes { 43 | ...TypeRef 44 | } 45 | } 46 | 47 | fragment InputValue on __InputValue { 48 | name 49 | description 50 | type { ...TypeRef } 51 | defaultValue 52 | } 53 | 54 | fragment TypeRef on __Type { 55 | kind 56 | name 57 | ofType { 58 | kind 59 | name 60 | ofType { 61 | kind 62 | name 63 | ofType { 64 | kind 65 | name 66 | } 67 | } 68 | } 69 | } 70 | `; 71 | 72 | export default introspectionQuery; 73 | -------------------------------------------------------------------------------- /src/app/query.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 6 |
7 | -------------------------------------------------------------------------------- /src/app/query.ts: -------------------------------------------------------------------------------- 1 | import {Component, Input, OnInit} from '@angular/core'; 2 | import SchemaService from './schema_service'; 3 | import { ActivatedRoute } from '@angular/router'; 4 | import GqlListComponent from './gql-list'; 5 | import GqlScalarComponent from './gql-scalar'; 6 | import GraphQLBuilder from './graphql_builder'; 7 | 8 | @Component({ 9 | selector: 'graphql-admin-query', 10 | templateUrl: 'query.html', 11 | directives: [GqlListComponent, GqlScalarComponent] 12 | }) 13 | export default class QueryComponent implements OnInit { 14 | queryName: String; 15 | queryTypeName: String; 16 | fieldPath: Array; 17 | queryBuilder: GraphQLBuilder; 18 | listQuery: boolean; 19 | 20 | constructor(private schemaService : SchemaService, public route: ActivatedRoute) { 21 | } 22 | 23 | ngOnInit() { 24 | // this.route.params.subscribe((params) => { 25 | // console.log(params); 26 | // this.queryName = params['queryName']; 27 | // this.queryTypeName = params['queryTypeName']; 28 | // this.querySchema = this.schemaService.getQuerySchema(this.queryTypeName, this.queryName); 29 | // this.listQuery = this.querySchema.type.kind === 'LIST'; 30 | // }) 31 | this.route.url.map((urlSegments) => urlSegments.map(segment => segment.path)).subscribe((paths) => { 32 | console.log(paths); 33 | this.fieldPath = paths; 34 | this.queryBuilder = this.schemaService.getGraphQLBuilder(paths); 35 | this.listQuery = this.queryBuilder.isList(); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/query_type_query.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | 3 | const queryTypeQuery = gql` 4 | query getSchema { 5 | __schema { 6 | queryType { 7 | fields { 8 | name 9 | args { 10 | name, 11 | type { 12 | name 13 | kind 14 | ofType { 15 | name 16 | kind 17 | } 18 | } 19 | } 20 | type { 21 | fields { 22 | name 23 | type { 24 | kind 25 | name 26 | } 27 | } 28 | kind 29 | ofType { 30 | fields { 31 | name 32 | description 33 | type { 34 | kind 35 | name 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | mutationType { 43 | fields { 44 | name 45 | args { 46 | name, 47 | type { 48 | name 49 | kind 50 | ofType { 51 | name 52 | kind 53 | } 54 | } 55 | } 56 | type { 57 | fields { 58 | name 59 | type { 60 | kind 61 | name 62 | } 63 | } 64 | kind 65 | ofType { 66 | fields { 67 | name 68 | description 69 | type { 70 | kind 71 | name 72 | } 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } 79 | } 80 | `; 81 | 82 | export default queryTypeQuery; 83 | -------------------------------------------------------------------------------- /src/app/routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes, RouterModule } from '@angular/router'; 2 | import HomeComponent from './home'; 3 | import QueryComponent from './query'; 4 | import FormTestComponent from './formTest'; 5 | import GqlMutationComponent from './gql-mutation'; 6 | 7 | const appRoutes: Routes = [ 8 | { path: '', component: HomeComponent }, 9 | { path: 'formtest', component: FormTestComponent }, 10 | { path: 'query', 11 | children: [ 12 | { 13 | path: '**', 14 | component: QueryComponent 15 | } 16 | ] 17 | }, 18 | { path: 'mutation/:mutationName', component: GqlMutationComponent } 19 | ]; 20 | 21 | export const appRoutingProviders: any[] = [ 22 | 23 | ]; 24 | 25 | export const routing = RouterModule.forRoot(appRoutes); 26 | -------------------------------------------------------------------------------- /src/app/schema_service.spec.ts: -------------------------------------------------------------------------------- 1 | import SchemaService from './schema_service'; 2 | import schema from '../fixtures/example_schema'; 3 | import graphqlHubSchema from '../fixtures/graphql_hub_schema'; 4 | 5 | describe('SchemaService', () => { 6 | const schemaService = new SchemaService(); 7 | 8 | beforeEach(() => { 9 | schemaService.schema = schema.data.__schema; 10 | }); 11 | 12 | it('has a query type', () => { 13 | const queryType = schemaService.getQueryType(); 14 | expect(queryType.fields.length).toEqual(3); 15 | }); 16 | 17 | describe('getQuerySchema', () => { 18 | it('resolves types for list queries', () => { 19 | const querySchema = schemaService.getQuerySchema(['posts']); 20 | expect(querySchema.type.kind).toEqual('LIST'); 21 | expect(querySchema.name).toEqual('posts'); 22 | expect(querySchema.type.ofType.fields.length).toBeGreaterThan(1); 23 | }); 24 | it('resolves types for object queries', () => { 25 | const querySchema = schemaService.getQuerySchema(['user']); 26 | expect(querySchema.type.kind).toEqual('OBJECT'); 27 | expect(querySchema.name).toEqual('user'); 28 | expect(querySchema.type.fields.length).toBeGreaterThan(1); 29 | }); 30 | it('finds nested query schemas', () => { 31 | schemaService.schema = graphqlHubSchema.data.__schema; 32 | const querySchema = schemaService.getQuerySchema(['github', 'user']); 33 | expect(querySchema.type.kind).toEqual('OBJECT'); 34 | expect(querySchema.name).toEqual('user'); 35 | expect(querySchema.type.fields.length).toBeGreaterThan(1); 36 | }); 37 | }); 38 | 39 | }); 40 | -------------------------------------------------------------------------------- /src/app/schema_service.ts: -------------------------------------------------------------------------------- 1 | import { Angular2Apollo } from 'angular2-apollo'; 2 | import { Injectable } from '@angular/core'; 3 | import introspectionQuery from './introspection_query'; 4 | import 'rxjs/Rx'; 5 | import { Observable } from 'rxjs/Rx'; 6 | import GraphQLBuilder from './graphql_builder'; 7 | 8 | const schemaQuery = { 9 | query: introspectionQuery, 10 | forceFetch: true 11 | }; 12 | 13 | @Injectable() 14 | export default class SchemaService { 15 | apolloClient: Angular2Apollo; 16 | schema: any; 17 | 18 | constructor(apolloClient?: Angular2Apollo) { 19 | this.apolloClient = apolloClient; 20 | } 21 | 22 | loadSchema() { 23 | return this.apolloClient.watchQuery(schemaQuery).map(({ data: { __schema } }) => { 24 | console.log(__schema); 25 | this.schema = __schema; 26 | return this.schema; 27 | }); 28 | } 29 | 30 | getType(typeName) { 31 | return this.schema.types.find(field => field.name === typeName); 32 | } 33 | 34 | getQueryType() { 35 | return this.getType(this.schema.queryType.name); 36 | } 37 | 38 | getField(type, fieldName) { 39 | const fields = type.kind === 'LIST' ? type.ofType.fields : type.fields; 40 | return fields.find(field => field.name === fieldName); 41 | } 42 | 43 | getQueryFieldType(queryField) { 44 | if (queryField.type.kind === 'LIST') { 45 | return this.getType(queryField.type.ofType.name); 46 | } else { 47 | return this.getType(queryField.type.name); 48 | } 49 | } 50 | 51 | resolveQueryReturnType(queryField) { 52 | if (queryField.type.kind === 'LIST') { 53 | queryField.type.ofType = this.getType(queryField.type.ofType.name); 54 | } else { 55 | queryField.type = this.getType(queryField.type.name); 56 | } 57 | } 58 | 59 | findQuerySchema(queryType: any, fieldPath: Array) { 60 | if (fieldPath.length > 1) { 61 | const field = this.getField(queryType, fieldPath[0]); 62 | return this.findQuerySchema(this.getQueryFieldType(field), fieldPath.slice(1)); 63 | } else { 64 | const querySchema = this.getField(queryType, fieldPath[0]); 65 | this.resolveQueryReturnType(querySchema); 66 | return querySchema; 67 | } 68 | } 69 | 70 | getQuerySchema(fieldPath: Array) { 71 | return this.findQuerySchema(this.getQueryType(), fieldPath); 72 | } 73 | 74 | getGraphQLBuilder(fieldPath: Array) { 75 | return new GraphQLBuilder(this, fieldPath); 76 | } 77 | // getMutationSchema(mutationName) { 78 | // return this.getSchema().map((schema) => { 79 | // return schema.mutationType.fields.find((field) => field.name === mutationName); 80 | // }); 81 | // } 82 | } 83 | -------------------------------------------------------------------------------- /src/app/shared/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/launchscout/graphql-admin/5c2bcc5ed8229b1da33546fa5871153145a110e6/src/app/shared/index.ts -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/launchscout/graphql-admin/5c2bcc5ed8229b1da33546fa5871153145a110e6/src/favicon.ico -------------------------------------------------------------------------------- /src/fixtures/example_schema.ts: -------------------------------------------------------------------------------- 1 | const schema = { 2 | "data": { 3 | "__schema": { 4 | "types": [ 5 | { 6 | "possibleTypes": null, 7 | "name": "__Directive", 8 | "kind": "OBJECT", 9 | "interfaces": [], 10 | "inputFields": null, 11 | "fields": [ 12 | { 13 | "type": { 14 | "ofType": { 15 | "ofType": null, 16 | "name": "__InputValue", 17 | "kind": "OBJECT" 18 | }, 19 | "name": null, 20 | "kind": "LIST" 21 | }, 22 | "name": "args", 23 | "isDeprecated": false, 24 | "description": null, 25 | "deprecationReason": null, 26 | "args": [] 27 | }, 28 | { 29 | "type": { 30 | "ofType": null, 31 | "name": "String", 32 | "kind": "SCALAR" 33 | }, 34 | "name": "description", 35 | "isDeprecated": false, 36 | "description": null, 37 | "deprecationReason": null, 38 | "args": [] 39 | }, 40 | { 41 | "type": { 42 | "ofType": null, 43 | "name": "String", 44 | "kind": "SCALAR" 45 | }, 46 | "name": "name", 47 | "isDeprecated": false, 48 | "description": null, 49 | "deprecationReason": null, 50 | "args": [] 51 | }, 52 | { 53 | "type": { 54 | "ofType": null, 55 | "name": "Boolean", 56 | "kind": "SCALAR" 57 | }, 58 | "name": "onField", 59 | "isDeprecated": false, 60 | "description": null, 61 | "deprecationReason": null, 62 | "args": [] 63 | }, 64 | { 65 | "type": { 66 | "ofType": null, 67 | "name": "Boolean", 68 | "kind": "SCALAR" 69 | }, 70 | "name": "onFragment", 71 | "isDeprecated": false, 72 | "description": null, 73 | "deprecationReason": null, 74 | "args": [] 75 | }, 76 | { 77 | "type": { 78 | "ofType": null, 79 | "name": "Boolean", 80 | "kind": "SCALAR" 81 | }, 82 | "name": "onOperation", 83 | "isDeprecated": false, 84 | "description": null, 85 | "deprecationReason": null, 86 | "args": [] 87 | } 88 | ], 89 | "enumValues": null, 90 | "description": "Represents a directive" 91 | }, 92 | { 93 | "possibleTypes": null, 94 | "name": "__EnumValue", 95 | "kind": "OBJECT", 96 | "interfaces": [], 97 | "inputFields": null, 98 | "fields": [ 99 | { 100 | "type": { 101 | "ofType": null, 102 | "name": "String", 103 | "kind": "SCALAR" 104 | }, 105 | "name": "deprecationReason", 106 | "isDeprecated": false, 107 | "description": null, 108 | "deprecationReason": null, 109 | "args": [] 110 | }, 111 | { 112 | "type": { 113 | "ofType": null, 114 | "name": "String", 115 | "kind": "SCALAR" 116 | }, 117 | "name": "description", 118 | "isDeprecated": false, 119 | "description": null, 120 | "deprecationReason": null, 121 | "args": [] 122 | }, 123 | { 124 | "type": { 125 | "ofType": null, 126 | "name": "Boolean", 127 | "kind": "SCALAR" 128 | }, 129 | "name": "isDeprecated", 130 | "isDeprecated": false, 131 | "description": null, 132 | "deprecationReason": null, 133 | "args": [] 134 | }, 135 | { 136 | "type": { 137 | "ofType": null, 138 | "name": "String", 139 | "kind": "SCALAR" 140 | }, 141 | "name": "name", 142 | "isDeprecated": false, 143 | "description": null, 144 | "deprecationReason": null, 145 | "args": [] 146 | } 147 | ], 148 | "enumValues": null, 149 | "description": null 150 | }, 151 | { 152 | "possibleTypes": null, 153 | "name": "__Field", 154 | "kind": "OBJECT", 155 | "interfaces": [], 156 | "inputFields": null, 157 | "fields": [ 158 | { 159 | "type": { 160 | "ofType": { 161 | "ofType": null, 162 | "name": "__InputValue", 163 | "kind": "OBJECT" 164 | }, 165 | "name": null, 166 | "kind": "LIST" 167 | }, 168 | "name": "args", 169 | "isDeprecated": false, 170 | "description": null, 171 | "deprecationReason": null, 172 | "args": [] 173 | }, 174 | { 175 | "type": { 176 | "ofType": null, 177 | "name": "String", 178 | "kind": "SCALAR" 179 | }, 180 | "name": "deprecationReason", 181 | "isDeprecated": false, 182 | "description": null, 183 | "deprecationReason": null, 184 | "args": [] 185 | }, 186 | { 187 | "type": { 188 | "ofType": null, 189 | "name": "String", 190 | "kind": "SCALAR" 191 | }, 192 | "name": "description", 193 | "isDeprecated": false, 194 | "description": null, 195 | "deprecationReason": null, 196 | "args": [] 197 | }, 198 | { 199 | "type": { 200 | "ofType": null, 201 | "name": "Boolean", 202 | "kind": "SCALAR" 203 | }, 204 | "name": "isDeprecated", 205 | "isDeprecated": false, 206 | "description": null, 207 | "deprecationReason": null, 208 | "args": [] 209 | }, 210 | { 211 | "type": { 212 | "ofType": null, 213 | "name": "String", 214 | "kind": "SCALAR" 215 | }, 216 | "name": "name", 217 | "isDeprecated": false, 218 | "description": null, 219 | "deprecationReason": null, 220 | "args": [] 221 | }, 222 | { 223 | "type": { 224 | "ofType": null, 225 | "name": "__Type", 226 | "kind": "OBJECT" 227 | }, 228 | "name": "type", 229 | "isDeprecated": false, 230 | "description": null, 231 | "deprecationReason": null, 232 | "args": [] 233 | } 234 | ], 235 | "enumValues": null, 236 | "description": null 237 | }, 238 | { 239 | "possibleTypes": null, 240 | "name": "__InputValue", 241 | "kind": "OBJECT", 242 | "interfaces": [], 243 | "inputFields": null, 244 | "fields": [ 245 | { 246 | "type": { 247 | "ofType": null, 248 | "name": "String", 249 | "kind": "SCALAR" 250 | }, 251 | "name": "defaultValue", 252 | "isDeprecated": false, 253 | "description": null, 254 | "deprecationReason": null, 255 | "args": [] 256 | }, 257 | { 258 | "type": { 259 | "ofType": null, 260 | "name": "String", 261 | "kind": "SCALAR" 262 | }, 263 | "name": "description", 264 | "isDeprecated": false, 265 | "description": null, 266 | "deprecationReason": null, 267 | "args": [] 268 | }, 269 | { 270 | "type": { 271 | "ofType": null, 272 | "name": "String", 273 | "kind": "SCALAR" 274 | }, 275 | "name": "name", 276 | "isDeprecated": false, 277 | "description": null, 278 | "deprecationReason": null, 279 | "args": [] 280 | }, 281 | { 282 | "type": { 283 | "ofType": null, 284 | "name": "__Type", 285 | "kind": "OBJECT" 286 | }, 287 | "name": "type", 288 | "isDeprecated": false, 289 | "description": null, 290 | "deprecationReason": null, 291 | "args": [] 292 | } 293 | ], 294 | "enumValues": null, 295 | "description": null 296 | }, 297 | { 298 | "possibleTypes": null, 299 | "name": "__Schema", 300 | "kind": "OBJECT", 301 | "interfaces": [], 302 | "inputFields": null, 303 | "fields": [ 304 | { 305 | "type": { 306 | "ofType": { 307 | "ofType": null, 308 | "name": "__Directive", 309 | "kind": "OBJECT" 310 | }, 311 | "name": null, 312 | "kind": "LIST" 313 | }, 314 | "name": "directives", 315 | "isDeprecated": false, 316 | "description": null, 317 | "deprecationReason": null, 318 | "args": [] 319 | }, 320 | { 321 | "type": { 322 | "ofType": null, 323 | "name": "__Type", 324 | "kind": "OBJECT" 325 | }, 326 | "name": "mutationType", 327 | "isDeprecated": false, 328 | "description": null, 329 | "deprecationReason": null, 330 | "args": [] 331 | }, 332 | { 333 | "type": { 334 | "ofType": null, 335 | "name": "__Type", 336 | "kind": "OBJECT" 337 | }, 338 | "name": "queryType", 339 | "isDeprecated": false, 340 | "description": null, 341 | "deprecationReason": null, 342 | "args": [] 343 | }, 344 | { 345 | "type": { 346 | "ofType": { 347 | "ofType": null, 348 | "name": "__Type", 349 | "kind": "OBJECT" 350 | }, 351 | "name": null, 352 | "kind": "LIST" 353 | }, 354 | "name": "types", 355 | "isDeprecated": false, 356 | "description": null, 357 | "deprecationReason": null, 358 | "args": [] 359 | } 360 | ], 361 | "enumValues": null, 362 | "description": "Represents a schema" 363 | }, 364 | { 365 | "possibleTypes": null, 366 | "name": "__Type", 367 | "kind": "OBJECT", 368 | "interfaces": [], 369 | "inputFields": null, 370 | "fields": [ 371 | { 372 | "type": { 373 | "ofType": null, 374 | "name": "String", 375 | "kind": "SCALAR" 376 | }, 377 | "name": "description", 378 | "isDeprecated": false, 379 | "description": null, 380 | "deprecationReason": null, 381 | "args": [] 382 | }, 383 | { 384 | "type": { 385 | "ofType": { 386 | "ofType": null, 387 | "name": "__EnumValue", 388 | "kind": "OBJECT" 389 | }, 390 | "name": null, 391 | "kind": "LIST" 392 | }, 393 | "name": "enumValues", 394 | "isDeprecated": false, 395 | "description": null, 396 | "deprecationReason": null, 397 | "args": [ 398 | { 399 | "type": { 400 | "ofType": null, 401 | "name": "Boolean", 402 | "kind": "SCALAR" 403 | }, 404 | "name": "includeDeprecated", 405 | "description": null, 406 | "defaultValue": "false" 407 | } 408 | ] 409 | }, 410 | { 411 | "type": { 412 | "ofType": { 413 | "ofType": null, 414 | "name": "__Field", 415 | "kind": "OBJECT" 416 | }, 417 | "name": null, 418 | "kind": "LIST" 419 | }, 420 | "name": "fields", 421 | "isDeprecated": false, 422 | "description": null, 423 | "deprecationReason": null, 424 | "args": [ 425 | { 426 | "type": { 427 | "ofType": null, 428 | "name": "Boolean", 429 | "kind": "SCALAR" 430 | }, 431 | "name": "includeDeprecated", 432 | "description": null, 433 | "defaultValue": "false" 434 | } 435 | ] 436 | }, 437 | { 438 | "type": { 439 | "ofType": { 440 | "ofType": null, 441 | "name": "__InputValue", 442 | "kind": "OBJECT" 443 | }, 444 | "name": null, 445 | "kind": "LIST" 446 | }, 447 | "name": "inputFields", 448 | "isDeprecated": false, 449 | "description": null, 450 | "deprecationReason": null, 451 | "args": [] 452 | }, 453 | { 454 | "type": { 455 | "ofType": { 456 | "ofType": null, 457 | "name": "__Type", 458 | "kind": "OBJECT" 459 | }, 460 | "name": null, 461 | "kind": "LIST" 462 | }, 463 | "name": "interfaces", 464 | "isDeprecated": false, 465 | "description": null, 466 | "deprecationReason": null, 467 | "args": [] 468 | }, 469 | { 470 | "type": { 471 | "ofType": null, 472 | "name": "String", 473 | "kind": "SCALAR" 474 | }, 475 | "name": "kind", 476 | "isDeprecated": false, 477 | "description": null, 478 | "deprecationReason": null, 479 | "args": [] 480 | }, 481 | { 482 | "type": { 483 | "ofType": null, 484 | "name": "String", 485 | "kind": "SCALAR" 486 | }, 487 | "name": "name", 488 | "isDeprecated": false, 489 | "description": null, 490 | "deprecationReason": null, 491 | "args": [] 492 | }, 493 | { 494 | "type": { 495 | "ofType": null, 496 | "name": "__Type", 497 | "kind": "OBJECT" 498 | }, 499 | "name": "ofType", 500 | "isDeprecated": false, 501 | "description": null, 502 | "deprecationReason": null, 503 | "args": [] 504 | }, 505 | { 506 | "type": { 507 | "ofType": { 508 | "ofType": null, 509 | "name": "__Type", 510 | "kind": "OBJECT" 511 | }, 512 | "name": null, 513 | "kind": "LIST" 514 | }, 515 | "name": "possibleTypes", 516 | "isDeprecated": false, 517 | "description": null, 518 | "deprecationReason": null, 519 | "args": [] 520 | } 521 | ], 522 | "enumValues": null, 523 | "description": "Represents scalars, interfaces, object types, unions, enums in the system" 524 | }, 525 | { 526 | "possibleTypes": null, 527 | "name": "Boolean", 528 | "kind": "SCALAR", 529 | "interfaces": null, 530 | "inputFields": null, 531 | "fields": null, 532 | "enumValues": null, 533 | "description": "The `Boolean` scalar type represents `true` or `false`." 534 | }, 535 | { 536 | "possibleTypes": null, 537 | "name": "Contact", 538 | "kind": "OBJECT", 539 | "interfaces": [], 540 | "inputFields": null, 541 | "fields": [ 542 | { 543 | "type": { 544 | "ofType": null, 545 | "name": "String", 546 | "kind": "SCALAR" 547 | }, 548 | "name": "type", 549 | "isDeprecated": false, 550 | "description": null, 551 | "deprecationReason": null, 552 | "args": [] 553 | }, 554 | { 555 | "type": { 556 | "ofType": null, 557 | "name": "String", 558 | "kind": "SCALAR" 559 | }, 560 | "name": "value", 561 | "isDeprecated": false, 562 | "description": null, 563 | "deprecationReason": null, 564 | "args": [] 565 | } 566 | ], 567 | "enumValues": null, 568 | "description": null 569 | }, 570 | { 571 | "possibleTypes": null, 572 | "name": "ContactInput", 573 | "kind": "INPUT_OBJECT", 574 | "interfaces": null, 575 | "inputFields": [ 576 | { 577 | "type": { 578 | "ofType": { 579 | "ofType": null, 580 | "name": "String", 581 | "kind": "SCALAR" 582 | }, 583 | "name": null, 584 | "kind": "NON_NULL" 585 | }, 586 | "name": "type", 587 | "description": null, 588 | "defaultValue": null 589 | }, 590 | { 591 | "type": { 592 | "ofType": { 593 | "ofType": null, 594 | "name": "String", 595 | "kind": "SCALAR" 596 | }, 597 | "name": null, 598 | "kind": "NON_NULL" 599 | }, 600 | "name": "value", 601 | "description": null, 602 | "defaultValue": null 603 | } 604 | ], 605 | "fields": null, 606 | "enumValues": null, 607 | "description": null 608 | }, 609 | { 610 | "possibleTypes": null, 611 | "name": "Float", 612 | "kind": "SCALAR", 613 | "interfaces": null, 614 | "inputFields": null, 615 | "fields": null, 616 | "enumValues": null, 617 | "description": "The `Float` scalar type represents signed double-precision fractional\nvalues as specified by\n[IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)." 618 | }, 619 | { 620 | "possibleTypes": null, 621 | "name": "ID", 622 | "kind": "SCALAR", 623 | "interfaces": null, 624 | "inputFields": null, 625 | "fields": null, 626 | "enumValues": null, 627 | "description": "The `ID` scalar type represents a unique identifier, often used to\nrefetch an object or as key for a cache. The ID type appears in a JSON\nresponse as a String; however, it is not intended to be human-readable.\nWhen expected as an input type, any string (such as `\"4\"`) or integer\n(such as `4`) input value will be accepted as an ID." 628 | }, 629 | { 630 | "possibleTypes": null, 631 | "name": "Int", 632 | "kind": "SCALAR", 633 | "interfaces": null, 634 | "inputFields": null, 635 | "fields": null, 636 | "enumValues": null, 637 | "description": "The `Int` scalar type represents non-fractional signed whole numeric\nvalues. Int can represent values between `-(2^53 - 1)` and `2^53 - 1` since\nrepresented in JSON as double-precision floating point numbers specified\nby [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)." 638 | }, 639 | { 640 | "possibleTypes": null, 641 | "name": "RootMutationType", 642 | "kind": "OBJECT", 643 | "interfaces": [], 644 | "inputFields": null, 645 | "fields": [ 646 | { 647 | "type": { 648 | "ofType": null, 649 | "name": "Post", 650 | "kind": "OBJECT" 651 | }, 652 | "name": "createPost", 653 | "isDeprecated": false, 654 | "description": null, 655 | "deprecationReason": null, 656 | "args": [ 657 | { 658 | "type": { 659 | "ofType": { 660 | "ofType": null, 661 | "name": "String", 662 | "kind": "SCALAR" 663 | }, 664 | "name": null, 665 | "kind": "NON_NULL" 666 | }, 667 | "name": "body", 668 | "description": null, 669 | "defaultValue": null 670 | }, 671 | { 672 | "type": { 673 | "ofType": { 674 | "ofType": null, 675 | "name": "String", 676 | "kind": "SCALAR" 677 | }, 678 | "name": null, 679 | "kind": "NON_NULL" 680 | }, 681 | "name": "title", 682 | "description": null, 683 | "defaultValue": null 684 | } 685 | ] 686 | }, 687 | { 688 | "type": { 689 | "ofType": null, 690 | "name": "User", 691 | "kind": "OBJECT" 692 | }, 693 | "name": "createUser", 694 | "isDeprecated": false, 695 | "description": null, 696 | "deprecationReason": null, 697 | "args": [ 698 | { 699 | "type": { 700 | "ofType": { 701 | "ofType": null, 702 | "name": "String", 703 | "kind": "SCALAR" 704 | }, 705 | "name": null, 706 | "kind": "NON_NULL" 707 | }, 708 | "name": "name", 709 | "description": null, 710 | "defaultValue": null 711 | } 712 | ] 713 | } 714 | ], 715 | "enumValues": null, 716 | "description": null 717 | }, 718 | { 719 | "possibleTypes": null, 720 | "name": "Post", 721 | "kind": "OBJECT", 722 | "interfaces": [], 723 | "inputFields": null, 724 | "fields": [ 725 | { 726 | "type": { 727 | "ofType": null, 728 | "name": "User", 729 | "kind": "OBJECT" 730 | }, 731 | "name": "author", 732 | "isDeprecated": false, 733 | "description": null, 734 | "deprecationReason": null, 735 | "args": [] 736 | }, 737 | { 738 | "type": { 739 | "ofType": null, 740 | "name": "String", 741 | "kind": "SCALAR" 742 | }, 743 | "name": "body", 744 | "isDeprecated": false, 745 | "description": null, 746 | "deprecationReason": null, 747 | "args": [] 748 | }, 749 | { 750 | "type": { 751 | "ofType": null, 752 | "name": "ID", 753 | "kind": "SCALAR" 754 | }, 755 | "name": "id", 756 | "isDeprecated": false, 757 | "description": null, 758 | "deprecationReason": null, 759 | "args": [] 760 | }, 761 | { 762 | "type": { 763 | "ofType": null, 764 | "name": "Time", 765 | "kind": "SCALAR" 766 | }, 767 | "name": "postedAt", 768 | "isDeprecated": false, 769 | "description": null, 770 | "deprecationReason": null, 771 | "args": [] 772 | }, 773 | { 774 | "type": { 775 | "ofType": null, 776 | "name": "String", 777 | "kind": "SCALAR" 778 | }, 779 | "name": "title", 780 | "isDeprecated": false, 781 | "description": null, 782 | "deprecationReason": null, 783 | "args": [] 784 | } 785 | ], 786 | "enumValues": null, 787 | "description": null 788 | }, 789 | { 790 | "possibleTypes": null, 791 | "name": "RootQueryType", 792 | "kind": "OBJECT", 793 | "interfaces": [], 794 | "inputFields": null, 795 | "fields": [ 796 | { 797 | "type": { 798 | "ofType": { 799 | "ofType": null, 800 | "name": "Post", 801 | "kind": "OBJECT" 802 | }, 803 | "name": null, 804 | "kind": "LIST" 805 | }, 806 | "name": "posts", 807 | "isDeprecated": false, 808 | "description": null, 809 | "deprecationReason": null, 810 | "args": [] 811 | }, 812 | { 813 | "type": { 814 | "ofType": null, 815 | "name": "User", 816 | "kind": "OBJECT" 817 | }, 818 | "name": "user", 819 | "isDeprecated": false, 820 | "description": null, 821 | "deprecationReason": null, 822 | "args": [ 823 | { 824 | "type": { 825 | "ofType": { 826 | "ofType": null, 827 | "name": "ID", 828 | "kind": "SCALAR" 829 | }, 830 | "name": null, 831 | "kind": "NON_NULL" 832 | }, 833 | "name": "id", 834 | "description": null, 835 | "defaultValue": null 836 | } 837 | ] 838 | }, 839 | { 840 | "type": { 841 | "ofType": { 842 | "ofType": null, 843 | "name": "User", 844 | "kind": "OBJECT" 845 | }, 846 | "name": null, 847 | "kind": "LIST" 848 | }, 849 | "name": "users", 850 | "isDeprecated": false, 851 | "description": null, 852 | "deprecationReason": null, 853 | "args": [] 854 | } 855 | ], 856 | "enumValues": null, 857 | "description": null 858 | }, 859 | { 860 | "possibleTypes": null, 861 | "name": "String", 862 | "kind": "SCALAR", 863 | "interfaces": null, 864 | "inputFields": null, 865 | "fields": null, 866 | "enumValues": null, 867 | "description": "The `String` scalar type represents textual data, represented as UTF-8\ncharacter sequences. The String type is most often used by GraphQL to\nrepresent free-form human-readable text." 868 | }, 869 | { 870 | "possibleTypes": null, 871 | "name": "Time", 872 | "kind": "SCALAR", 873 | "interfaces": null, 874 | "inputFields": null, 875 | "fields": null, 876 | "enumValues": null, 877 | "description": null 878 | }, 879 | { 880 | "possibleTypes": null, 881 | "name": "User", 882 | "kind": "OBJECT", 883 | "interfaces": [], 884 | "inputFields": null, 885 | "fields": [ 886 | { 887 | "type": { 888 | "ofType": null, 889 | "name": "Contact", 890 | "kind": "OBJECT" 891 | }, 892 | "name": "contact", 893 | "isDeprecated": false, 894 | "description": null, 895 | "deprecationReason": null, 896 | "args": [] 897 | }, 898 | { 899 | "type": { 900 | "ofType": null, 901 | "name": "ID", 902 | "kind": "SCALAR" 903 | }, 904 | "name": "id", 905 | "isDeprecated": false, 906 | "description": null, 907 | "deprecationReason": null, 908 | "args": [] 909 | }, 910 | { 911 | "type": { 912 | "ofType": null, 913 | "name": "String", 914 | "kind": "SCALAR" 915 | }, 916 | "name": "name", 917 | "isDeprecated": false, 918 | "description": null, 919 | "deprecationReason": null, 920 | "args": [] 921 | }, 922 | { 923 | "type": { 924 | "ofType": { 925 | "ofType": null, 926 | "name": "Post", 927 | "kind": "OBJECT" 928 | }, 929 | "name": null, 930 | "kind": "LIST" 931 | }, 932 | "name": "posts", 933 | "isDeprecated": false, 934 | "description": null, 935 | "deprecationReason": null, 936 | "args": [] 937 | } 938 | ], 939 | "enumValues": null, 940 | "description": "A user of the blog\n" 941 | } 942 | ], 943 | "queryType": { 944 | "name": "RootQueryType" 945 | }, 946 | "mutationType": { 947 | "name": "RootMutationType" 948 | } 949 | } 950 | } 951 | }; 952 | 953 | export default schema; 954 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular2CliSpike 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Loading... 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | import { enableProdMode } from '@angular/core'; 3 | import { AppModule, environment } from './app/'; 4 | 5 | if (environment.production) { 6 | enableProdMode(); 7 | } 8 | 9 | platformBrowserDynamic().bootstrapModule(AppModule); 10 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | // Prefer CoreJS over the polyfills above 2 | import 'core-js/es6/symbol'; 3 | import 'core-js/es6/object'; 4 | import 'core-js/es6/function'; 5 | import 'core-js/es6/parse-int'; 6 | import 'core-js/es6/parse-float'; 7 | import 'core-js/es6/number'; 8 | import 'core-js/es6/math'; 9 | import 'core-js/es6/string'; 10 | import 'core-js/es6/date'; 11 | import 'core-js/es6/array'; 12 | import 'core-js/es6/regexp'; 13 | import 'core-js/es6/map'; 14 | import 'core-js/es6/set'; 15 | import 'core-js/es6/reflect'; 16 | 17 | import 'core-js/es7/reflect'; 18 | import 'zone.js/dist/zone'; 19 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | import 'core-js/es6'; 2 | import 'core-js/es7/reflect'; 3 | 4 | // Typescript emit helpers polyfill 5 | import 'ts-helpers'; 6 | 7 | import 'zone.js/dist/zone'; 8 | import 'zone.js/dist/long-stack-trace-zone'; 9 | import 'zone.js/dist/jasmine-patch'; 10 | import 'zone.js/dist/async-test'; 11 | import 'zone.js/dist/fake-async-test'; 12 | import 'zone.js/dist/sync-test'; 13 | 14 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 15 | declare var __karma__: any; 16 | 17 | // Prevent Karma from running prematurely. 18 | __karma__.loaded = function () {}; 19 | 20 | 21 | Promise.all([ 22 | System.import('@angular/core/testing'), 23 | System.import('@angular/platform-browser-dynamic/testing') 24 | ]) 25 | // First, initialize the Angular testing environment. 26 | .then(([testing, testingBrowser]) => { 27 | testing.setBaseTestProviders( 28 | testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, 29 | testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS 30 | ); 31 | }) 32 | // Then we find all the tests. 33 | .then(() => require.context('./', true, /\.spec\.ts/)) 34 | // And load the modules. 35 | .then(context => context.keys().map(context)) 36 | // Finally, start Karma to run the tests. 37 | .then(__karma__.start, __karma__.error); 38 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "lib": ["es6", "dom"], 8 | "mapRoot": "./", 9 | "module": "es6", 10 | "moduleResolution": "node", 11 | "outDir": "../dist/out-tsc", 12 | "sourceMap": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "/Users/kon4244/dev/angular2-cli-spike/node_modules/@types" 16 | ], 17 | "types": [ 18 | "jasmine" 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | // Typings reference file, see links for more information 2 | // https://github.com/typings/typings 3 | // https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html 4 | 5 | declare var System: any; 6 | declare var module: { id: string }; 7 | declare var require: any; 8 | 9 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "class-name": true, 7 | "comment-format": [ 8 | true, 9 | "check-space" 10 | ], 11 | "curly": true, 12 | "eofline": true, 13 | "forin": true, 14 | "indent": [ 15 | true, 16 | "spaces" 17 | ], 18 | "label-position": true, 19 | "label-undefined": true, 20 | "max-line-length": [ 21 | true, 22 | 140 23 | ], 24 | "member-access": false, 25 | "member-ordering": [ 26 | true, 27 | "static-before-instance", 28 | "variables-before-functions" 29 | ], 30 | "no-arg": true, 31 | "no-bitwise": true, 32 | "no-console": [ 33 | true, 34 | "debug", 35 | "info", 36 | "time", 37 | "timeEnd", 38 | "trace" 39 | ], 40 | "no-construct": true, 41 | "no-debugger": true, 42 | "no-duplicate-key": true, 43 | "no-duplicate-variable": true, 44 | "no-empty": false, 45 | "no-eval": true, 46 | "no-inferrable-types": true, 47 | "no-shadowed-variable": true, 48 | "no-string-literal": false, 49 | "no-switch-case-fall-through": true, 50 | "no-trailing-whitespace": true, 51 | "no-unused-expression": true, 52 | "no-unused-variable": true, 53 | "no-unreachable": true, 54 | "no-use-before-declare": true, 55 | "no-var-keyword": true, 56 | "object-literal-sort-keys": false, 57 | "one-line": [ 58 | true, 59 | "check-open-brace", 60 | "check-catch", 61 | "check-else", 62 | "check-whitespace" 63 | ], 64 | "quotemark": [ 65 | true, 66 | "single" 67 | ], 68 | "radix": true, 69 | "semicolon": [ 70 | "always" 71 | ], 72 | "triple-equals": [ 73 | true, 74 | "allow-null-check" 75 | ], 76 | "typedef-whitespace": [ 77 | true, 78 | { 79 | "call-signature": "nospace", 80 | "index-signature": "nospace", 81 | "parameter": "nospace", 82 | "property-declaration": "nospace", 83 | "variable-declaration": "nospace" 84 | } 85 | ], 86 | "variable-name": false, 87 | "whitespace": [ 88 | true, 89 | "check-branch", 90 | "check-decl", 91 | "check-operator", 92 | "check-separator", 93 | "check-type" 94 | ], 95 | 96 | "directive-selector-name": [true, "camelCase"], 97 | "component-selector-name": [true, "kebab-case"], 98 | "directive-selector-type": [true, "attribute"], 99 | "component-selector-type": [true, "element"], 100 | "use-input-property-decorator": true, 101 | "use-output-property-decorator": true, 102 | "use-host-property-decorator": true, 103 | "no-input-rename": true, 104 | "no-output-rename": true, 105 | "use-life-cycle-interface": true, 106 | "use-pipe-transform-interface": true, 107 | "component-class-suffix": true, 108 | "directive-class-suffix": true 109 | } 110 | } 111 | --------------------------------------------------------------------------------