Skip to content

Commit

Permalink
avniproject/avni-client#1142 - check for mandatory object fields to b…
Browse files Browse the repository at this point in the history
…e not null during create and update.
  • Loading branch information
petmongrels committed Oct 26, 2023
1 parent d518fe7 commit 8d70e06
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/ReportCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ReportCard extends BaseEntity {
name: "string",
query: {type: "string", optional: true},
description: {type: "string", optional: true},
standardReportCardType: {type: "StandardReportCardType", option: true},
standardReportCardType: {type: "StandardReportCardType", optional: true},
colour: "string",
voided: {type: "bool", default: false},
},
Expand Down
34 changes: 22 additions & 12 deletions src/Schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import UserSubjectAssignment from "./assignment/UserSubjectAssignment";
import SchemaNames from "./SchemaNames";
import DashboardFilter from "./reports/DashboardFilter";
import CustomDashboardCache from './CustomDashboardCache';
import DefinedObjectSchema from "./framework/DefinedObjectSchema";

const entities = [
DashboardFilter,
Expand Down Expand Up @@ -693,39 +694,39 @@ function createRealmConfig() {

for (let i = 0; i < oldObjects.length; i++) {
let entityApprovalStatus = oldObjects[i];
if(oldObjects[i].entityType === 'Subject') {
if (oldObjects[i].entityType === 'Subject') {
const subject = oldDB
.objects(Individual.schema.name)
.filtered("uuid = $0", entityApprovalStatus.entityUUID)[0];
if(subject) {
if (subject) {
newObjects[i].entityTypeUuid = subject.subjectType.uuid;
}
} else if(oldObjects[i].entityType === 'ProgramEnrolment') {
} else if (oldObjects[i].entityType === 'ProgramEnrolment') {
const programEnrolment = oldDB
.objects(ProgramEnrolment.schema.name)
.filtered("uuid = $0", entityApprovalStatus.entityUUID)[0];
if(programEnrolment) {
if (programEnrolment) {
newObjects[i].entityTypeUuid = programEnrolment.program.uuid;
}
} else if(oldObjects[i].entityType === 'ChecklistItem') {
} else if (oldObjects[i].entityType === 'ChecklistItem') {
const checklistItem = oldDB
.objects(ChecklistItem.schema.name)
.filtered("uuid = $0", entityApprovalStatus.entityUUID)[0];
if(checklistItem) {
if (checklistItem) {
newObjects[i].entityTypeUuid = checklistItem.checklist.programEnrolment.program.uuid;
}
} else if(oldObjects[i].entityType === 'Encounter') {
} else if (oldObjects[i].entityType === 'Encounter') {
const encounter = oldDB
.objects(Encounter.schema.name)
.filtered("uuid = $0", entityApprovalStatus.entityUUID)[0];
if(encounter) {
if (encounter) {
newObjects[i].entityTypeUuid = encounter.encounterType.uuid;
}
} else if(oldObjects[i].entityType === 'ProgramEncounter') {
} else if (oldObjects[i].entityType === 'ProgramEncounter') {
const programEncounter = oldDB
.objects(ProgramEncounter.schema.name)
.filtered("uuid = $0", entityApprovalStatus.entityUUID)[0];
if(programEncounter) {
if (programEncounter) {
newObjects[i].entityTypeUuid = programEncounter.encounterType.uuid;
}
}
Expand All @@ -752,7 +753,7 @@ function createRealmConfig() {
}
});
const entityApprovalStatusSyncStatus = newDB.objects(EntitySyncStatus.schema.name).filtered("entityName = $0", "EntityApprovalStatus");
if(entityApprovalStatusSyncStatus[0]) {
if (entityApprovalStatusSyncStatus[0]) {
newDB.delete(entityApprovalStatusSyncStatus);
}
}
Expand Down Expand Up @@ -811,12 +812,17 @@ class EntityMappingConfig {
this.realmConfig = createRealmConfig();
this.realmConfig.schema = [];
this.schemaEntityMap = new Map();
this.mandatoryObjectSchemaProperties = new Map();
entities.forEach((entity) => {
if(_.isNil(this.schemaEntityMap.get(entity.schema.name))) {
if (_.isNil(this.schemaEntityMap.get(entity.schema.name))) {
this.realmConfig.schema.push(entity.schema);
this.schemaEntityMap.set(entity.schema.name, entity);
}
});
this.realmConfig.schema.forEach((x) => {
const nonOptionalObjectProperties = DefinedObjectSchema.getNonOptionalObjectProperties(x);
this.mandatoryObjectSchemaProperties.set(x.name, nonOptionalObjectProperties);
}, []);
}

getEntityClass(schemaName) {
Expand All @@ -834,6 +840,10 @@ class EntityMappingConfig {
getSchemaVersion() {
return this.getRealmConfig().schemaVersion;
}

getMandatoryObjectSchemaProperties(schemaName) {
return this.mandatoryObjectSchemaProperties.get(schemaName);
}
}

export default EntityMappingConfig;
2 changes: 1 addition & 1 deletion src/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Settings extends BaseEntity {
properties: {
uuid: "string",
serverURL: "string",
locale: {type: "LocaleMapping"},
locale: {type: "LocaleMapping", optional: true},
logLevel: "int",
pageSize: "int",
poolId: "string",
Expand Down
20 changes: 20 additions & 0 deletions src/framework/DefinedObjectSchema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import _ from "lodash";

// https://www.mongodb.com/docs/realm/sdk/react-native/model-data/data-types/property-types/#std-label-react-native-supported-property-types
const inBuiltPropertyTypes = ["bool", "int", "float", "double", "string", "decimal128", "objectId", "data", "date", "list", "linkingObjects", "dictionary", "set", "mixed", "uuid"];

class DefinedObjectSchema {
properties;

static getNonOptionalObjectProperties(definedSchema) {
return Object.keys(definedSchema.properties).filter((key) => {
const value = definedSchema.properties[key];
const isSimpleDefinition = typeof value === 'string';
const type = isSimpleDefinition ? value : value["type"];
const contains = _.some(inBuiltPropertyTypes, (x) => type.replace("?", "") === x);
return !contains && (isSimpleDefinition ? true : !value["optional"]);
});
}
}

export default DefinedObjectSchema;
22 changes: 18 additions & 4 deletions src/framework/RealmProxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,23 @@ class RealmProxy {
return this.realmDb.close();
}

create(type, properties, updateMode = "never") {
const createProperties = _.isNil(properties.that) ? properties : properties.that;
const entityClass = this.entityMappingConfig.getEntityClass(type);
const dbEntity = this.realmDb.create(type, createProperties, updateMode);
create(schemaName, properties, updateMode = "never") {
const underlyingObject = _.isNil(properties.that) ? properties : properties.that;
const entityClass = this.entityMappingConfig.getEntityClass(schemaName);
const mandatoryObjectSchemaProperties = this.entityMappingConfig.getMandatoryObjectSchemaProperties(schemaName);
const emptyMandatoryProperties = [];

const saveObjectKeys = Object.keys(underlyingObject);
if (updateMode === "never" || updateMode === false || _.intersection(mandatoryObjectSchemaProperties, saveObjectKeys.length > 0)) {

This comment has been minimized.

Copy link
@ashusvnath

ashusvnath Oct 30, 2023

Looks like the .length is fetched on the wrong object

This comment has been minimized.

Copy link
@petmongrels

petmongrels Oct 30, 2023

Author Contributor

yes..will fix

saveObjectKeys.forEach((x) => {
const propertyValue = underlyingObject[x];
if (_.isNil(propertyValue) && _.some(mandatoryObjectSchemaProperties, (y) => y === x)) emptyMandatoryProperties.push(x.propertyName);
});
if (emptyMandatoryProperties.length > 0) {
throw new Error(`${emptyMandatoryProperties.join(",")} are mandatory for ${schemaName}, ${saveObjectKeys}`);
}
}
const dbEntity = this.realmDb.create(schemaName, underlyingObject, updateMode);
return new entityClass(dbEntity);
}

Expand All @@ -65,6 +78,7 @@ class RealmProxy {
const entityClass = this.entityMappingConfig.getEntityClass(type);
return new entityClass(this.realmDb.objectForPrimaryKey(type, key));
}

write(callback) {
return this.realmDb.write(callback);
}
Expand Down
2 changes: 2 additions & 0 deletions src/framework/RealmResultsProxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class RealmResultsProxy {
}

getAt(index) {
if (this.realmCollection.length <= index) return null;

const realmCollectionElement = this.realmCollection[index];
if (_.isNil(realmCollectionElement)) return null;
return this.createEntity(realmCollectionElement);
Expand Down
29 changes: 29 additions & 0 deletions test/DefinedObjectSchemaTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import DefinedObjectSchema from "../src/framework/DefinedObjectSchema";
import {assert} from 'chai';

const schema = {
name: "Foo",
primaryKey: "uuid",
properties: {
uuid: "string",
subjectType: "SubjectType",
name: "string",
age: "float",
wage: "float?",
middleName: {type: "string", optional: true},
profilePicture: {type: "string", optional: false},
dateOfBirth: {type: "date", optional: true},
gender: {type: "Gender", optional: true},
registrationDate: "date",
lowestAddressLevel: {type: "AddressLevel", optional: false},
enrolments: {type: "list", objectType: "Bar"},
registrationLocation: {type: "Point", optional: true}
}
};

it('should getNonOptionalObjectProperties', function () {
const nonOptionalObjectProperties = DefinedObjectSchema.getNonOptionalObjectProperties(schema);
assert.equal(nonOptionalObjectProperties.length, 2);
assert.equal(nonOptionalObjectProperties[0], "subjectType");
assert.equal(nonOptionalObjectProperties[1], "lowestAddressLevel");
});
6 changes: 6 additions & 0 deletions test/EntityMappingConfigTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {EntityMappingConfig} from "../src";

This comment has been minimized.

Copy link
@ashusvnath

ashusvnath Oct 30, 2023

File contains only 1 skipped test. Is this test needed ?

This comment has been minimized.

Copy link
@petmongrels

petmongrels Oct 30, 2023

Author Contributor

I used this to create a report for what is optional. will remove after a few releases.


xit('should get all non optional object properties', function () {
let instance = EntityMappingConfig.getInstance();
console.log(instance.mandatoryObjectSchemaProperties.map((x) => `${x.definedSchema.name} --> ${x.propertyName}`));
});

0 comments on commit 8d70e06

Please sign in to comment.