Skip to content

Commit 7a1b3a1

Browse files
borodayevnodkz
authored andcommitted
feat: add findById and updateById resolvers (#35)
1 parent 4f132fd commit 7a1b3a1

11 files changed

+347
-11
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ node_modules
4141
# Transpiled code
4242
/es
4343
/lib
44+
/__fixtures__
4445

4546
coverage
4647
.nyc_output

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"graphql-compose": ">=2.13.1 || >=3.0.0 || >=4.0.0"
3131
},
3232
"devDependencies": {
33+
"aws-sdk": "^2.224.1",
3334
"babel-cli": "^6.26.0",
3435
"babel-eslint": "^8.2.3",
3536
"babel-jest": "^22.4.3",
@@ -85,6 +86,7 @@
8586
"docker:v2": "node ./scripts/docker/start 2 & wait",
8687
"docker:v5": "node ./scripts/docker/start 5 & wait",
8788
"link": "yarn build && yarn link graphql-compose && yarn link",
88-
"unlink": "yarn unlink graphql-compose && yarn add graphql-compose"
89+
"unlink": "yarn unlink graphql-compose && yarn add graphql-compose",
90+
"my-demo": "./node_modules/.bin/babel-node ./__fixtures__/index.js"
8991
}
9092
}

src/ElasticApiParser.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export default class ElasticApiParser {
7575
parsedSource: ElasticParsedSourceT;
7676

7777
constructor(opts: ElasticApiParserOptsT = {}) {
78-
// avaliable varsions can be found in installed package `elasticsearch`
78+
// avaliable versions can be found in installed package `elasticsearch`
7979
// in file /node_modules/elasticsearch/src/lib/apis/index.js
8080
this.apiVersion =
8181
opts.apiVersion ||
@@ -312,7 +312,6 @@ export default class ElasticApiParser {
312312
...args,
313313
});
314314
}
315-
316315
return client[elasticMethod]({ ...methodArgs, ...args });
317316
},
318317
};

src/composeWithElastic.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { TypeComposer } from 'graphql-compose';
44
import { convertToSourceTC, inputPropertiesToGraphQLTypes } from './mappingConverter';
55
import createSearchResolver from './resolvers/search';
66
import createSearchConnectionResolver from './resolvers/searchConnection';
7+
import createFindByIdResolver from './resolvers/findById';
8+
import createUpdateByIdResolver from './resolvers/updateById';
79

810
import type { ElasticMappingT } from './mappingConverter';
911

@@ -63,9 +65,13 @@ export function composeWithElastic(opts: composeWithElasticOptsT): TypeComposer
6365

6466
const searchR = createSearchResolver(fieldMap, sourceTC, opts);
6567
const searchConnectionR = createSearchConnectionResolver(searchR, opts);
68+
const findByIdR = createFindByIdResolver(fieldMap, sourceTC, opts);
69+
const updateByIdR = createUpdateByIdResolver(fieldMap, sourceTC, opts);
6670

6771
sourceTC.addResolver(searchR);
6872
sourceTC.addResolver(searchConnectionR);
73+
sourceTC.addResolver(findByIdR);
74+
sourceTC.addResolver(updateByIdR);
6975

7076
return sourceTC;
7177
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* @flow */
2+
3+
import { Resolver } from 'graphql-compose';
4+
import createFindByIdResolver from '../findById';
5+
import elasticClient from '../../__mocks__/elasticClient';
6+
import { CvTC, CvFieldMap } from '../../__mocks__/cv';
7+
8+
const findByIdResolver = createFindByIdResolver(CvFieldMap, CvTC, {
9+
elasticClient,
10+
elasticIndex: 'cv',
11+
elasticType: 'cv',
12+
});
13+
14+
describe('findById', () => {
15+
it('return instance of Resolver', () => {
16+
expect(findByIdResolver).toBeInstanceOf(Resolver);
17+
});
18+
19+
it('check args', () => {
20+
expect(findByIdResolver.hasArg('id')).toBeTruthy();
21+
});
22+
23+
it('resolve', () => {
24+
findByIdResolver.resolve({ args: { id: '4554' }, context: { elasticClient } }).then(res => {
25+
console.log(res); // eslint-disable-line
26+
});
27+
});
28+
});

