Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
3,007 changes: 1,290 additions & 1,717 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 5 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@
},
"homepage": "https://github.com/uc-cdis/guppy#readme",
"dependencies": {
"@apollo/server": "^4.7.5",
"@elastic/elasticsearch": "~7.13.0",
"@gen3/ui-component": "^0.11.4",
"apollo-server": "^2.4.8",
"apollo-server-express": "^2.4.8",
"array.prototype.flat": "^1.2.2",
"array.prototype.flatmap": "^1.3.1",
"body-parser": "^1.20.2",
Expand All @@ -40,11 +39,11 @@
"express": "^4.18.2",
"file-saver": "^2.0.5",
"flat": "^5.0.2",
"graphql": "^14.1.1",
"graphql": "^16.7.1",
"graphql-depth-limit": "^1.1.0",
"graphql-middleware": "^3.0.2",
"graphql-parse-resolve-info": "^4.1.0",
"graphql-tools": "^4.0.4",
"graphql-middleware": "^6.1.34",
"graphql-parse-resolve-info": "^4.13.0",
"graphql-tools": "^9.0.0",
"graphql-type-json": "^0.3.2",
"helmet": "^7.0.0",
"isomorphic-fetch": "^3.0.0",
Expand Down
8 changes: 4 additions & 4 deletions src/server/es/__tests__/filter.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// eslint-disable-next-line
import nock from 'nock'; // must import this to enable mock data by nock
import { UserInputError } from 'apollo-server';
import { GraphQLError } from 'graphql';
import getFilterObj from '../filter';
import esInstance from '../index';
import setupMockDataEndpoint from '../../__mocks__/mockDataFromES';
Expand Down Expand Up @@ -216,12 +216,12 @@ describe('Transfer GraphQL filter to ES filter, filter unit', () => {
expect(() => { // for string field
const gqlFilter = { '+': { gender: 'female' } };
getFilterObj(esInstance, esIndex, esType, gqlFilter);
}).toThrow(UserInputError);
}).toThrow(GraphQLError);

expect(() => { // for int field
const gqlFilter = { '+': { file_count: 10 } };
getFilterObj(esInstance, esIndex, esType, gqlFilter);
}).toThrow(UserInputError);
}).toThrow(GraphQLError);
});

test('could throw err for nonexisting field', async () => {
Expand All @@ -230,7 +230,7 @@ describe('Transfer GraphQL filter to ES filter, filter unit', () => {
expect(() => { // for string field
const gqlFilter = { '=': { strange_field: 'value' } };
getFilterObj(esInstance, esIndex, esType, gqlFilter);
}).toThrow(UserInputError);
}).toThrow(GraphQLError);
});
});

Expand Down
18 changes: 9 additions & 9 deletions src/server/es/__tests__/sort.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// eslint-disable-next-line
import nock from 'nock'; // must import this to enable mock data by nock
import { UserInputError } from 'apollo-server';
import { GraphQLError } from 'graphql';
import getESSortBody from '../sort';
import esInstance from '../index';
import setupMockDataEndpoint from '../../__mocks__/mockDataFromES';
Expand Down Expand Up @@ -125,44 +125,44 @@ describe('Transfer GraphQL sort argument to ES sort argument', () => {
expect(() => {
const graphQLSort = { invalid_field: 'asc' };
getESSortBody(graphQLSort, esInstance, esIndex);
}).toThrow(UserInputError);
}).toThrow(GraphQLError);

expect(() => {
const graphQLSort = [{ invalid_field: 'asc' }];
getESSortBody(graphQLSort, esInstance, esIndex);
}).toThrow(UserInputError);
}).toThrow(GraphQLError);

expect(() => {
const graphQLSort = { gender: 'female', invalid_field: 'asc' };
getESSortBody(graphQLSort, esInstance, esIndex);
}).toThrow(UserInputError);
}).toThrow(GraphQLError);

expect(() => {
const graphQLSort = [{ gender: 'female', 'visits.invalid_field': 'asc' }];
getESSortBody(graphQLSort, esInstance, esIndex);
}).toThrow(UserInputError);
}).toThrow(GraphQLError);
});

test('array format sort arg with invalid method', async () => {
await esInstance.initialize();
expect(() => {
const graphQLSort = { gender: 'invalid_method' };
getESSortBody(graphQLSort, esInstance, esIndex);
}).toThrow(UserInputError);
}).toThrow(GraphQLError);

expect(() => {
const graphQLSort = [{ gender: 'invalid_method' }];
getESSortBody(graphQLSort, esInstance, esIndex);
}).toThrow(UserInputError);
}).toThrow(GraphQLError);

