Skip to content

Commit c8ef89a

Browse files
author
Sergey Dolin
committed
v1.0
1 parent 0b38127 commit c8ef89a

10 files changed

+448
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.hg
2+
dist
3+
node_modules

.hgignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules$
2+
dist$
3+
mongodb$
4+
.*\.swp$
5+
\.tmp$
6+
\.DS_Store$
7+
\.session.vim$
8+
config\.json$

graphQL/.typeDefs.js.swp

12 KB
Binary file not shown.

graphQL/typeDefs.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
const schema = `
2+
3+
interface Node {
4+
id: ID!
5+
}
6+
7+
type PageInfo {
8+
hasNextPage: Boolean!
9+
hasPreviousPage: Boolean!
10+
startCursor: String
11+
endCursor: String
12+
}
13+
14+
type Phone implements Node {
15+
id: ID!
16+
no: String!
17+
name: String!
18+
}
19+
20+
type PhoneEdge {
21+
cursor: String!
22+
node: Phone!
23+
}
24+
25+
type PhoneConnection {
26+
pageInfo: PageInfo!
27+
edges: [PhoneEdge]!
28+
}
29+
30+
type Query {
31+
phone(id:ID!): Phone
32+
phones(after:Int,first:Int,before:Int,last:Int): PhoneConnection!
33+
}
34+
35+
input SubmitPhoneInput{
36+
id: ID!
37+
name: String
38+
no: String
39+
clientMutationId: String!
40+
}
41+
42+
type SubmitPhonePayload{
43+
phone: Phone
44+
clientMutationId: String!
45+
}
46+
47+
input DeleteByIdInput{
48+
id: ID!
49+
clientMutationId: String!
50+
}
51+
52+
type DeleteByIdPayload{
53+
success: Int!
54+
clientMutationId: String!
55+
}
56+
57+
type Mutation {
58+
deletePhone(input:DeleteByIdInput!):DeleteByIdPayload!
59+
submitPhone(input:SubmitPhoneInput!):SubmitPhonePayload!
60+
}
61+
62+
schema {
63+
query: Query
64+
mutation: Mutation
65+
}
66+
`;
67+
68+
export default schema;

package.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "phones-gql.devbucket.me",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test:server": "mocha-webpack --require 'babel-core/register' --require babel-polyfill --webpack-config webpack.config-test.js \"test/server/**/*.test.js\"",
8+
"nodemon:server": "nodemon dist/server/server.js",
9+
"watch-dev-build:server": "webpack --watch --progress --config webpack.config-server.js",
10+
"watch:server": "npm-run-all --parallel watch-dev-build:server nodemon:server",
11+
"build:server": "webpack --progress --config webpack.config-server.js",
12+
"start:server:dev": "forever start -a -l forever.log -o forever.log -e forever.log --spinSleepTime=1000 --workingDir /home/dev/public_html/phones-gql.devbucket.me /home/dev/public_html/phones-gql.devbucket.me/dist/server/server.js",
13+
"stop:server:dev": "forever stop /home/dev/public_html/phones-gql.devbucket.me/dist/server/server.js"
14+
},
15+
"author": "S4Y Solution <android@s4y.solutions>",
16+
"license": "ISC",
17+
"dependencies": {
18+
"apollo-server": "0.3.3",
19+
"babel-cli": "6.18.0",
20+
"babel-core": "6.18.2",
21+
"babel-loader": "6.2.7",
22+
"babel-polyfill": "6.16.0",
23+
"babel-preset-es2015": "6.18.0",
24+
"babel-preset-es2017": "6.16.0",
25+
"chai": "3.5.0",
26+
"chai-as-promised": "6.0.0",
27+
"clean-webpack-plugin": "0.1.14",
28+
"graphql": "0.7.2",
29+
"graphql-server-koa": "0.4.3",
30+
"graphql-subscriptions": "0.2.1",
31+
"graphql-tools": "0.8.1",
32+
"json-loader": "0.5.4",
33+
"kcors": "2.2.0",
34+
"koa": "2.0.0-alpha.7",
35+
"koa-bodyparser": "3.2.0",
36+
"koa-logger": "2.0.0",
37+
"koa-router": "7.0.1",
38+
"mocha": "3.1.2",
39+
"mocha-webpack": "0.7.0",
40+
"npm-run-all": "3.1.1",
41+
"webpack": "1.13.3",
42+
"webpack-merge": "0.15.0",
43+
"webpack-node-externals": "1.5.4",
44+
"webpack-validator": "2.2.9"
45+
}
46+
}

