Skip to content

Commit 0aaf531

Browse files
committed
wip: tested indexes
1 parent 2907315 commit 0aaf531

File tree

5 files changed

+155
-80
lines changed

5 files changed

+155
-80
lines changed

spec/DefinedSchemas.spec.js

Lines changed: 110 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
11
const { DefinedSchemas } = require('../lib/DefinedSchemas');
22
const Config = require('../lib/Config');
33

4-
fdescribe('DefinedSchemas', () => {
4+
const cleanUpIndexes = schema => {
5+
if (schema.indexes) {
6+
delete schema.indexes._id_;
7+
if (!Object.keys(schema.indexes).length) {
8+
delete schema.indexes;
9+
}
10+
}
11+
};
12+
13+
describe('DefinedSchemas', () => {
14+
let config;
515
beforeEach(async () => {
6-
const config = Config.get('test');
16+
config = Config.get('test');
17+
await config.database.adapter.deleteAllClasses();
18+
});
19+
afterAll(async () => {
720
await config.database.adapter.deleteAllClasses();
821
});
922

@@ -253,14 +266,102 @@ fdescribe('DefinedSchemas', () => {
253266
});
254267

255268
describe('Indexes', () => {
256-
xit('should create new indexes');
257-
xit('should re create changed indexes');
258-
xit('should delete removed indexes');
259-
describe('User', () => {
260-
xit('should protect default indexes');
269+
it('should create new indexes', async () => {
270+
const server = await reconfigureServer();
271+
272+
const indexes = { complex: { createdAt: 1, updatedAt: 1 } };
273+
274+
const schemas = [{ className: 'Test', indexes }];
275+
await new DefinedSchemas(schemas, server.config).execute();
276+
277+
let schema = await new Parse.Schema('Test').get();
278+
cleanUpIndexes(schema);
279+
expect(schema.indexes).toEqual(indexes);
280+
281+
await new DefinedSchemas(schemas, server.config).execute();
282+
schema = await new Parse.Schema('Test').get();
283+
cleanUpIndexes(schema);
284+
expect(schema.indexes).toEqual(indexes);
285+
});
286+
it('should re create changed indexes', async () => {
287+
const server = await reconfigureServer();
288+
289+
let indexes = { complex: { createdAt: 1, updatedAt: 1 } };
290+
291+
let schemas = [{ className: 'Test', indexes }];
292+
await new DefinedSchemas(schemas, server.config).execute();
293+
294+
indexes = { complex: { createdAt: 1 } };
295+
schemas = [{ className: 'Test', indexes }];
296+
297+
// Change indexes
298+
await new DefinedSchemas(schemas, server.config).execute();
299+
let schema = await new Parse.Schema('Test').get();
300+
cleanUpIndexes(schema);
301+
expect(schema.indexes).toEqual(indexes);
302+
303+
// Update
304+
await new DefinedSchemas(schemas, server.config).execute();
305+
schema = await new Parse.Schema('Test').get();
306+
cleanUpIndexes(schema);
307+
expect(schema.indexes).toEqual(indexes);
261308
});
262-
describe('Role', () => {
263-
xit('should protect default indexes');
309+
it('should delete removed indexes', async () => {
310+
const server = await reconfigureServer();
311+
312+
let indexes = { complex: { createdAt: 1, updatedAt: 1 } };
313+
314+
let schemas = [{ className: 'Test', indexes }];
315+
await new DefinedSchemas(schemas, server.config).execute();
316+
317+
indexes = {};
318+
schemas = [{ className: 'Test', indexes }];
319+
// Change indexes
320+
await new DefinedSchemas(schemas, server.config).execute();
321+
let schema = await new Parse.Schema('Test').get();
322+
cleanUpIndexes(schema);
323+
expect(schema.indexes).toBeUndefined();
324+
325+
// Update
326+
await new DefinedSchemas(schemas, server.config).execute();
327+
schema = await new Parse.Schema('Test').get();
328+
cleanUpIndexes(schema);
329+
expect(schema.indexes).toBeUndefined();
330+
});
331+
it('should keep protected indexes', async () => {
332+
const server = await reconfigureServer();
333+
334+
const schemas = [
335+
{
336+
className: '_User',
337+
indexes: {
338+
case_insensitive_username: { password: true },
339+
case_insensitive_email: { password: true },
340+
},
341+
},
342+
{ className: 'Test' },
343+
];
344+
// Create
345+
await new DefinedSchemas(schemas, server.config).execute();
346+
let userSchema = await new Parse.Schema('_User').get();
347+
let testSchema = await new Parse.Schema('Test').get();
348+
cleanUpIndexes(userSchema);
349+
cleanUpIndexes(testSchema);
350+
// If indexes are undefined it means that their
351+
// were not touched
352+
expect(testSchema.indexes).toBeUndefined();
353+
expect(userSchema.indexes).toBeUndefined();
354+
355+
// Update
356+
await new DefinedSchemas(schemas, server.config).execute();
357+
userSchema = await new Parse.Schema('_User').get();
358+
testSchema = await new Parse.Schema('Test').get();
359+
cleanUpIndexes(userSchema);
360+
cleanUpIndexes(testSchema);
361+
// If indexes are undefined it means that their
362+
// were not touched
363+
expect(testSchema.indexes).toBeUndefined();
364+
expect(userSchema.indexes).toBeUndefined();
264365
});
265366
});
266367

spec/schemas.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ describe('schemas', () => {
763763
});
764764
});
765765

766-
it('refuses to put to existing fields, even if it would not be a change', done => {
766+
it('refuses to put to existing fields with different type, even if it would not be a change', done => {
767767
const obj = hasAllPODobject();
768768
obj.save().then(() => {
769769
request({
@@ -773,7 +773,7 @@ describe('schemas', () => {
773773
json: true,
774774
body: {
775775
fields: {
776-
aString: { type: 'String' },
776+
aString: { type: 'Number' },
777777
},
778778
},
779779
}).then(fail, response => {

src/Controllers/SchemaController.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,12 @@ export default class SchemaController {
10671067
// object if the provided className-fieldName-type tuple is valid.
10681068
// The className must already be validated.
10691069
// If 'freeze' is true, refuse to update the schema for this field.
1070-
enforceFieldExists(className: string, fieldName: string, type: string | SchemaField) {
1070+
enforceFieldExists(
1071+
className: string,
1072+
fieldName: string,
1073+
type: string | SchemaField,
1074+
isValidation?: boolean
1075+
) {
10711076
if (fieldName.indexOf('.') > 0) {
10721077
// subdocument key (x.y) => ok if x is of type 'object'
10731078
fieldName = fieldName.split('.')[0];
@@ -1113,7 +1118,7 @@ export default class SchemaController {
11131118
}
11141119
// If type options do not change
11151120
// we can safely return
1116-
if (_.isEqual(expectedType, type)) {
1121+
if (isValidation || _.isEqual(expectedType, type)) {
11171122
return undefined;
11181123
} else {
11191124
// Field options are may be changed
@@ -1252,7 +1257,7 @@ export default class SchemaController {
12521257
// Every object has ACL implicitly.
12531258
continue;
12541259
}
1255-
promises.push(schema.enforceFieldExists(className, fieldName, expected));
1260+
promises.push(schema.enforceFieldExists(className, fieldName, expected, true));
12561261
}
12571262
const results = await Promise.all(promises);
12581263
const enforceFields = results.filter(result => !!result);

src/DefinedSchemas.js

Lines changed: 34 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,11 @@ export class DefinedSchemas {
108108
}
109109
// Handle indexes
110110
if (localSchema.indexes) {
111-
Object.keys(localSchema.indexes).forEach(indexName =>
112-
newLocalSchema.addIndex(indexName, localSchema.indexes[indexName])
113-
);
111+
Object.keys(localSchema.indexes).forEach(indexName => {
112+
if (!this.isProtectedIndex(localSchema.className, indexName)) {
113+
newLocalSchema.addIndex(indexName, localSchema.indexes[indexName]);
114+
}
115+
});
114116
}
115117

116118
this.handleCLP(localSchema, newLocalSchema);
@@ -184,37 +186,45 @@ export class DefinedSchemas {
184186

185187
// Handle Indexes
186188
// Check addition
187-
const cloudIndexes = this.convertCloudIndexes(cloudSchema.indexes);
188-
189189
if (localSchema.indexes) {
190190
Object.keys(localSchema.indexes).forEach(indexName => {
191-
if (!cloudIndexes[indexName] && !this.isNativeIndex(localSchema.className, indexName))
191+
if (
192+
(!cloudSchema.indexes || !cloudSchema.indexes[indexName]) &&
193+
!this.isProtectedIndex(localSchema.className, indexName)
194+
)
192195
newLocalSchema.addIndex(indexName, localSchema.indexes[indexName]);
193196
});
194197
}
195198

196199
const indexesToAdd = [];
197200

198201
// Check deletion
199-
Object.keys(cloudIndexes).forEach(async indexName => {
200-
if (!this.isNativeIndex(localSchema.className, indexName)) {
201-
if (!localSchema.indexes[indexName]) {
202-
newLocalSchema.deleteIndex(indexName);
203-
} else if (!this.paramsAreEquals(localSchema.indexes[indexName], cloudIndexes[indexName])) {
204-
newLocalSchema.deleteIndex(indexName);
205-
indexesToAdd.push({
206-
indexName,
207-
index: localSchema.indexes[indexName],
208-
});
202+
if (cloudSchema.indexes) {
203+
Object.keys(cloudSchema.indexes).forEach(async indexName => {
204+
if (!this.isProtectedIndex(localSchema.className, indexName)) {
205+
if (!localSchema.indexes[indexName]) {
206+
newLocalSchema.deleteIndex(indexName);
207+
} else if (
208+
!this.paramsAreEquals(localSchema.indexes[indexName], cloudSchema.indexes[indexName])
209+
) {
210+
newLocalSchema.deleteIndex(indexName);
211+
indexesToAdd.push({
212+
indexName,
213+
index: localSchema.indexes[indexName],
214+
});
215+
}
209216
}
210-
}
211-
});
217+
});
218+
}
212219

213220
this.handleCLP(localSchema, newLocalSchema, cloudSchema);
221+
// Apply changes
222+
await this.updateSchemaToDB(newLocalSchema);
223+
// Apply new/changed indexes
214224
if (indexesToAdd.length) {
215225
indexesToAdd.forEach(o => newLocalSchema.addIndex(o.indexName, o.index));
226+
await this.updateSchemaToDB(newLocalSchema);
216227
}
217-
await this.updateSchemaToDB(newLocalSchema);
218228
}
219229

220230
handleCLP(localSchema, newLocalSchema, cloudSchema) {
@@ -235,61 +245,20 @@ export class DefinedSchemas {
235245
newLocalSchema.setCLP(clp);
236246
}
237247

238-
isProtectedSchema(className) {
239-
return (
240-
[
241-
'_Session',
242-
'_PushStatus',
243-
'_Installation',
244-
'_JobStatus',
245-
'_PushStatus',
246-
'_Hooks',
247-
'_GlobalConfig',
248-
'_JobSchedule',
249-
'_Idempotency',
250-
].indexOf(className) !== -1
251-
);
252-
}
253-
254248
isProtectedFields(className, fieldName) {
255249
return (
256250
!!defaultColumns._Default[fieldName] ||
257251
!!(defaultColumns[className] && defaultColumns[className][fieldName])
258252
);
259253
}
260254

261-
convertCloudIndexes(cloudSchemaIndexes) {
262-
if (!cloudSchemaIndexes) return {};
263-
// eslint-disable-next-line no-unused-vars
264-
const { _id_, ...others } = cloudSchemaIndexes;
265-
266-
return {
267-
objectId: { objectId: 1 },
268-
...others,
269-
};
270-
}
271-
272-
isNativeIndex(className, indexName) {
255+
isProtectedIndex(className, indexName) {
256+
let indexes = ['_id_'];
273257
if (className === '_User') {
274-
switch (indexName) {
275-
case 'case_insensitive_username':
276-
return true;
277-
case 'case_insensitive_email':
278-
return true;
279-
case 'username_1':
280-
return true;
281-
case 'objectId':
282-
return true;
283-
case 'email_1':
284-
return true;
285-
default:
286-
break;
287-
}
258+
indexes = [...indexes, 'case_insensitive_username', 'case_insensitive_email'];
288259
}
289-
if (className === '_Role') {
290-
return true;
291-
}
292-
return false;
260+
261+
return indexes.indexOf(indexName) !== -1;
293262
}
294263

295264
paramsAreEquals(indexA, indexB) {

src/Routers/SchemasRouter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ function getOneSchema(req) {
3636
}
3737

3838
const checkIfDefinedSchemasIsUsed = req => {
39-
if (req.config.schemas) {
39+
if (req.config && req.config.schemas) {
4040
throw new Parse.Error(
4141
Parse.Error.OPERATION_FORBIDDEN,
4242
'cannot perform this operation when schemas options is used.'

0 commit comments

Comments
 (0)