From 70a4c3e59b3b8b6304bcce79ce9d23f2d242f5e8 Mon Sep 17 00:00:00 2001 From: guoshuai Date: Thu, 7 Jun 2018 20:49:14 +0800 Subject: [PATCH 01/13] rewrite loader logic --- agent.js | 4 +- app.js | 4 +- app/extend/context.js | 1 - lib/load_connector.js | 31 ----- lib/load_schema.js | 58 --------- lib/loader/graphql-loader.js | 110 ++++++++++++++++++ test/app/service/graphql.test.js | 19 ++- .../app/graphql/directives/date.js | 22 ++++ .../app/graphql/directives/schema.graphql | 4 + .../graphql-app/app/graphql/user/connector.js | 19 +-- .../app/graphql/user/schema.graphql | 1 + 11 files changed, 164 insertions(+), 109 deletions(-) delete mode 100644 lib/load_connector.js delete mode 100644 lib/load_schema.js create mode 100644 lib/loader/graphql-loader.js create mode 100644 test/fixtures/apps/graphql-app/app/graphql/directives/date.js diff --git a/agent.js b/agent.js index a4d00a9..31f7978 100644 --- a/agent.js +++ b/agent.js @@ -1,7 +1,5 @@ 'use strict'; module.exports = agent => { - require('./lib/load_schema')(agent); - require('./lib/load_connector')(agent); + require('./lib/loader/graphql-loader')(agent); }; - diff --git a/app.js b/app.js index b6868cc..08303d7 100644 --- a/app.js +++ b/app.js @@ -1,7 +1,5 @@ 'use strict'; module.exports = app => { - require('./lib/load_schema')(app); - require('./lib/load_connector')(app); + require('./lib/loader/graphql-loader')(app); }; - diff --git a/app/extend/context.js b/app/extend/context.js index de5f09e..b1e6a88 100644 --- a/app/extend/context.js +++ b/app/extend/context.js @@ -3,7 +3,6 @@ const SYMBOL_CONNECTOR = Symbol('connector'); module.exports = { - /** * connector instance * @member Context#connector diff --git a/lib/load_connector.js b/lib/load_connector.js deleted file mode 100644 index 7249c9f..0000000 --- a/lib/load_connector.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); - -const SYMBOL_CONNECTOR_CLASS = Symbol('Application#connectorClass'); - -module.exports = app => { - const basePath = path.join(app.baseDir, 'app/graphql'); - const types = fs.readdirSync(basePath); - - Object.defineProperty(app, 'connectorClass', { - get() { - if (!this[SYMBOL_CONNECTOR_CLASS]) { - const classes = new Map(); - - types.forEach(type => { - const connectorFile = path.join(basePath, type, 'connector.js'); - /* istanbul ignore else */ - if (fs.existsSync(connectorFile)) { - const Connector = require(connectorFile); - classes.set(type, Connector); - } - }); - - this[SYMBOL_CONNECTOR_CLASS] = classes; - } - return this[SYMBOL_CONNECTOR_CLASS]; - }, - }); -}; diff --git a/lib/load_schema.js b/lib/load_schema.js deleted file mode 100644 index 61ff598..0000000 --- a/lib/load_schema.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const { - makeExecutableSchema, -} = require('graphql-tools'); -const _ = require('lodash'); - -const SYMBOL_SCHEMA = Symbol('Applicaton#schema'); - -module.exports = app => { - const basePath = path.join(app.baseDir, 'app/graphql'); - const types = fs.readdirSync(basePath); - - const schemas = []; - const resolverMap = {}; - const directiveMap = {}; - - types.forEach(type => { - // 加载schema - const schemaFile = path.join(basePath, type, 'schema.graphql'); - /* istanbul ignore else */ - if (fs.existsSync(schemaFile)) { - const schema = fs.readFileSync(schemaFile, { - encoding: 'utf8', - }); - schemas.push(schema); - } - - // 加载resolver - const resolverFile = path.join(basePath, type, 'resolver.js'); - if (fs.existsSync(resolverFile)) { - const resolver = require(resolverFile); - _.merge(resolverMap, resolver); - } - - // 加载directive resolver - const directiveFile = path.join(basePath, type, 'directive.js'); - if (fs.existsSync(directiveFile)) { - const directive = require(directiveFile); - _.merge(directiveMap, directive); - } - }); - - Object.defineProperty(app, 'schema', { - get() { - if (!this[SYMBOL_SCHEMA]) { - this[SYMBOL_SCHEMA] = makeExecutableSchema({ - typeDefs: schemas, - resolvers: resolverMap, - directiveResolvers: directiveMap, - }); - } - return this[SYMBOL_SCHEMA]; - }, - }); -}; diff --git a/lib/loader/graphql-loader.js b/lib/loader/graphql-loader.js new file mode 100644 index 0000000..e13bb5c --- /dev/null +++ b/lib/loader/graphql-loader.js @@ -0,0 +1,110 @@ +'use strict'; + +const { join, dirname } = require('path'); +const { merge } = require('lodash'); +const is = require('is-type-of'); +const { + makeExecutableSchema, + SchemaDirectiveVisitor, +} = require('graphql-tools'); + +const SYMBOL_SCHEMA = Symbol('Application#schema'); +const SYMBOL_CONNECTOR_CLASS = Symbol('Application#connectorClass'); + +module.exports = app => { + const directiveResolvers = {}; + const schemaDirectives = {}; + const resolvers = {}; + const typeDefs = []; + + class GraphqlLoader { + constructor(app) { + this.app = app; + } + + load() { + this.loadGraphql(); + this.loadTypeDefs(); + /** + * create a GraphQL.js GraphQLSchema instance + */ + Object.defineProperty(this.app, 'schema', { + get() { + if (!this[SYMBOL_SCHEMA]) { + this[SYMBOL_SCHEMA] = makeExecutableSchema({ + typeDefs, + resolvers, + directiveResolvers, + schemaDirectives, + }); + } + return this[SYMBOL_SCHEMA]; + }, + }); + } + // 加载graphql + loadGraphql() { + const loader = this.app.loader; + loader.timing.start('Loader Graphql'); + const opt = { + caseStyle: 'lower', + directory: join(this.app.baseDir, 'app/graphql'), + target: {}, + initializer: (obj, opt) => { + const pathName = opt.pathName.split('.').pop(); + // 加载resolver + if (pathName === 'resolver') { + merge(resolvers, obj); + } + // 加载schemaDirective + if (is.class(obj)) { + const proto = Object.getPrototypeOf(obj); + if (proto === SchemaDirectiveVisitor) { + const name = opt.pathName.split('.').pop(); + schemaDirectives[name] = obj; + } + } + // 加载directiveResolver + if (pathName === 'directive') { + merge(directiveResolvers, obj); + } + // 加载connector + if (pathName === 'connector') { + // 获取文件目录名 + const type = dirname(opt.path) + .split(/\/|\\/) + .pop(); + Object.defineProperty(this.app, 'connectorClass', { + get() { + if (!this[SYMBOL_CONNECTOR_CLASS]) { + const classes = new Map(); + + classes.set(type, obj); + + this[SYMBOL_CONNECTOR_CLASS] = classes; + } + return this[SYMBOL_CONNECTOR_CLASS]; + }, + }); + } + }, + }; + new this.app.loader.FileLoader(opt).load(); + loader.timing.end('Loader Graphql'); + } + // 加载typeDefs + loadTypeDefs() { + const opt = { + directory: join(this.app.baseDir, 'app/graphql'), + match: '**/*.graphql', + target: {}, + initializer: obj => { + typeDefs.push(obj.toString('utf8')); + }, + }; + new this.app.loader.FileLoader(opt).load(); + } + } + + new GraphqlLoader(app).load(); +}; diff --git a/test/app/service/graphql.test.js b/test/app/service/graphql.test.js index 8b834fc..6133cfb 100644 --- a/test/app/service/graphql.test.js +++ b/test/app/service/graphql.test.js @@ -40,11 +40,22 @@ describe('test/plugin.test.js', () => { assert.equal(resp.errors[0].message, 'Unexpected end of JSON input'); }); - it('should return name\'s upperCase with @upper directive', async () => { + it("should return name's upperCase with @upper directive", async () => { const ctx = app.mockContext(); - const resp = await ctx.graphql.query(JSON.stringify({ - query: '{ user(id: 1) { upperName } }', - })); + const resp = await ctx.graphql.query( + JSON.stringify({ + query: '{ user(id: 1) { upperName } }', + }) + ); assert.deepEqual(resp.data, { user: { upperName: 'NAME1' } }); }); + it('should return createAt with @date directive', async () => { + const ctx = app.mockContext(); + const resp = await ctx.service.graphql.query( + JSON.stringify({ + query: '{ user(id: 1) { createAt } }', + }) + ); + assert.deepEqual(resp.data, { user: { createAt: '2018-6-7' } }); + }); }); diff --git a/test/fixtures/apps/graphql-app/app/graphql/directives/date.js b/test/fixtures/apps/graphql-app/app/graphql/directives/date.js new file mode 100644 index 0000000..998d0d4 --- /dev/null +++ b/test/fixtures/apps/graphql-app/app/graphql/directives/date.js @@ -0,0 +1,22 @@ +'use strict'; + +const { SchemaDirectiveVisitor } = require('graphql-tools'); +const { GraphQLString } = require('graphql'); +const moment = require('moment'); + +class FormatDateDirective extends SchemaDirectiveVisitor { + visitFieldDefinition(field) { + const { defaultFormat } = this.args; + field.args.push({ + name: 'format', + type: GraphQLString, + }); + field.resolve = async function(source, args) { + const theDay = moment(source.createAt); + return theDay.format(args.format || defaultFormat); + }; + field.type = GraphQLString; + } +} + +module.exports = FormatDateDirective; diff --git a/test/fixtures/apps/graphql-app/app/graphql/directives/schema.graphql b/test/fixtures/apps/graphql-app/app/graphql/directives/schema.graphql index c3cbc1a..48a8574 100644 --- a/test/fixtures/apps/graphql-app/app/graphql/directives/schema.graphql +++ b/test/fixtures/apps/graphql-app/app/graphql/directives/schema.graphql @@ -1 +1,5 @@ directive @upper on FIELD_DEFINITION + +directive @date(defaultFormat: String = "YYYY-M-D") on FIELD_DEFINITION + +scalar Date diff --git a/test/fixtures/apps/graphql-app/app/graphql/user/connector.js b/test/fixtures/apps/graphql-app/app/graphql/user/connector.js index f6fc3d5..8bad9a6 100644 --- a/test/fixtures/apps/graphql-app/app/graphql/user/connector.js +++ b/test/fixtures/apps/graphql-app/app/graphql/user/connector.js @@ -10,13 +10,16 @@ class UserConnector { fetch(ids) { // this.ctx.model.user.find(ids); - return Promise.resolve(ids.map(id => ({ - id, - name: `name${id}`, - upperName: `name${id}`, - password: `password${id}`, - projects: [], - }))); + return Promise.resolve( + ids.map(id => ({ + id, + name: `name${id}`, + upperName: `name${id}`, + password: `password${id}`, + createAt: 1528375304600, + projects: [], + })) + ); } fetchByIds(ids) { @@ -26,8 +29,6 @@ class UserConnector { fetchById(id) { return this.loader.load(id); } - } module.exports = UserConnector; - diff --git a/test/fixtures/apps/graphql-app/app/graphql/user/schema.graphql b/test/fixtures/apps/graphql-app/app/graphql/user/schema.graphql index 05ac91b..ef1f37f 100644 --- a/test/fixtures/apps/graphql-app/app/graphql/user/schema.graphql +++ b/test/fixtures/apps/graphql-app/app/graphql/user/schema.graphql @@ -8,5 +8,6 @@ type User { password: String! name: String! upperName: String @upper + createAt: Date @date projects: [Project!] } From 4e1ff580aeccf933296f89bfb187faf0ef9611d2 Mon Sep 17 00:00:00 2001 From: guoshuai Date: Thu, 7 Jun 2018 20:57:49 +0800 Subject: [PATCH 02/13] add index.d.ts --- index.d.ts | 6 ++++++ package.json | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 index.d.ts diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..779bf20 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,6 @@ +declare module 'egg' { + export interface IConnector extends PlainObject {} + interface Context { + connector: IConnector; + } +} diff --git a/package.json b/package.json index d2a54c6..3d61651 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,8 @@ "agent.js", "config", "app", - "lib" + "lib", + "index.d.ts" ], "ci": { "version": "8, 9" From e0e9ef8e0eb41ff8b145d6907e5754eb89803c33 Mon Sep 17 00:00:00 2001 From: guoshuai Date: Fri, 8 Jun 2018 10:36:17 +0800 Subject: [PATCH 03/13] add ts generator --- index.d.ts | 6 --- lib/ts-generator/connector.js | 75 +++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 6 deletions(-) delete mode 100644 index.d.ts create mode 100644 lib/ts-generator/connector.js diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 779bf20..0000000 --- a/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module 'egg' { - export interface IConnector extends PlainObject {} - interface Context { - connector: IConnector; - } -} diff --git a/lib/ts-generator/connector.js b/lib/ts-generator/connector.js new file mode 100644 index 0000000..558fc4d --- /dev/null +++ b/lib/ts-generator/connector.js @@ -0,0 +1,75 @@ +'use strict'; + +const utils = require('egg-ts-helper/dist/utils'); +const d = require('debug'); +const path = require('path'); + +const debug = d('egg-ts-helper#generators_extend'); +module.exports = (config, baseConfig) => { + const fileList = utils.loadFiles(config.dir, config.pattern); + const dist = path.resolve(baseConfig.typings, 'app/extend/context.d.ts'); + + debug('file list : %o', fileList); + if (!fileList.length) { + return { dist }; + } + + // using to compose import code + let importStr = ''; + // using to create interface mapping + const interfaceMap = {}; + + fileList.forEach(f => { + f = f.substring(0, f.lastIndexOf('.')); + const obj = utils.getModuleObjByPath(f); + const tsPath = path + .relative(config.dtsDir, path.join(config.dir, f)) + .replace(/\/|\\/g, '/'); + debug('import %s from %s', obj.moduleName, tsPath); + importStr += `import ${obj.moduleName} from '${tsPath}';\n`; + + // create mapping + const collector = interfaceMap; + if (obj.props.length) { + const name = utils.camelProp( + obj.props.shift(), + config.caseStyle || baseConfig.caseStyle + ); + collector[name] = obj.moduleName; + } + }); + + // composing all the interface + const composeInterface = (obj, indent = '') => { + let str = ''; + + Object.keys(obj).forEach(key => { + const val = obj[key]; + if (typeof val === 'string') { + str += `${indent + key}: ${ + config.interfaceHandle ? config.interfaceHandle(val) : val + };\n`; + } else { + const newVal = composeInterface(val, indent + ' '); + if (newVal) { + str += `${indent + key}: {\n${newVal + indent}};\n`; + } + } + }); + + return str; + }; + + return { + dist, + content: + `${importStr}\n` + + `declare module '${config.framework || baseConfig.framework}' {\n` + + ` interface ${config.interface} {\n` + + ' connector: {\n' + + composeInterface(interfaceMap, ' ') + + ' }\n' + + ' }\n' + + '}\n', + }; +}; From 4e545527f479512766335c3ba20d99fb0e35f9dc Mon Sep 17 00:00:00 2001 From: guoshuai Date: Fri, 8 Jun 2018 10:40:14 +0800 Subject: [PATCH 04/13] format --- lib/ts-generator/connector.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ts-generator/connector.js b/lib/ts-generator/connector.js index 558fc4d..8693516 100644 --- a/lib/ts-generator/connector.js +++ b/lib/ts-generator/connector.js @@ -66,9 +66,9 @@ module.exports = (config, baseConfig) => { `${importStr}\n` + `declare module '${config.framework || baseConfig.framework}' {\n` + ` interface ${config.interface} {\n` + - ' connector: {\n' + - composeInterface(interfaceMap, ' ') + - ' }\n' + + ' connector: {\n' + + composeInterface(interfaceMap, ' ') + + ' }\n' + ' }\n' + '}\n', }; From de57faccb62ac0e8fae455fecfc7065c30d1019e Mon Sep 17 00:00:00 2001 From: guoshuai Date: Fri, 8 Jun 2018 11:12:18 +0800 Subject: [PATCH 05/13] add connector ts helper --- lib/ts-generator/connector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ts-generator/connector.js b/lib/ts-generator/connector.js index 8693516..8b70f43 100644 --- a/lib/ts-generator/connector.js +++ b/lib/ts-generator/connector.js @@ -7,7 +7,7 @@ const path = require('path'); const debug = d('egg-ts-helper#generators_extend'); module.exports = (config, baseConfig) => { const fileList = utils.loadFiles(config.dir, config.pattern); - const dist = path.resolve(baseConfig.typings, 'app/extend/context.d.ts'); + const dist = path.resolve(baseConfig.typings, 'app/connector/index.d.ts'); debug('file list : %o', fileList); if (!fileList.length) { From 51a3df89edccdd7c4604ac3ff502d889ca6679ce Mon Sep 17 00:00:00 2001 From: guoshuai Date: Fri, 8 Jun 2018 12:27:12 +0800 Subject: [PATCH 06/13] add tshelper --- tshelper.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tshelper.js diff --git a/tshelper.js b/tshelper.js new file mode 100644 index 0000000..2fc7e72 --- /dev/null +++ b/tshelper.js @@ -0,0 +1,17 @@ +'use strict'; + +const generator = require('./lib/ts-generator/connector'); + +module.exports = { + watchDirs: { + connector: { + path: 'app/graphql', + interface: 'Context', + pattern: '**/connector*.(ts|js)', + generator, + caseStyle: 'lower', + trigger: ['add', 'change', 'unlink'], + enabled: true, + }, + }, +}; From b38e205d4d93ddb8f4f8946b5daebd0035a54de0 Mon Sep 17 00:00:00 2001 From: guoshuai Date: Fri, 8 Jun 2018 12:49:17 +0800 Subject: [PATCH 07/13] bug fix --- lib/loader/graphql-loader.js | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/loader/graphql-loader.js b/lib/loader/graphql-loader.js index e13bb5c..4eb2ca1 100644 --- a/lib/loader/graphql-loader.js +++ b/lib/loader/graphql-loader.js @@ -23,13 +23,14 @@ module.exports = app => { } load() { - this.loadGraphql(); + const connectorClasses = new Map(); + this.loadGraphql(connectorClasses); this.loadTypeDefs(); /** * create a GraphQL.js GraphQLSchema instance */ - Object.defineProperty(this.app, 'schema', { - get() { + Object.defineProperties(this.app, { + get schema() { if (!this[SYMBOL_SCHEMA]) { this[SYMBOL_SCHEMA] = makeExecutableSchema({ typeDefs, @@ -40,10 +41,16 @@ module.exports = app => { } return this[SYMBOL_SCHEMA]; }, + get connectorClass() { + if (!this[SYMBOL_CONNECTOR_CLASS]) { + this[SYMBOL_CONNECTOR_CLASS] = connectorClasses; + } + return this[SYMBOL_CONNECTOR_CLASS]; + }, }); } // 加载graphql - loadGraphql() { + loadGraphql(connectorClasses) { const loader = this.app.loader; loader.timing.start('Loader Graphql'); const opt = { @@ -74,18 +81,7 @@ module.exports = app => { const type = dirname(opt.path) .split(/\/|\\/) .pop(); - Object.defineProperty(this.app, 'connectorClass', { - get() { - if (!this[SYMBOL_CONNECTOR_CLASS]) { - const classes = new Map(); - - classes.set(type, obj); - - this[SYMBOL_CONNECTOR_CLASS] = classes; - } - return this[SYMBOL_CONNECTOR_CLASS]; - }, - }); + connectorClasses.set(type, obj); } }, }; From fbd20f829870a39c94eebf54ad65cfb4bb0afaf8 Mon Sep 17 00:00:00 2001 From: guoshuai Date: Fri, 8 Jun 2018 13:06:05 +0800 Subject: [PATCH 08/13] bug fix --- lib/loader/graphql-loader.js | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/loader/graphql-loader.js b/lib/loader/graphql-loader.js index 4eb2ca1..63813a4 100644 --- a/lib/loader/graphql-loader.js +++ b/lib/loader/graphql-loader.js @@ -30,22 +30,26 @@ module.exports = app => { * create a GraphQL.js GraphQLSchema instance */ Object.defineProperties(this.app, { - get schema() { - if (!this[SYMBOL_SCHEMA]) { - this[SYMBOL_SCHEMA] = makeExecutableSchema({ - typeDefs, - resolvers, - directiveResolvers, - schemaDirectives, - }); - } - return this[SYMBOL_SCHEMA]; + schema: { + get() { + if (!this[SYMBOL_SCHEMA]) { + this[SYMBOL_SCHEMA] = makeExecutableSchema({ + typeDefs, + resolvers, + directiveResolvers, + schemaDirectives, + }); + } + return this[SYMBOL_SCHEMA]; + }, }, - get connectorClass() { - if (!this[SYMBOL_CONNECTOR_CLASS]) { - this[SYMBOL_CONNECTOR_CLASS] = connectorClasses; - } - return this[SYMBOL_CONNECTOR_CLASS]; + connectorClass: { + get() { + if (!this[SYMBOL_CONNECTOR_CLASS]) { + this[SYMBOL_CONNECTOR_CLASS] = connectorClasses; + } + return this[SYMBOL_CONNECTOR_CLASS]; + }, }, }); } From 607ce3f0ebb43a96aae52f9734014783c6e8c376 Mon Sep 17 00:00:00 2001 From: guoshuai Date: Fri, 8 Jun 2018 13:53:29 +0800 Subject: [PATCH 09/13] add readme --- README.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index dba930a..163ce8d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # egg-graphql + --- [GraphQL](http://facebook.github.io/graphql/)使用 Schema 来描述数据,并通过制定和实现 GraphQL 规范定义了支持 Schema 查询的 DSQL (Domain Specific Query Language,领域特定查询语言,由 FACEBOOK 提出。 @@ -62,15 +63,15 @@ exports.graphql = { // 是否加载开发者工具 graphiql, 默认开启。路由同 router 字段。使用浏览器打开该可见。 graphiql: true, // graphQL 路由前的拦截器 - onPreGraphQL: function* (ctx) {}, + onPreGraphQL: function*(ctx) {}, // 开发工具 graphiQL 路由前的拦截器,建议用于做权限操作(如只提供开发者使用) - onPreGraphiQL: function* (ctx) {}, + onPreGraphiQL: function*(ctx) {}, }; ``` ## 使用方式 -请将 graphql 相关逻辑放到 app/graphql 下,请参考测试用例,里面有connector/schema 的目录结构, 以及 dataloader 的使用。 +请将 graphql 相关逻辑放到 app/graphql 下,请参考测试用例,里面有 connector/schema 的目录结构, 以及 dataloader 的使用。 目录结构如下 @@ -85,21 +86,28 @@ exports.graphql = { │   │   └── user // 一个graphql模型 │   │   ├── connector.js │   │   ├── resolver.js -│   │   └── schema.graphql +│   │   └── schema.graphql │   ├── model │   │   └── user.js │   ├── public │   └── router.js +``` + +## ts 的支持 +在项目根目录创建 tshelper.js,并赋值如下代码: + +``` +module.exports = require("egg-graphql/tshelper"); ``` ## 参考文章 -- [graphql官网](http://facebook.github.io/graphql) +- [graphql 官网](http://facebook.github.io/graphql) -- [如何在egg中使用graphql](https://zhuanlan.zhihu.com/p/30604868) +- [如何在 egg 中使用 graphql](https://zhuanlan.zhihu.com/p/30604868) -- [项目例子:结合sequelize](https://github.com/freebyron/egg-graphql-boilerplate) +- [项目例子:结合 sequelize](https://github.com/freebyron/egg-graphql-boilerplate) ## 协议 From b6c76d4e632c7d5ae945156aae4bdeb3a25b50aa Mon Sep 17 00:00:00 2001 From: guoshuai Date: Fri, 8 Jun 2018 13:54:07 +0800 Subject: [PATCH 10/13] add readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 163ce8d..3bc0d52 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,8 @@ exports.graphql = { module.exports = require("egg-graphql/tshelper"); ``` +## 增加了 schemaDireactives 的支持 + ## 参考文章 - [graphql 官网](http://facebook.github.io/graphql) From 3a9710315e80f2d1e5995fa909cfb830026779fb Mon Sep 17 00:00:00 2001 From: guoshuai Date: Fri, 8 Jun 2018 14:21:27 +0800 Subject: [PATCH 11/13] format --- tshelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tshelper.js b/tshelper.js index 2fc7e72..6cafe91 100644 --- a/tshelper.js +++ b/tshelper.js @@ -10,7 +10,7 @@ module.exports = { pattern: '**/connector*.(ts|js)', generator, caseStyle: 'lower', - trigger: ['add', 'change', 'unlink'], + trigger: [ 'add', 'change', 'unlink' ], enabled: true, }, }, From ab3006db09bb988dae278c2fac272ad7e12bf7ff Mon Sep 17 00:00:00 2001 From: guoshuai Date: Tue, 26 Jun 2018 08:26:49 +0800 Subject: [PATCH 12/13] =?UTF-8?q?=E7=A7=BB=E9=99=A4ts=20generator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 --- lib/ts-generator/connector.js | 75 ----------------------------------- tshelper.js | 17 -------- 3 files changed, 98 deletions(-) delete mode 100644 lib/ts-generator/connector.js delete mode 100644 tshelper.js diff --git a/README.md b/README.md index 3bc0d52..b9610d2 100644 --- a/README.md +++ b/README.md @@ -95,12 +95,6 @@ exports.graphql = { ## ts 的支持 -在项目根目录创建 tshelper.js,并赋值如下代码: - -``` -module.exports = require("egg-graphql/tshelper"); -``` - ## 增加了 schemaDireactives 的支持 ## 参考文章 diff --git a/lib/ts-generator/connector.js b/lib/ts-generator/connector.js deleted file mode 100644 index 8b70f43..0000000 --- a/lib/ts-generator/connector.js +++ /dev/null @@ -1,75 +0,0 @@ -'use strict'; - -const utils = require('egg-ts-helper/dist/utils'); -const d = require('debug'); -const path = require('path'); - -const debug = d('egg-ts-helper#generators_extend'); -module.exports = (config, baseConfig) => { - const fileList = utils.loadFiles(config.dir, config.pattern); - const dist = path.resolve(baseConfig.typings, 'app/connector/index.d.ts'); - - debug('file list : %o', fileList); - if (!fileList.length) { - return { dist }; - } - - // using to compose import code - let importStr = ''; - // using to create interface mapping - const interfaceMap = {}; - - fileList.forEach(f => { - f = f.substring(0, f.lastIndexOf('.')); - const obj = utils.getModuleObjByPath(f); - const tsPath = path - .relative(config.dtsDir, path.join(config.dir, f)) - .replace(/\/|\\/g, '/'); - debug('import %s from %s', obj.moduleName, tsPath); - importStr += `import ${obj.moduleName} from '${tsPath}';\n`; - - // create mapping - const collector = interfaceMap; - if (obj.props.length) { - const name = utils.camelProp( - obj.props.shift(), - config.caseStyle || baseConfig.caseStyle - ); - collector[name] = obj.moduleName; - } - }); - - // composing all the interface - const composeInterface = (obj, indent = '') => { - let str = ''; - - Object.keys(obj).forEach(key => { - const val = obj[key]; - if (typeof val === 'string') { - str += `${indent + key}: ${ - config.interfaceHandle ? config.interfaceHandle(val) : val - };\n`; - } else { - const newVal = composeInterface(val, indent + ' '); - if (newVal) { - str += `${indent + key}: {\n${newVal + indent}};\n`; - } - } - }); - - return str; - }; - - return { - dist, - content: - `${importStr}\n` + - `declare module '${config.framework || baseConfig.framework}' {\n` + - ` interface ${config.interface} {\n` + - ' connector: {\n' + - composeInterface(interfaceMap, ' ') + - ' }\n' + - ' }\n' + - '}\n', - }; -}; diff --git a/tshelper.js b/tshelper.js deleted file mode 100644 index 6cafe91..0000000 --- a/tshelper.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -const generator = require('./lib/ts-generator/connector'); - -module.exports = { - watchDirs: { - connector: { - path: 'app/graphql', - interface: 'Context', - pattern: '**/connector*.(ts|js)', - generator, - caseStyle: 'lower', - trigger: [ 'add', 'change', 'unlink' ], - enabled: true, - }, - }, -}; From 3026c756d157290cafb397bd7761fb5b5d8deef9 Mon Sep 17 00:00:00 2001 From: gs1866 Date: Mon, 15 Apr 2019 14:33:29 +0800 Subject: [PATCH 13/13] =?UTF-8?q?=E5=90=88=E5=B9=B6master?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 2 +- README.md | 6 ++++-- appveyor.yml | 2 +- lib/loader/graphql-loader.js | 14 ++++++++++---- test/app/service/graphql.test.js | 1 + .../graphql-app/app/graphql/project/schema.graphql | 1 - .../apps/graphql-app/app/graphql/user/connector.js | 7 ++++--- 7 files changed, 21 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index ce21122..960bc57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ sudo: false language: node_js node_js: - '8' - - '10' + - '9' install: - npm i npminstall && npminstall script: diff --git a/README.md b/README.md index 2290a4d..5f0eed9 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ exports.graphql = { }; // 添加中间件拦截请求 -exports.middleware = [ 'graphql' ]; +exports.middleware = ['graphql']; ``` ## 使用方式 @@ -90,7 +90,7 @@ exports.middleware = [ 'graphql' ]; │ │ │ └── schemaDirective.js // 自定义 SchemaDirective │  │  │  │   │   └── user // 一个graphql模型 -│   │   ├── connector.js +│   │   ├── connector.js │   │   ├── resolver.js │   │   └── schema.graphql │   ├── model @@ -103,6 +103,8 @@ exports.middleware = [ 'graphql' ]; ## 增加了 schemaDireactives 的支持 +支持如上约定的目录结构,以及文件名作为指令名的方式. + ## 参考文章 - [graphql 官网](http://facebook.github.io/graphql) diff --git a/appveyor.yml b/appveyor.yml index 981e82b..d0aa47e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ environment: matrix: - nodejs_version: '8' - - nodejs_version: '10' + - nodejs_version: '9' install: - ps: Install-Product node $env:nodejs_version diff --git a/lib/loader/graphql-loader.js b/lib/loader/graphql-loader.js index 63813a4..0ad400f 100644 --- a/lib/loader/graphql-loader.js +++ b/lib/loader/graphql-loader.js @@ -1,7 +1,7 @@ 'use strict'; const { join, dirname } = require('path'); -const { merge } = require('lodash'); +const { merge, isFunction } = require('lodash'); const is = require('is-type-of'); const { makeExecutableSchema, @@ -65,9 +65,12 @@ module.exports = app => { const pathName = opt.pathName.split('.').pop(); // 加载resolver if (pathName === 'resolver') { + if (isFunction(obj)) { + obj = obj(this.app); + } merge(resolvers, obj); } - // 加载schemaDirective + // load schemaDirective if (is.class(obj)) { const proto = Object.getPrototypeOf(obj); if (proto === SchemaDirectiveVisitor) { @@ -75,11 +78,14 @@ module.exports = app => { schemaDirectives[name] = obj; } } - // 加载directiveResolver + if (pathName === 'schemaDirective') { + merge(schemaDirectives, obj); + } + // load directiveResolver if (pathName === 'directive') { merge(directiveResolvers, obj); } - // 加载connector + // load connector if (pathName === 'connector') { // 获取文件目录名 const type = dirname(opt.path) diff --git a/test/app/service/graphql.test.js b/test/app/service/graphql.test.js index abcf13c..afea8d1 100644 --- a/test/app/service/graphql.test.js +++ b/test/app/service/graphql.test.js @@ -49,6 +49,7 @@ describe('test/plugin.test.js', () => { ); assert.deepEqual(resp.data, { user: { upperName: 'NAME1' } }); }); + it('should return createAt with @date directive', async () => { const ctx = app.mockContext(); const resp = await ctx.service.graphql.query( diff --git a/test/fixtures/apps/graphql-app/app/graphql/project/schema.graphql b/test/fixtures/apps/graphql-app/app/graphql/project/schema.graphql index 2249d1c..2ae5da5 100644 --- a/test/fixtures/apps/graphql-app/app/graphql/project/schema.graphql +++ b/test/fixtures/apps/graphql-app/app/graphql/project/schema.graphql @@ -1,4 +1,3 @@ - type Project { name: String! } diff --git a/test/fixtures/apps/graphql-app/app/graphql/user/connector.js b/test/fixtures/apps/graphql-app/app/graphql/user/connector.js index 8bad9a6..b144e0d 100644 --- a/test/fixtures/apps/graphql-app/app/graphql/user/connector.js +++ b/test/fixtures/apps/graphql-app/app/graphql/user/connector.js @@ -16,18 +16,19 @@ class UserConnector { name: `name${id}`, upperName: `name${id}`, password: `password${id}`, + lowerName: `NAME${id}`, createAt: 1528375304600, projects: [], })) ); } - fetchByIds(ids) { + async fetchByIds(ids) { return this.loader.loadMany(ids); } - fetchById(id) { - return this.loader.load(id); + async fetchById(id) { + return await this.loader.load(id); } }