src/resolvers/findById.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/* @flow */
2+
3+
import { Resolver, TypeComposer } from 'graphql-compose';
4+
import type { ResolveParams } from 'graphql-compose';
5+
import type { FieldsMapByElasticType } from '../mappingConverter';
6+
import ElasticApiParser from '../ElasticApiParser';
7+
import { getFindByIdOutputTC } from '../types/FindByIdOutput';
8+
9+
export type ElasticResolverOpts = {
10+
prefix?: ?string,
11+
elasticIndex: string,
12+
elasticType: string,
13+
elasticClient: Object,
14+
};
15+
16+
export default function createFindByIdResolver(
17+
fieldMap: FieldsMapByElasticType,
18+
sourceTC: TypeComposer,
19+
opts: ElasticResolverOpts
20+
): Resolver {
21+
if (!fieldMap || !fieldMap._all) {
22+
throw new Error(
23+
'First arg for Resolver findById() should be fieldMap of FieldsMapByElasticType type.'
24+
);
25+
}
26+
27+
if (!sourceTC || sourceTC.constructor.name !== 'TypeComposer') {
28+
throw new Error('Second arg for Resolver findById() should be instance of TypeComposer.');
29+
}
30+
31+
const prefix = opts.prefix || 'Es';
32+
33+
const parser = new ElasticApiParser({
34+
elasticClient: opts.elasticClient,
35+
prefix,
36+
});
37+
38+
const findByIdFC = parser.generateFieldConfig('get', {
39+
index: opts.elasticIndex,
40+
type: opts.elasticType,
41+
});
42+
43+
const argsConfigMap = {
44+
id: 'String!',
45+
};
46+
47+
const type = getFindByIdOutputTC({ prefix, fieldMap, sourceTC });
48+
49+
return new Resolver({
50+
type,
51+
name: 'findById',
52+
kind: 'query',
53+
args: argsConfigMap,
54+
resolve: async (rp: ResolveParams<*, *>) => {
55+
const { source, args, context, info } = rp;
56+
57+
if (!args.id) {
58+
throw new Error(`Missed 'id' argument!`);
59+
}
60+
61+
const res = await findByIdFC.resolve(source, args, context, info);
62+
const { _index, _type, _id, _version, _source } = res || {};
63+
64+
return {
65+
_index,
66+
_type,
67+
_id,
68+
_version,
69+
..._source,
70+
};
71+
},
72+
});
73+
}

src/resolvers/search.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import ElasticApiParser from '../ElasticApiParser';
88
import { getSearchBodyITC, prepareBodyInResolve } from '../elasticDSL/SearchBody';
99
import { getSearchOutputTC } from '../types/SearchOutput';
1010