server/.resolvers.js.swp

16 KB
Binary file not shown.

server/config.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"serverPort":"3000"
3+
}

server/resolvers.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import {locatedError} from 'graphql/error';
2+
3+
let fakes=
4+
[
5+
{id:0,no:'000000000',name:'Aaaa 0 Aaaaaa'},
6+
{id:1,no:'111111110',name:'Bbbb 0 Bbbbbb'},
7+
{id:2,no:'222222220',name:'Cccc 0 Cccccc'},
8+
{id:3,no:'333333330',name:'Dddd 0 Dddddd'},
9+
{id:4,no:'444444440',name:'Eeee 0 Eeeeee'},
10+
{id:5,no:'555555550',name:'Ffff 0 Ffffff'},
11+
{id:6,no:'666666660',name:'Gggg 0 Gggggg'},
12+
{id:7,no:'777777770',name:'Hhhh 0 Hhhhhh'},
13+
{id:8,no:'888888880',name:'Iiii 0 Iiiiii'},
14+
{id:9,no:'999999990',name:'Jjjj 0 Jjjjjj'},
15+
{id:10,no:'000000001',name:'Aaaa 1 Aaaaaa'},
16+
{id:11,no:'111111111',name:'Bbbb 1 Bbbbbb'},
17+
{id:12,no:'222222221',name:'Cccc 1 Cccccc'},
18+
{id:13,no:'333333331',name:'Dddd 1 Dddddd'},
19+
{id:14,no:'444444441',name:'Eeee 1 Eeeeee'},
20+
{id:15,no:'555555551',name:'Ffff 1 Ffffff'},
21+
{id:16,no:'666666661',name:'Gggg 1 Gggggg'},
22+
{id:17,no:'777777771',name:'Hhhh 1 Hhhhhh'},
23+
{id:18,no:'888888881',name:'Iiii 1 Iiiiii'},
24+
{id:19,no:'999999991',name:'Jjjj 1 Jjjjjj'},
25+
{id:20,no:'000000002',name:'Aaaa 2 Aaaaaa'},
26+
{id:21,no:'111111112',name:'Bbbb 2 Bbbbbb'},
27+
{id:22,no:'222222222',name:'Cccc 2 Cccccc'},
28+
{id:23,no:'333333332',name:'Dddd 2 Dddddd'},
29+
{id:24,no:'444444442',name:'Eeee 2 Eeeeee'},
30+
{id:25,no:'555555552',name:'Ffff 2 Ffffff'},
31+
{id:26,no:'666666662',name:'Gggg 2 Gggggg'},
32+
{id:27,no:'777777772',name:'Hhhh 2 Hhhhhh'},
33+
{id:28,no:'888888882',name:'Iiii 2 Iiiiii'},
34+
{id:29,no:'999999992',name:'Jjjj 2 Jjjjjj'},
35+
{id:30,no:'000000003',name:'Aaaa 3 Aaaaaa'},
36+
{id:31,no:'111111113',name:'Bbbb 3 Bbbbbb'},
37+
{id:32,no:'222222223',name:'Cccc 3 Cccccc'},
38+
{id:33,no:'333333333',name:'Dddd 3 Dddddd'},
39+
{id:34,no:'444444443',name:'Eeee 3 Eeeeee'},
40+
{id:35,no:'555555553',name:'Ffff 3 Ffffff'},
41+
{id:36,no:'666666663',name:'Gggg 3 Gggggg'},
42+
{id:37,no:'777777773',name:'Hhhh 3 Hhhhhh'},
43+
{id:38,no:'888888883',name:'Iiii 3 Iiiiii'},
44+
{id:39,no:'999999993',name:'Jjjj 3 Jjjjjj'}
45+
];
46+
export default {
47+
Query: {
48+
phone:(root,args,ctx,info)=>{
49+
return fakes[args.id];
50+
},
51+
phones:(root,args,ctx,info)=>{
52+
let a=(args.after==undefined||args.after<0)?0:args.after+1;
53+
let b=(args.before==undefined||args.before>fakes.length)?fakes.length:args.before;
54+
55+
if (args.first!=undefined){
56+
if (args.first<0)
57+
throw new Error('FIRST parameter must not be less than zero');
58+
if (a+args.first<b)
59+
b=a+args.first;
60+
if (args.last!=undefined)
61+
throw new Error('FIRST parameter must not be used simultaneously with LAST');
62+
}else if (args.last!=undefined){
63+
if (args.last<0)
64+
throw locatedError(new Error('LAST parameter must not be less than zero'));
65+
if (b-args.last>a){
66+
a=(b-args.last);
67+
}
68+
}
69+
70+
return {
71+
pageInfo:{
72+
hasNextPage:b<fakes.length,
73+
hasPreviousPage:a>0,
74+
startCursor:fakes[a].id,
75+
endCursor:fakes[b-1].id
76+
},
77+
edges:fakes.slice(a,b).map((fake)=>({cursor:fake.id,node:fake}))
78+
}
79+
},
80+
},
81+
Mutation: {
82+
submitPhone:(root,args)=>{
83+
let {id,no,name}=args.input;
84+
if (id && id!='add' && id<40){
85+
throw locatedError({message:"ID must be above 39"})
86+
}
87+
let max_id=-1;
88+
fakes=fakes.map(v=>{
89+
if (v.id==id){
90+
max_id=-2; //flag of updated
91+
return {id,no,name}
92+
}else{
93+
if (max_id!=-2 && v.id>max_id) max_id=v.id;
94+
return v;
95+
}
96+
});
97+
if (max_id!=-2) { //not update, i.e. new
98+
id=max_id+1;
99+
fakes.push({id,no,name});
100+
}
101+
return {phone:{id,no,name},clientMutationId:args.input.clientMutationId};
102+
},
103+
deletePhone:(root,args)=>{
104+
const id=(args.input.id);
105+
if (id<40){
106+
throw locatedError({message:"ID must be above 39"})
107+
}else{
108+
const prev=fakes;
109+
fakes=fakes.filter(fake=>fake.id!=id);
110+
return {
111+
success:prev.length>fakes.length,
112+
clientMutationId:args.input.clientMutationId
113+
}
114+
}
115+
}
116+
}
117+
}