expect(() => {
const graphQLSort = { gender: 'asc', file_count: 'invalid_method' };
getESSortBody(graphQLSort, esInstance, esIndex);
}).toThrow(UserInputError);
}).toThrow(GraphQLError);

expect(() => {
const graphQLSort = { gender: 'asc', 'visits.visit_label': 'invalid_method' };
getESSortBody(graphQLSort, esInstance, esIndex);
}).toThrow(UserInputError);
}).toThrow(GraphQLError);
});
});
26 changes: 21 additions & 5 deletions src/server/es/aggs.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UserInputError } from 'apollo-server';
import { GraphQLError } from 'graphql';
import getFilterObj from './filter';
import {
AGGS_GLOBAL_STATS_NAME,
Expand Down Expand Up @@ -447,16 +447,32 @@ export const numericAggregation = async (
},
) => {
if (rangeStep <= 0) {
throw new UserInputError(`Invalid rangeStep ${rangeStep}`);
throw new GraphQLError(`Invalid rangeStep ${rangeStep}`, {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
if (rangeStart > rangeEnd) {
throw new UserInputError(`Invalid rangeStart (${rangeStep}) > rangeEnd (${rangeEnd})`);
throw new GraphQLError(`Invalid rangeStart (${rangeStep}) > rangeEnd (${rangeEnd})`, {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
if (binCount <= 0) {
throw new UserInputError(`Invalid binCount ${binCount}`);
throw new GraphQLError(`Invalid binCount ${binCount}`, {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
if (typeof rangeStep !== 'undefined' && typeof binCount !== 'undefined') {
throw new UserInputError('Invalid to set "rangeStep" and "binCount" at same time');
throw new GraphQLError('Invalid to set "rangeStep" and "binCount" at same time', {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
if (typeof rangeStep !== 'undefined') {
return numericHistogramWithFixedRangeStep(
Expand Down
68 changes: 56 additions & 12 deletions src/server/es/filter.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import _ from 'lodash';
import { ApolloError, UserInputError } from 'apollo-server';
import { GraphQLError } from 'graphql';
import { esFieldNumericTextTypeMapping, NumericTextTypeTypeEnum } from './const';
import config from '../config';

Expand All @@ -11,7 +11,11 @@ const fromPathToNode = (esInstance, esIndex, path) => {
if (n in node) {
node = node[n].properties;
} else {
throw new UserInputError(`Field ${n} does not exist in ES index`);
throw new GraphQLError(`Field ${n} does not exist in ES index`, {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
});
}
Expand All @@ -38,11 +42,19 @@ const getNumericTextType = (
) => {
const node = fromPathToNode(esInstance, esIndex, path);
if (!esInstance.fieldTypes[esIndex] || !node[field]) {
throw new UserInputError('Please check your syntax for input "filter" argument');
throw new GraphQLError('Please check your syntax for input "filter" argument', {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
const numericTextType = esFieldNumericTextTypeMapping[node[field].type];
if (typeof numericTextType === 'undefined') {
throw new ApolloError(`ES type ${node[field].type} not supported.`, 500);
throw new GraphQLError(`ES type ${node[field].type} not supported.`, {
extensions: {
code: 'INTERNAL_SERVER_ERROR',
},
});
}
return numericTextType;
};
Expand Down Expand Up @@ -122,7 +134,11 @@ const getFilterItemForString = (op, pField, value, path) => {
},
};
default:
throw new UserInputError(`Invalid operation "${op}" in filter argument.`);
throw new GraphQLError(`Invalid operation "${op}" in filter argument.`, {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
};

Expand Down Expand Up @@ -181,7 +197,11 @@ const getFilterItemForNumbers = (op, pField, value, path) => {
},
};
}
throw new UserInputError(`Invalid numeric operation "${op}" for field "${field}" in filter argument`);
throw new GraphQLError(`Invalid numeric operation "${op}" for field "${field}" in filter argument`, {
extensions: {
code: 'BAD_USER_INPUT',
},
});
};

const getESSearchFilterFragment = (esInstance, esIndex, fields, keyword) => {
Expand All @@ -193,9 +213,17 @@ const getESSearchFilterFragment = (esInstance, esIndex, fields, keyword) => {
// Check fields are valid
fields.forEach((f) => {
if (!esInstance.fieldTypes[esIndex]) {
throw new UserInputError(`es index ${esIndex} doesn't exist`);
throw new GraphQLError(`es index ${esIndex} doesn't exist`, {
extensions: {
code: 'BAD_USER_INPUT',
},
});
} else if (!esInstance.fieldTypes[esIndex][f]) {
throw new UserInputError(`invalid field ${f} in "filter" variable`);
throw new GraphQLError(`invalid field ${f} in "filter" variable`, {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
});
analyzedFields = fields.map((f) => `${f}${config.analyzedTextFieldSuffix}`);
Expand Down Expand Up @@ -285,16 +313,28 @@ const getFilterObj = (
}
} else if (topLevelOpLowerCase === 'search') {
if (!('keyword' in graphqlFilterObj[topLevelOp])) { // "keyword" required
throw new UserInputError('Invalid search filter syntax: missing \'keyword\' field');
throw new GraphQLError('Invalid search filter syntax: missing \'keyword\' field', {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
Object.keys(graphqlFilterObj[topLevelOp]).forEach((o) => { // check filter syntax
if (o !== 'keyword' && o !== 'fields') {
throw new UserInputError(`Invalid search filter syntax: unrecognized field '${o}'`);
throw new GraphQLError(`Invalid search filter syntax: unrecognized field '${o}'`, {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
});
const targetSearchKeyword = graphqlFilterObj[topLevelOp].keyword;
if (targetSearchKeyword.length < config.allowedMinimumSearchLen) {
throw new UserInputError(`Keyword too short (length < ${config.allowedMinimumSearchLen}`);
throw new GraphQLError(`Keyword too short (length < ${config.allowedMinimumSearchLen}`, {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
const targetSearchFields = graphqlFilterObj[topLevelOp].fields;
resultFilterObj = getESSearchFilterFragment(
Expand Down Expand Up @@ -348,7 +388,11 @@ const getFilterObj = (
} else if (numericOrTextType === NumericTextTypeTypeEnum.ES_NUMERIC_TYPE) {
resultFilterObj = getFilterItemForNumbers(topLevelOp, field, value, objPath);
} else {
throw new ApolloError(`Invalid es field type ${numericOrTextType}`, 500);
throw new GraphQLError(`Invalid es field type ${numericOrTextType}`, {
extensions: {
code: 'INTERNAL_SERVER_ERROR',
},
});
}
}
return resultFilterObj;
Expand Down
10 changes: 7 additions & 3 deletions src/server/es/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Client } from '@elastic/elasticsearch';
import _ from 'lodash';
import { UserInputError } from 'apollo-server';
import { GraphQLError } from 'graphql';
import config from '../config';
import getFilterObj from './filter';
import getESSortBody from './sort';
Expand Down Expand Up @@ -475,9 +475,13 @@ class ES {
esIndex, esType, fields, filter, sort, offset, size,
}) {
if (typeof size !== 'undefined' && offset + size > SCROLL_PAGE_SIZE) {
throw new UserInputError(`Large graphql query forbidden for offset + size > ${SCROLL_PAGE_SIZE},
throw new GraphQLError(`Large graphql query forbidden for offset + size > ${SCROLL_PAGE_SIZE},
offset = ${offset} and size = ${size},
please use download endpoint for large data queries instead.`);
please use download endpoint for large data queries instead.`, {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
const result = await this.filterData(
{ esInstance: this, esIndex, esType },
Expand Down
26 changes: 21 additions & 5 deletions src/server/es/sort.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UserInputError } from 'apollo-server';
import { GraphQLError } from 'graphql';

/**
* Transfer graphql sort arg to ES sort object
Expand Down Expand Up @@ -33,17 +33,29 @@ const getESSortBody = (graphqlSort, esInstance, esIndex) => {
// check fields and sort methods are valid
for (let i = 0; i < graphqlSortObj.length; i += 1) {
if (!graphqlSortObj[i] || Object.keys(graphqlSortObj[i]).length !== 1) {
throw new UserInputError('Invalid sort argument');
throw new GraphQLError('Invalid sort argument', {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
const field = Object.keys(graphqlSortObj[i])[0];
const method = graphqlSortObj[i][field];
if (method !== 'asc' && method !== 'desc') {
throw new UserInputError('Invalid sort argument');
throw new GraphQLError('Invalid sort argument', {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
if (!field.includes('.')) {
// non-nested field name, normal check logic
if (typeof esInstance.fieldTypes[esIndex][field] === 'undefined') {
throw new UserInputError('Invalid sort argument');
throw new GraphQLError('Invalid sort argument', {
extensions: {
code: 'BAD_USER_INPUT',
},
});
} else {
sortBody.push({
[field]: {
Expand All @@ -60,7 +72,11 @@ const getESSortBody = (graphqlSort, esInstance, esIndex) => {
if (fieldTypesToCheck && fieldTypesToCheck[FieldNameToCheck]) {
fieldTypesToCheck = fieldTypesToCheck[FieldNameToCheck].properties;
} else {
throw new UserInputError('Invalid sort argument');
throw new GraphQLError('Invalid sort argument', {
extensions: {
code: 'BAD_USER_INPUT',
},
});
}
}
// if we got here, everything looks good
Expand Down
Loading