11-
export type ElasticSearchResolverOpts = {
11+
export type ElasticResolverOpts = {
1212
prefix?: ?string,
1313
elasticIndex: string,
1414
elasticType: string,
@@ -18,7 +18,7 @@ export type ElasticSearchResolverOpts = {
1818
export default function createSearchResolver(
1919
fieldMap: FieldsMapByElasticType,
2020
sourceTC: TypeComposer,
21-
opts: ElasticSearchResolverOpts
21+
opts: ElasticResolverOpts
2222
): Resolver {
2323
if (!fieldMap || !fieldMap._all) {
2424
throw new Error(

src/resolvers/updateById.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/* @flow */
2+
3+
import { Resolver, TypeComposer, InputTypeComposer, type ResolveParams } from 'graphql-compose';
4+
import type { FieldsMapByElasticType } from '../mappingConverter';
5+
import ElasticApiParser from '../ElasticApiParser';
6+
import { getUpdateByIdOutputTC } from '../types/UpdateByIdOutput';
7+
import { getTypeName, getOrSetType, desc } from '../utils';
8+
9+
export type ElasticResolverOpts = {
10+
prefix?: ?string,
11+
elasticIndex: string,
12+
elasticType: string,
13+
elasticClient: Object,
14+
};
15+
16+
export default function createUpdateByIdResolver(
17+
fieldMap: FieldsMapByElasticType,
18+
sourceTC: TypeComposer,
19+
opts: ElasticResolverOpts
20+
): Resolver {
21+
if (!fieldMap || !fieldMap._all) {
22+
throw new Error(
23+
'First arg for Resolver updateById() should be fieldMap of FieldsMapByElasticType type.'
24+
);
25+
}
26+
27+
if (!sourceTC || sourceTC.constructor.name !== 'TypeComposer') {
28+
throw new Error('Second arg for Resolver updateById() should be instance of TypeComposer.');
29+
}
30+
31+
const prefix = opts.prefix || 'Es';
32+
33+
const parser = new ElasticApiParser({
34+
elasticClient: opts.elasticClient,
35+
prefix,
36+
});
37+
38+
const updateByIdFC = parser.generateFieldConfig('update', {
39+
index: opts.elasticIndex,
40+
type: opts.elasticType,
41+
_source: true,
42+
});
43+
44+
const argsConfigMap = {
45+
id: 'String!',
46+
record: getRecordITC(fieldMap).getTypeAsRequired(),
47+
};
48+
49+
const type = getUpdateByIdOutputTC({ prefix, fieldMap, sourceTC });
50+
51+
return new Resolver({
52+
type,
53+
name: 'updateById',
54+
kind: 'mutation',
55+
args: argsConfigMap,
56+
resolve: async (rp: ResolveParams<*, *>) => {
57+
const { source, args, context, info } = rp;
58+
59+
args.body = {
60+
doc: {
61+
...args.record,
62+
},
63+
};
64+
delete args.record;
65+
66+
const res = await updateByIdFC.resolve(source, args, context, info);
67+
68+
const { _index, _type, _id, _version, result, get } = res || {};
69+
const { _source } = get || {};
70+
71+
return {
72+
_id,
73+
_index,
74+
_type,
75+
_version,
76+
result,
77+
..._source,
78+
};
79+
},
80+
});
81+
}
82+
83+
export function getRecordITC(fieldMap: FieldsMapByElasticType): InputTypeComposer {
84+
const name = getTypeName('Record', {});
85+
const description = desc(`The record from Elastic Search`);
86+
return getOrSetType(name, () =>
87+
InputTypeComposer.create({
88+
name,
89+
description,
90+
fields: { ...fieldMap._all },
91+
})
92+
);
93+
}

src/types/FindByIdOutput.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* @flow */
2+
3+
import { TypeComposer } from 'graphql-compose';
4+
import { getTypeName, getOrSetType } from '../utils';
5+
import type { FieldsMapByElasticType } from '../mappingConverter';
6+
7+
export type FindByIdOptsT = {
8+
prefix?: string,
9+
postfix?: string,
10+
fieldMap?: FieldsMapByElasticType,
11+
sourceTC: TypeComposer,
12+
};
13+
14+
export function getFindByIdOutputTC(opts: FindByIdOptsT): TypeComposer {
15+
const name = getTypeName('FindByIdOutput', opts);
16+
const { sourceTC } = opts || {};
17+
return getOrSetType(name, () =>
18+
TypeComposer.create({
19+
name,
20+
fields: {
21+
_id: 'String',
22+
_index: 'String',
23+
_type: 'String',
24+
_version: 'Int',
25+
...sourceTC.getFields(),
26+
},
27+
})
28+
);
29+
}

src/types/UpdateByIdOutput.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* @flow */
2+
3+
import { TypeComposer } from 'graphql-compose';
4+
import { getTypeName, getOrSetType } from '../utils';
5+
import type { FieldsMapByElasticType } from '../mappingConverter';
6+
7+
export type UpdateByIdOptsT = {
8+
prefix?: string,
9+
postfix?: string,
10+
fieldMap?: FieldsMapByElasticType,
11+
sourceTC: TypeComposer,
12+
};
13+
14+
export function getUpdateByIdOutputTC(opts: UpdateByIdOptsT): TypeComposer {
15+
const name = getTypeName('UpdateByIdOutput', opts);
16+
const { sourceTC } = opts || {};
17+
return getOrSetType(name, () =>
18+
TypeComposer.create({
19+
name,
20+
fields: {
21+
_id: 'String',
22+
_index: 'String',
23+
_type: 'String',
24+
_version: 'Int',
25+
result: 'String',
26+
...sourceTC.getFields(),
27+
},
28+
})
29+
);
30+
}

0 commit comments

Comments
 (0)