server/server.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import polyfill from 'babel-polyfill'
2+
import koa from 'koa';
3+
import koaRouter from 'koa-router';
4+
import koaBody from 'koa-bodyparser';
5+
import logger from 'koa-logger';
6+
import { apolloKoa,graphiqlKoa } from 'apollo-server';
7+
import { makeExecutableSchema } from 'graphql-tools';
8+
import typeDefs from '../graphQL/typeDefs'
9+
import resolvers from './resolvers'
10+
import {serverPort} from './config.json'
11+
import cors from 'kcors';
12+
13+
const executableSchema = makeExecutableSchema({typeDefs,resolvers});
14+
15+
const router = new koaRouter();
16+
const PORT = serverPort;
17+
const app = new koa();
18+
19+
app.use(cors());
20+
21+
app.use(koaBody());
22+
23+
app.use(async (ctx, next) => {
24+
try {
25+
await next();
26+
} catch (err) {
27+
ctx.body = { message: err.message };
28+
ctx.status = err.status || 500;
29+
}
30+
});
31+
32+
router.get('/graphiql', graphiqlKoa({
33+
endpointURL: '/graphql',
34+
query: `query {
35+
phones (after:10,first:10) {
36+
pageInfo{
37+
hasNextPage,
38+
hasPreviousPage,
39+
startCursor,
40+
endCursor
41+
},
42+
edges{
43+
cursor,
44+
node{
45+
id,
46+
no,
47+
name
48+
}
49+
}
50+
}
51+
}`,
52+
debug: true
53+
}));
54+
router.post('/graphql', apolloKoa({ schema: executableSchema }));
55+
56+
app.use(logger())
57+
.use(router.routes())
58+
.use(router.allowedMethods());
59+
60+
export default app;
61+
62+
(async ()=>{
63+
try {
64+
await app.listen(PORT);
65+
console.log("Server is listening on "+PORT);
66+
}catch(error){
67+
console.log(error);
68+
}
69+
})()

0 commit comments

Comments
 (0)