Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

增加ts的支持 #15

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ sudo: false
language: node_js
node_js:
- '8'
- '10'
- '9'
install:
- npm i npminstall && npminstall
script:
Expand Down
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# egg-graphql

---

[GraphQL](http://facebook.github.io/graphql/)使用 Schema 来描述数据,并通过制定和实现 GraphQL 规范定义了支持 Schema 查询的 DSQL (Domain Specific Query Language,领域特定查询语言,由 FACEBOOK 提出。
Expand Down Expand Up @@ -62,13 +63,13 @@ exports.graphql = {
// 是否加载开发者工具 graphiql, 默认开启。路由同 router 字段。使用浏览器打开该可见。
graphiql: true,
// graphQL 路由前的拦截器
onPreGraphQL: function* (ctx) {},
onPreGraphQL: function*(ctx) {},
// 开发工具 graphiQL 路由前的拦截器,建议用于做权限操作(如只提供开发者使用)
onPreGraphiQL: function* (ctx) {},
onPreGraphiQL: function*(ctx) {},
};

// 添加中间件拦截请求
exports.middleware = [ 'graphql' ];
exports.middleware = ['graphql'];
```

## 使用方式
Expand All @@ -89,23 +90,28 @@ exports.middleware = [ 'graphql' ];
│ │ │ └── schemaDirective.js // 自定义 SchemaDirective
│  │  │ 
│   │   └── user // 一个graphql模型
│   │   ├── connector.js
│   │   ├── connector.js
│   │   ├── resolver.js
│   │   └── schema.graphql
│   ├── model
│   │   └── user.js
│   ├── public
│   └── router.js

```

## ts 的支持

## 增加了 schemaDireactives 的支持

支持如上约定的目录结构,以及文件名作为指令名的方式.

## 参考文章

- [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)

## 协议

Expand Down
4 changes: 1 addition & 3 deletions agent.js
Original file line number Diff line number Diff line change
@@ -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);
};

4 changes: 1 addition & 3 deletions app.js
Original file line number Diff line number Diff line change
@@ -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);
};

1 change: 0 additions & 1 deletion app/extend/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
const SYMBOL_CONNECTOR = Symbol('connector');

module.exports = {

/**
* connector instance
* @member Context#connector
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
environment:
matrix:
- nodejs_version: '8'
- nodejs_version: '10'
- nodejs_version: '9'

install:
- ps: Install-Product node $env:nodejs_version
Expand Down
31 changes: 0 additions & 31 deletions lib/load_connector.js

This file was deleted.

74 changes: 0 additions & 74 deletions lib/load_schema.js

This file was deleted.

116 changes: 116 additions & 0 deletions lib/loader/graphql-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
'use strict';

const { join, dirname } = require('path');
const { merge, isFunction } = 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() {
const connectorClasses = new Map();
this.loadGraphql(connectorClasses);
this.loadTypeDefs();
/**
* create a GraphQL.js GraphQLSchema instance
*/
Object.defineProperties(this.app, {
schema: {
get() {
if (!this[SYMBOL_SCHEMA]) {
this[SYMBOL_SCHEMA] = makeExecutableSchema({
typeDefs,
resolvers,
directiveResolvers,
schemaDirectives,
});
}
return this[SYMBOL_SCHEMA];
},
},
connectorClass: {
get() {
if (!this[SYMBOL_CONNECTOR_CLASS]) {
this[SYMBOL_CONNECTOR_CLASS] = connectorClasses;
}
return this[SYMBOL_CONNECTOR_CLASS];
},
},
});
}
// 加载graphql
loadGraphql(connectorClasses) {
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') {
if (isFunction(obj)) {
obj = obj(this.app);
}
merge(resolvers, obj);
}
// load schemaDirective
if (is.class(obj)) {
const proto = Object.getPrototypeOf(obj);
if (proto === SchemaDirectiveVisitor) {
const name = opt.pathName.split('.').pop();
schemaDirectives[name] = obj;
}
}
if (pathName === 'schemaDirective') {
merge(schemaDirectives, obj);
}
// load directiveResolver
if (pathName === 'directive') {
merge(directiveResolvers, obj);
}
// load connector
if (pathName === 'connector') {
// 获取文件目录名
const type = dirname(opt.path)
.split(/\/|\\/)
.pop();
connectorClasses.set(type, obj);
}
},
};
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();
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"agent.js",
"config",
"app",
"lib"
"lib",
"index.d.ts"
],
"ci": {
"version": "8, 9"
Expand Down
30 changes: 22 additions & 8 deletions test/app/service/graphql.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,33 @@ 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 name\'s lowerCase with schemaDirectives', async () => {
it('should return createAt with @date directive', async () => {
const ctx = app.mockContext();
const resp = await ctx.graphql.query(JSON.stringify({
query: '{ user(id: 1) { lowerName } }',
}));
const resp = await ctx.service.graphql.query(
JSON.stringify({
query: '{ user(id: 1) { createAt } }',
})
);
assert.deepEqual(resp.data, { user: { createAt: '2018-6-7' } });
});

it("should return name's lowerCase with schemaDirectives", async () => {
const ctx = app.mockContext();
const resp = await ctx.graphql.query(
JSON.stringify({
query: '{ user(id: 1) { lowerName } }',
})
);
assert.deepEqual(resp.data, { user: { lowerName: 'name1' } });
});
});
22 changes: 22 additions & 0 deletions test/fixtures/apps/graphql-app/app/graphql/directives/date.js
Original file line number Diff line number Diff line change
@@ -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;
Loading