Skip to content

Commit 6bc34cf

Browse files
authored
refactor: change to GrAMPS 1.x structure (#1)
* refactor: change to GrAMPS 1.x structure Extract mocks to a new file, change context to namespace, add new tests, simplify resolvers. BREAKING CHANGES: resolver structure is now more similar to Apollo resolvers, context is now namespace, mocks are a separate file and object now. re gramps-graphql/gramps-express#39 * fix: 1.x data sources no longer extend Query * fix: 1.x data source context doesn’t use namespace * feat: add errors * fix: error handling and tests * fix: actually, didn’t need that namespace * v1.0.0-beta.4 * feat: remove @gramps/gramps-express dependency - move to @gramps/rest-helpers for connector/model classes * update cli version and dev command
1 parent 7726b24 commit 6bc34cf

13 files changed

Lines changed: 722 additions & 185 deletions

package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
],
1818
"scripts": {
1919
"prepush": "npm test",
20+
"prepublish": "npm run build",
2021
"prebuild": "del-cli ./dist",
2122
"build": "babel src -d dist",
22-
"dev": "gramps --data-source-dir ./",
23+
"dev": "gramps dev -d ./",
2324
"mock-data": "npm run dev -- --mock",
2425
"live-data": "npm run dev -- --live",
2526
"lint": "eslint src/",
@@ -32,14 +33,17 @@
3233
],
3334
"license": "MIT",
3435
"dependencies": {
35-
"@gramps/gramps-express": "^0.1.3",
36+
"@gramps/errors": "^1.0.0",
37+
"@gramps/rest-helpers": "^1.0.0-beta.3",
3638
"casual": "^1.5.14"
3739
},
3840
"peerDependencies": {
3941
"graphql": "^0.9.0 || ^0.10.0 || ^0.11.0",
4042
"graphql-tools": "^1.2.1 || ^2.5.1"
4143
},
4244
"devDependencies": {
45+
"@gramps/cli": "^1.0.0-beta.4",
46+
"@gramps/gramps": "^1.0.0-beta-7",
4347
"babel-cli": "^6.24.1",
4448
"babel-eslint": "^8.0.1",
4549
"babel-jest": "^21.2.0",
@@ -77,5 +81,5 @@
7781
}
7882
}
7983
},
80-
"version": "0.0.0-development"
84+
"version": "1.0.0-beta.4"
8185
}

src/connector.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import { GraphQLConnector } from '@gramps/gramps-express';
1+
import { GraphQLConnector } from '@gramps/rest-helpers';
22

