Skip to content

Add 'findById' & 'updateById' resolvers #35

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

Merged
merged 8 commits into from
Apr 20, 2018
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ node_modules
# Transpiled code
/es
/lib
/__fixtures__

coverage
.nyc_output
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"graphql-compose": ">=2.13.1 || >=3.0.0 || >=4.0.0"
},
"devDependencies": {
"aws-sdk": "^2.224.1",
"babel-cli": "^6.26.0",
"babel-eslint": "^8.2.3",
"babel-jest": "^22.4.3",
Expand Down Expand Up @@ -85,6 +86,7 @@
"docker:v2": "node ./scripts/docker/start 2 & wait",
"docker:v5": "node ./scripts/docker/start 5 & wait",
"link": "yarn build && yarn link graphql-compose && yarn link",
"unlink": "yarn unlink graphql-compose && yarn add graphql-compose"
"unlink": "yarn unlink graphql-compose && yarn add graphql-compose",
"my-demo": "./node_modules/.bin/babel-node ./__fixtures__/index.js"
}
}
3 changes: 1 addition & 2 deletions src/ElasticApiParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default class ElasticApiParser {
parsedSource: ElasticParsedSourceT;

constructor(opts: ElasticApiParserOptsT = {}) {
// avaliable varsions can be found in installed package `elasticsearch`
// avaliable versions can be found in installed package `elasticsearch`
// in file /node_modules/elasticsearch/src/lib/apis/index.js
this.apiVersion =
opts.apiVersion ||
Expand Down Expand Up @@ -312,7 +312,6 @@ export default class ElasticApiParser {
...args,
});
}

return client[elasticMethod]({ ...methodArgs, ...args });
},
};
Expand Down
6 changes: 6 additions & 0 deletions src/composeWithElastic.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { TypeComposer } from 'graphql-compose';
import { convertToSourceTC, inputPropertiesToGraphQLTypes } from './mappingConverter';
import createSearchResolver from './resolvers/search';
import createSearchConnectionResolver from './resolvers/searchConnection';
import createFindByIdResolver from './resolvers/findById';
import createUpdateByIdResolver from './resolvers/updateById';

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

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

const searchR = createSearchResolver(fieldMap, sourceTC, opts);
const searchConnectionR = createSearchConnectionResolver(searchR, opts);
const findByIdR = createFindByIdResolver(fieldMap, sourceTC, opts);
const updateByIdR = createUpdateByIdResolver(fieldMap, sourceTC, opts);

sourceTC.addResolver(searchR);
sourceTC.addResolver(searchConnectionR);
sourceTC.addResolver(findByIdR);
sourceTC.addResolver(updateByIdR);

return sourceTC;
}
28 changes: 28 additions & 0 deletions src/resolvers/__tests__/findById-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* @flow */

import { Resolver } from 'graphql-compose';
import createFindByIdResolver from '../findById';
import elasticClient from '../../__mocks__/elasticClient';
import { CvTC, CvFieldMap } from '../../__mocks__/cv';

const findByIdResolver = createFindByIdResolver(CvFieldMap, CvTC, {
elasticClient,
elasticIndex: 'cv',
elasticType: 'cv',
});

describe('findById', () => {
it('return instance of Resolver', () => {
expect(findByIdResolver).toBeInstanceOf(Resolver);
});

it('check args', () => {
expect(findByIdResolver.hasArg('id')).toBeTruthy();
});

it('resolve', () => {
findByIdResolver.resolve({ args: { id: '4554' }, context: { elasticClient } }).then(res => {
console.log(res); // eslint-disable-line
});
});
});
73 changes: 73 additions & 0 deletions src/resolvers/findById.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* @flow */

import { Resolver, TypeComposer } from 'graphql-compose';
import type { ResolveParams } from 'graphql-compose';
import type { FieldsMapByElasticType } from '../mappingConverter';
import ElasticApiParser from '../ElasticApiParser';
import { getFindByIdOutputTC } from '../types/FindByIdOutput';

export type ElasticResolverOpts = {
prefix?: ?string,
elasticIndex: string,
elasticType: string,
elasticClient: Object,
};

export default function createFindByIdResolver(
fieldMap: FieldsMapByElasticType,
sourceTC: TypeComposer,
opts: ElasticResolverOpts
): Resolver {
if (!fieldMap || !fieldMap._all) {
throw new Error(
'First arg for Resolver findById() should be fieldMap of FieldsMapByElasticType type.'
);
}

if (!sourceTC || sourceTC.constructor.name !== 'TypeComposer') {
throw new Error('Second arg for Resolver findById() should be instance of TypeComposer.');
}

const prefix = opts.prefix || 'Es';

const parser = new ElasticApiParser({
elasticClient: opts.elasticClient,
prefix,
});

const findByIdFC = parser.generateFieldConfig('get', {
index: opts.elasticIndex,
type: opts.elasticType,
});

const argsConfigMap = {
id: 'String!',
};

const type = getFindByIdOutputTC({ prefix, fieldMap, sourceTC });

return new Resolver({
type,
name: 'findById',
kind: 'query',
args: argsConfigMap,
resolve: async (rp: ResolveParams<*, *>) => {
const { source, args, context, info } = rp;

if (!args.id) {
throw new Error(`Missed 'id' argument!`);
}

const res = await findByIdFC.resolve(source, args, context, info);
const { _index, _type, _id, _version, _source } = res || {};

return {
_index,
_type,
_id,
_version,
..._source,
};
},
});
}
4 changes: 2 additions & 2 deletions src/resolvers/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ElasticApiParser from '../ElasticApiParser';
import { getSearchBodyITC, prepareBodyInResolve } from '../elasticDSL/SearchBody';
import { getSearchOutputTC } from '../types/SearchOutput';

