Skip to content

Commit 84574b7

Browse files
committed
fix(updateById): pull out _id arg from record. It was an old schema design error.
BREAKING CHANGE: resolver `updateById` changes its arguments. Before was `updateById(record: { _id: 1, name: 'New' })`, now became `updateById(_id: 1, record: { name: 'New' })`
1 parent d3ce99e commit 84574b7

File tree

2 files changed

+31
-39
lines changed

2 files changed

+31
-39
lines changed

src/resolvers/__tests__/updateById-test.ts

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
/* eslint-disable no-param-reassign */
22

33
import { Resolver, schemaComposer, ObjectTypeComposer } from 'graphql-compose';
4-
import {
5-
GraphQLNonNull,
6-
GraphQLInputObjectType,
7-
getNullableType,
8-
} from 'graphql-compose/lib/graphql';
4+
import { GraphQLNonNull } from 'graphql-compose/lib/graphql';
95
import { UserModel, IUser } from '../../__mocks__/userModel';
106
import updateById from '../updateById';
117
import GraphQLMongoID from '../../types/MongoID';
@@ -63,17 +59,9 @@ describe('updateById() ->', () => {
6359
expect(argConfig.type.ofType.name).toBe('UpdateByIdUserInput');
6460
});
6561

66-
it('should have `record._id` required arg', () => {
62+
it('should have `_id` required arg', () => {
6763
const resolver = updateById(UserModel, UserTC);
68-
const argConfig: any = resolver.getArgConfig('record') || {};
69-
expect(argConfig.type.ofType).toBeInstanceOf(GraphQLInputObjectType);
70-
if (argConfig.type && argConfig.type.ofType) {
71-
const _idFieldType = schemaComposer
72-
.createInputTC(argConfig.type.ofType)
73-
.getFieldType('_id');
74-
expect(_idFieldType).toBeInstanceOf(GraphQLNonNull);
75-
expect(getNullableType(_idFieldType)).toBe(GraphQLMongoID);
76-
}
64+
expect(resolver.getArgTypeName('_id')).toBe('MongoID!');
7765
});
7866
});
7967

@@ -84,19 +72,18 @@ describe('updateById() ->', () => {
8472
result.catch(() => 'catch error if appear, hide it from mocha');
8573
});
8674