33
export default class XKCDConnector extends GraphQLConnector {
4+
constructor() {
5+
super();
6+
7+
this.headers.Accept = 'application/json';
8+
}
9+
410
/**
511
* xkcd exposes their API through the root URI.
612
* @member {string}

src/index.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import schema from './schema.graphql';
2-
import resolvers from './resolvers';
1+
import typeDefs from './schema.graphql';
32
import Connector from './connector';
43
import Model from './model';
4+
import resolvers from './resolvers';
5+
import mocks from './mocks';
56

67
/*
78
* For more information on the GrAMPS data sources, see
89
* https://ibm.biz/gramps-data-source-tutorial
910
*/
1011
export default {
11-
context: 'XKCD',
12-
model: new Model({ connector: new Connector() }),
13-
schema,
12+
namespace: 'XKCD',
13+
context: new Model({ connector: new Connector() }),
14+
typeDefs,
1415
resolvers,
16+
mocks,
1517
};

src/mocks.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import casual from 'casual';
2+
3+
export default {
4+
XKCD_Comic: () => ({
5+
num: casual.integer(0, 1999),
6+
alt: casual.sentence,
7+
title: casual.title,
8+
safe_title: casual.title,
9+
img: 'https://imgs.xkcd.com/comics/cast_iron_pans.png',
10+
transcript: casual.sentences(2),
11+
year: casual.year,
12+
month: casual.month,
13+
day: casual.integer(1, 31),
14+
link: casual.url,
15+
news: '',
16+
}),
17+
};

src/model.js

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { GraphQLModel, GrampsError } from '@gramps/gramps-express';
1+
import { GrampsError } from '@gramps/errors';
2+
import { GraphQLModel } from '@gramps/rest-helpers';
23

34
export default class XKCDModel extends GraphQLModel {
45
/**
@@ -7,7 +8,7 @@ export default class XKCDModel extends GraphQLModel {
78
*/
89
getLatestComic() {
910
return this.connector.get(`/info.0.json`).catch(res =>
10-
this.throwError(res, {
11+
this.throwError(res.error, {
1112
description: 'Could not load the latest xkcd comic',
1213
}),
1314
);
@@ -19,12 +20,19 @@ export default class XKCDModel extends GraphQLModel {
1920
* @return {Promise} resolves with the loaded comic data
2021
*/
2122
getComicById(id) {
22-
return this.connector.get(`/${id}/info.0.json`).catch(res =>
23-
this.throwError(res, {
24-
description: 'Could not load the given xkcd comic',
23+
return this.connector.get(`/${id}/info.0.json`).catch(res => {
24+
const description =
25+
res.statusCode >= 400 && res.statusCode < 500
26+
? 'Comic not found'
27+
: 'Could not load the given xkcd comic';
28+
29+
return this.throwError(false, {
30+
statusCode: res.statusCode,
31+
targetEndpoint: `${this.connector.apiBaseUri}/${id}/info.0.json`,
2532
data: { id },
26-
}),
27-
);
33+
description,
34+
});
35+
});
2836
}
2937

3038
/**
@@ -33,12 +41,19 @@ export default class XKCDModel extends GraphQLModel {
3341
* @param {Object?} customErrorData additional error data to display
3442
* @return {void}
3543
*/
36-
throwError(error, customErrorData = {}) {
44+
throwError(
45+
{
46+
statusCode = 500,
47+
message = 'Something went wrong.',
48+
targetEndpoint = null,
49+
} = {},
50+
customErrorData = {},
51+
) {
3752
const defaults = {
38-
statusCode: error.statusCode || 500,
53+
statusCode,
54+
targetEndpoint,
3955
errorCode: `${this.constructor.name}_Error`,
40-
description: error.message || 'Something went wrong.',
41-
targetEndpoint: error.options ? error.options.uri : null,
56+
description: message,
4257
graphqlModel: this.constructor.name,
4358
docsLink: 'https://ibm.biz/gramps-data-source-tutorial',
4459
};

src/resolvers.js

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,22 @@
1-
import casual from 'casual';
2-
31
export default {
4-
queryResolvers: {
2+
Query: {
53
getLatestComic: (_, __, context) =>
64
new Promise((resolve, reject) => {
7-
context.XKCD
5+
context
86
.getLatestComic()
97
.then(resolve)
108
.catch(reject);
119
}),
1210
getComicById: (_, { id }, context) =>
1311
new Promise((resolve, reject) => {
14-
context.XKCD
12+
context
1513
.getComicById(id)
1614
.then(resolve)
1715
.catch(reject);
1816
}),
1917
},
20-
21-
dataResolvers: {
22-
XKCD_Comic: {
23-
// The link is often empty, so build one if it’s not returned.
24-
link: data => data.link || `https://xkcd.com/${data.num}/`,
25-
},
26-
},
27-
28-
mockResolvers: {
29-
XKCD_Comic: () => ({
30-
num: casual.integer(0, 1999),
31-
alt: casual.sentence,
32-
title: casual.title,
33-
safe_title: casual.title,
34-
img: 'https://imgs.xkcd.com/comics/cast_iron_pans.png',
35-
transcript: casual.sentences(2),
36-
year: casual.year,
37-
month: casual.month,
38-
day: casual.integer(1, 31),
39-
link: casual.url,
40-
news: '',
41-
}),
18+
XKCD_Comic: {
19+
// The link is often empty, so build one if it’s not returned.
20+
link: data => data.link || `https://xkcd.com/${data.num}/`,
4221
},
4322
};

src/schema.graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# The query/queries to access data MUST extend the Query type.
2-
extend type Query {
2+
type Query {
33
# Load info about the most recent xkcd comic.
44
getLatestComic: XKCD_Comic
55

test/connector.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { GraphQLConnector } from '@gramps/gramps-express';
1+
import { GraphQLConnector } from '@gramps/rest-helpers';
22
import Connector from '../src/connector';
33

44
const connector = new Connector();

test/index.test.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@ import dataSource from '../src';
22
import Model from '../src/model';
33

44
describe('Data Source: XKCD', () => {
5-
it('returns a context type', () => {
6-
expect(dataSource.context).toBe('XKCD');
5+
it('returns a namespace', () => {
6+
expect(dataSource.namespace).toBe('XKCD');
77
});
88

9-
it('returns a model', () => {
10-
expect(dataSource.model).toBeInstanceOf(Model);
9+
it('returns its model as a context', () => {
10+
expect(dataSource.context).toBeInstanceOf(Model);
1111
});
1212

13-
it('returns a schema', () => {
14-
expect(dataSource.schema).toBeTruthy();
13+
it('returns typeDefs', () => {
14+
expect(dataSource.typeDefs).toBeTruthy();
1515
});
1616

1717
it('returns a resolver object', () => {
18-
expect(Object.keys(dataSource.resolvers)).toEqual([
19-
'queryResolvers',
20-
'dataResolvers',
21-
'mockResolvers',
22-
]);
18+
expect(dataSource.resolvers).toBeTruthy();
19+
});
20+
21+
it('returns mock resolvers', () => {
22+
expect(dataSource.mocks).toBeTruthy();
2323
});
2424
});

test/mocks.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import mocks from '../src/mocks';
2+
import expectMockFields from './helpers/expectMockFields';
3+
4+
describe('XKCD mock resolvers', () => {
5+
describe('XKCD_Comic', () => {
6+
const mockResolvers = mocks.XKCD_Comic();
7+
8+
expectMockFields(mockResolvers, [
9+
'num',
10+
'alt',
11+
'title',
12+
'safe_title',
13+
'img',
14+
'transcript',
15+
'year',
16+
'month',
17+
'day',
18+
'link',
19+
'news',
20+
]);
21+
});
22+
});

0 commit comments

Comments
 (0)