export type ElasticSearchResolverOpts = {
export type ElasticResolverOpts = {
prefix?: ?string,
elasticIndex: string,
elasticType: string,
Expand All @@ -18,7 +18,7 @@ export type ElasticSearchResolverOpts = {
export default function createSearchResolver(
fieldMap: FieldsMapByElasticType,
sourceTC: TypeComposer,
opts: ElasticSearchResolverOpts
opts: ElasticResolverOpts
): Resolver {
if (!fieldMap || !fieldMap._all) {
throw new Error(
Expand Down
93 changes: 93 additions & 0 deletions src/resolvers/updateById.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/* @flow */

import { Resolver, TypeComposer, InputTypeComposer, type ResolveParams } from 'graphql-compose';
import type { FieldsMapByElasticType } from '../mappingConverter';
import ElasticApiParser from '../ElasticApiParser';
import { getUpdateByIdOutputTC } from '../types/UpdateByIdOutput';
import { getTypeName, getOrSetType, desc } from '../utils';

export type ElasticResolverOpts = {
prefix?: ?string,
elasticIndex: string,
elasticType: string,
elasticClient: Object,
};

export default function createUpdateByIdResolver(
fieldMap: FieldsMapByElasticType,
sourceTC: TypeComposer,
opts: ElasticResolverOpts
): Resolver {
if (!fieldMap || !fieldMap._all) {
throw new Error(
'First arg for Resolver updateById() should be fieldMap of FieldsMapByElasticType type.'
);
}

if (!sourceTC || sourceTC.constructor.name !== 'TypeComposer') {
throw new Error('Second arg for Resolver updateById() should be instance of TypeComposer.');
}

const prefix = opts.prefix || 'Es';

const parser = new ElasticApiParser({
elasticClient: opts.elasticClient,
prefix,
});

const updateByIdFC = parser.generateFieldConfig('update', {
index: opts.elasticIndex,
type: opts.elasticType,
_source: true,
});

const argsConfigMap = {
id: 'String!',
record: getRecordITC(fieldMap).getTypeAsRequired(),
};

const type = getUpdateByIdOutputTC({ prefix, fieldMap, sourceTC });

return new Resolver({
type,
name: 'updateById',
kind: 'mutation',
args: argsConfigMap,
resolve: async (rp: ResolveParams<*, *>) => {
const { source, args, context, info } = rp;

args.body = {
doc: {
...args.record,
},
};
delete args.record;

const res = await updateByIdFC.resolve(source, args, context, info);

const { _index, _type, _id, _version, result, get } = res || {};
const { _source } = get || {};

return {
_id,
_index,
_type,
_version,
result,
..._source,
};
},
});
}

export function getRecordITC(fieldMap: FieldsMapByElasticType): InputTypeComposer {
const name = getTypeName('Record', {});
const description = desc(`The record from Elastic Search`);
return getOrSetType(name, () =>
InputTypeComposer.create({
name,
description,
fields: { ...fieldMap._all },
})
);
}
29 changes: 29 additions & 0 deletions src/types/FindByIdOutput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* @flow */

import { TypeComposer } from 'graphql-compose';
import { getTypeName, getOrSetType } from '../utils';
import type { FieldsMapByElasticType } from '../mappingConverter';

export type FindByIdOptsT = {
prefix?: string,
postfix?: string,
fieldMap?: FieldsMapByElasticType,
sourceTC: TypeComposer,
};

export function getFindByIdOutputTC(opts: FindByIdOptsT): TypeComposer {
const name = getTypeName('FindByIdOutput', opts);
const { sourceTC } = opts || {};
return getOrSetType(name, () =>
TypeComposer.create({
name,
fields: {
_id: 'String',
_index: 'String',
_type: 'String',
_version: 'Int',
...sourceTC.getFields(),
},
})
);
}
30 changes: 30 additions & 0 deletions src/types/UpdateByIdOutput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* @flow */

import { TypeComposer } from 'graphql-compose';
import { getTypeName, getOrSetType } from '../utils';
import type { FieldsMapByElasticType } from '../mappingConverter';

export type UpdateByIdOptsT = {
prefix?: string,
postfix?: string,
fieldMap?: FieldsMapByElasticType,
sourceTC: TypeComposer,
};

export function getUpdateByIdOutputTC(opts: UpdateByIdOptsT): TypeComposer {
const name = getTypeName('UpdateByIdOutput', opts);
const { sourceTC } = opts || {};
return getOrSetType(name, () =>
TypeComposer.create({
name,
fields: {
_id: 'String',
_index: 'String',
_type: 'String',
_version: 'Int',
result: 'String',
...sourceTC.getFields(),
},
})
);
}
Loading