Files
kami-parse-server/src/GraphQL/loaders/parseClassMutations.js
Omair Vaiyani b70915098f fix(GraphQL): Mutations not returning updated information (#6130)
This issue was spotted when an updated field is modified in beforeSave, but the unmodified version is returned if requested by the resolver.

For example
```graphql
mutation UpdateTitle($id: ID!, $title: String!) {
  updateSomeObject(id: $id, fields: { title: $title }) {
      id
      title
      slug
  }
}
```

In the above, if we modify the `title` by let's say, trimming it - the resolved `title` will not reflect this change, and instead just return the input variable. Other resolved fields that are not sent within the `fields` input are returned properly using the latest data.
2019-10-14 11:09:10 -03:00

242 lines
7.2 KiB
JavaScript

import { GraphQLNonNull } from 'graphql';
import getFieldNames from 'graphql-list-fields';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import {
extractKeysAndInclude,
getParseClassMutationConfig,
} from '../parseGraphQLUtils';
import * as objectsMutations from '../helpers/objectsMutations';
import * as objectsQueries from '../helpers/objectsQueries';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
import { transformClassNameToGraphQL } from '../transformers/className';
import { transformTypes } from '../transformers/mutation';
const getOnlyRequiredFields = (
updatedFields,
selectedFieldsString,
includedFieldsString,
nativeObjectFields
) => {
const includedFields = includedFieldsString.split(',');
const selectedFields = selectedFieldsString.split(',');
const missingFields = selectedFields
.filter(
field =>
!nativeObjectFields.includes(field) ||
includedFields.includes(field)
)
.join(',');
if (!missingFields.length) {
return { needGet: false, keys: '' };
} else {
return { needGet: true, keys: missingFields };
}
};
const load = function(
parseGraphQLSchema,
parseClass,
parseClassConfig: ?ParseGraphQLClassConfig
) {
const className = parseClass.className;
const graphQLClassName = transformClassNameToGraphQL(className);
const {
create: isCreateEnabled = true,
update: isUpdateEnabled = true,
destroy: isDestroyEnabled = true,
} = getParseClassMutationConfig(parseClassConfig);
const {
classGraphQLCreateType,
classGraphQLUpdateType,
classGraphQLOutputType,
} = parseGraphQLSchema.parseClassTypes[className];
if (isCreateEnabled) {
const createGraphQLMutationName = `create${graphQLClassName}`;
parseGraphQLSchema.addGraphQLMutation(createGraphQLMutationName, {
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${graphQLClassName} class.`,
args: {
fields: {
description: 'These are the fields used to create the object.',
type: classGraphQLCreateType || defaultGraphQLTypes.OBJECT,
},
},
type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, mutationInfo) {
try {
let { fields } = args;
if (!fields) fields = {};
const { config, auth, info } = context;
const parseFields = await transformTypes('create', fields, {
className,
parseGraphQLSchema,
req: { config, auth, info },
});
const createdObject = await objectsMutations.createObject(
className,
parseFields,
config,
auth,
info
);
const selectedFields = getFieldNames(mutationInfo);
const { keys, include } = extractKeysAndInclude(selectedFields);
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(
fields,
keys,
include,
['id', 'createdAt', 'updatedAt']
);
let optimizedObject = {};
if (needGet) {
optimizedObject = await objectsQueries.getObject(
className,
createdObject.objectId,
requiredKeys,
include,
undefined,
undefined,
config,
auth,
info
);
}
return {
...createdObject,
updatedAt: createdObject.createdAt,
...parseFields,
...optimizedObject,
};
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
});
}
if (isUpdateEnabled) {
const updateGraphQLMutationName = `update${graphQLClassName}`;
parseGraphQLSchema.addGraphQLMutation(updateGraphQLMutationName, {
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`,
args: {
id: defaultGraphQLTypes.OBJECT_ID_ATT,
fields: {
description: 'These are the fields used to update the object.',
type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT,
},
},
type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, mutationInfo) {
try {
const { id, fields } = args;
const { config, auth, info } = context;
const parseFields = await transformTypes('update', fields, {
className,
parseGraphQLSchema,
req: { config, auth, info },
});
const updatedObject = await objectsMutations.updateObject(
className,
id,
parseFields,
config,
auth,
info
);
const selectedFields = getFieldNames(mutationInfo);
const { keys, include } = extractKeysAndInclude(selectedFields);
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(
fields,
keys,
include,
['id', 'updatedAt']
);
let optimizedObject = {};
if (needGet) {
optimizedObject = await objectsQueries.getObject(
className,
id,
requiredKeys,
include,
undefined,
undefined,
config,
auth,
info
);
}
return {
id,
...updatedObject,
...parseFields,
...optimizedObject,
};
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
});
}
if (isDestroyEnabled) {
const deleteGraphQLMutationName = `delete${graphQLClassName}`;
parseGraphQLSchema.addGraphQLMutation(deleteGraphQLMutationName, {
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`,
args: {
id: defaultGraphQLTypes.OBJECT_ID_ATT,
},
type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, mutationInfo) {
try {
const { id } = args;
const { config, auth, info } = context;
const selectedFields = getFieldNames(mutationInfo);
const { keys, include } = extractKeysAndInclude(selectedFields);
let optimizedObject = {};
const splitedKeys = keys.split(',');
if (splitedKeys.length > 1 || splitedKeys[0] !== 'id') {
optimizedObject = await objectsQueries.getObject(
className,
id,
keys,
include,
undefined,
undefined,
config,
auth,
info
);
}
await objectsMutations.deleteObject(
className,
id,
config,
auth,
info
);
return { id, ...optimizedObject };
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
});
}
};
export { load };