87-
it('should rejected with Error if args.record._id is empty', async () => {
75+
it('should rejected with Error if args._id is empty', async () => {
8876
const result = updateById(UserModel, UserTC).resolve({
8977
args: { record: {} },
9078
});
91-
await expect(result).rejects.toThrow(
92-
'User.updateById resolver requires args.record._id value'
93-
);
79+
await expect(result).rejects.toThrow('User.updateById resolver requires args._id value');
9480
});
9581

9682
it('should return payload.recordId', async () => {
9783
const result = await updateById(UserModel, UserTC).resolve({
9884
args: {
99-
record: { _id: user1.id, name: 'some name' },
85+
_id: user1.id,
86+
record: { name: 'some name' },
10087
},
10188
});
10289
expect(result.recordId).toBe(user1.id);
@@ -117,7 +104,8 @@ describe('updateById() ->', () => {
117104
it('should return empty payload.error', async () => {
118105
const result = await updateById(UserModel, UserTC).resolve({
119106
args: {
120-
record: { _id: user1.id, name: 'some name' },
107+
_id: user1.id,
108+
record: { name: 'some name' },
121109
},
122110
});
123111
expect(result.error).toEqual(undefined);
@@ -126,7 +114,8 @@ describe('updateById() ->', () => {
126114
it('should return payload.error', async () => {
127115
const result = await updateById(UserModel, UserTC).resolve({
128116
args: {
129-
record: { _id: user1.id, name: 'some name', valid: 'AlwaysFails' },
117+
_id: user1.id,
118+
record: { name: 'some name', valid: 'AlwaysFails' },
130119
},
131120
projection: {
132121
error: true,
@@ -149,7 +138,8 @@ describe('updateById() ->', () => {
149138
await expect(
150139
updateById(UserModel, UserTC).resolve({
151140
args: {
152-
record: { _id: user1.id, name: 'some name', valid: 'AlwaysFails' },
141+
_id: user1.id,
142+
record: { name: 'some name', valid: 'AlwaysFails' },
153143
},
154144
})
155145
).rejects.toThrowError('User validation failed: valid: this is a validate message');
@@ -158,7 +148,8 @@ describe('updateById() ->', () => {
158148
it('should change data via args.record in model', async () => {
159149
const result = await updateById(UserModel, UserTC).resolve({
160150
args: {
161-
record: { _id: user1.id, name: 'newName' },
151+
_id: user1.id,
152+
record: { name: 'newName' },
162153
},
163154
});
164155
expect(result.record.name).toBe('newName');
@@ -168,7 +159,8 @@ describe('updateById() ->', () => {
168159
const checkedName = 'nameForMongoDB';
169160
await updateById(UserModel, UserTC).resolve({
170161
args: {
171-
record: { _id: user1.id, name: checkedName },
162+
_id: user1.id,
163+
record: { name: checkedName },
172164
},
173165
});
174166

@@ -181,7 +173,8 @@ describe('updateById() ->', () => {
181173
const checkedName = 'anyName123';
182174
const result = await updateById(UserModel, UserTC).resolve({
183175
args: {
184-
record: { _id: user1.id, name: checkedName },
176+
_id: user1.id,
177+
record: { name: checkedName },
185178
},
186179
});
187180
expect(result.record.id).toBe(user1.id);
@@ -191,9 +184,8 @@ describe('updateById() ->', () => {
191184
it('should pass empty projection to findById and got full document data', async () => {
192185
const result = await updateById(UserModel, UserTC).resolve({
193186
args: {
194-
record: {
195-
_id: user1.id,
196-
},
187+
_id: user1.id,
188+
record: {},
197189
},
198190
projection: {
199191
record: {
@@ -208,15 +200,15 @@ describe('updateById() ->', () => {
208200

209201
it('should return mongoose document', async () => {
210202
const result = await updateById(UserModel, UserTC).resolve({
211-
args: { record: { _id: user1.id } },
203+
args: { _id: user1.id, record: {} },
212204
});
213205
expect(result.record).toBeInstanceOf(UserModel);
214206
});
215207

216208
it('should call `beforeRecordMutate` method with founded `record` and `resolveParams` as args', async () => {
217209
let beforeMutationId;
218210
const result = await updateById(UserModel, UserTC).resolve({
219-
args: { record: { _id: user1.id } },
211+
args: { _id: user1.id, record: {} },
220212
context: { ip: '1.1.1.1' },
221213
beforeRecordMutate: (record: any, rp: ExtendedResolveParams) => {
222214
beforeMutationId = record.id;
@@ -231,7 +223,7 @@ describe('updateById() ->', () => {
231223

232224
it('`beforeRecordMutate` may reject operation', async () => {
233225
const result = updateById(UserModel, UserTC).resolve({
234-
args: { record: { _id: user1.id, name: 'new name' } },
226+
args: { _id: user1.id, record: { name: 'new name' } },
235227
context: { readOnly: true },
236228
beforeRecordMutate: (record: any, rp: ExtendedResolveParams) => {
237229
if (rp.context.readOnly) {
@@ -249,7 +241,7 @@ describe('updateById() ->', () => {
249241
let beforeQueryCalled = false;
250242

251243
const result = await updateById(UserModel, UserTC).resolve({
252-
args: { record: { _id: user1.id, name: 'new name' } },
244+
args: { _id: user1.id, record: { name: 'new name' } },
253245
beforeQuery: (query: any, rp: ExtendedResolveParams) => {
254246
expect(query).toHaveProperty('exec');
255247
expect(rp.model).toBe(UserModel);
@@ -295,7 +287,7 @@ describe('updateById() ->', () => {
295287

296288
it('should have all fields optional in record', () => {
297289
const resolver = updateById(UserModel, UserTC);
298-
expect(resolver.getArgITC('record').getFieldTypeName('_id')).toBe('MongoID!');
290+
expect(resolver.getArgTypeName('_id')).toBe('MongoID!');
299291
expect(resolver.getArgITC('record').getFieldTypeName('name')).toBe('String');
300292
expect(resolver.getArgITC('record').getFieldTypeName('age')).toBe('Float');
301293
});

src/resolvers/updateById.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ export default function updateById<TSource = Document, TContext = any>(
4848
'4) And save it.',
4949
type: outputType,
5050
args: {
51+
_id: 'MongoID!',
5152
...recordHelperArgs(tc, {
53+
removeFields: ['_id'], // pull out `_id` to top-level
5254
prefix: 'UpdateById',
5355
suffix: 'Input',
54-
requiredFields: ['_id'],
5556
isRequired: true,
5657
allFieldsNullable: true,
5758
...(opts && opts.record),
@@ -66,13 +67,12 @@ export default function updateById<TSource = Document, TContext = any>(
6667
);
6768
}
6869

69-
if (!recordData._id) {
70+
if (!resolveParams?.args?._id) {
7071
return Promise.reject(
71-
new Error(`${tc.getTypeName()}.updateById resolver requires args.record._id value`)
72+
new Error(`${tc.getTypeName()}.updateById resolver requires args._id value`)
7273
);
7374
}
7475

75-
resolveParams.args._id = recordData._id;
7676
delete recordData._id;
7777

7878
// We should get all data for document, cause Mongoose model may have hooks/middlewares
@@ -109,5 +109,5 @@ export default function updateById<TSource = Document, TContext = any>(
109109
// and return it in mutation payload
110110
addErrorCatcherField(resolver);
111111

112-
return resolver;
112+
return resolver as any;
113113
}

0 commit comments

Comments
 (0)