Skip to content

Commit

Permalink
fix: throw validation error when updating models with misconfigured h… (
Browse files Browse the repository at this point in the history
#886)

* fix: throw validation error when updating models with misconfigured hasMany relationship

* fix: edit error message for updating hasMany parent

* fix: extract primary key from model def
  • Loading branch information
bombguy committed Jan 19, 2023
1 parent 0b50f29 commit ac80c9c
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export default function SchoolUpdateForm(props) {

const [schoolRecord, setSchoolRecord] = React.useState(school);
const [linkedStudents, setLinkedStudents] = React.useState([]);
const canUnlinkStudents = false;

React.useEffect(() => {
const queryData = async () => {
Expand Down Expand Up @@ -252,6 +253,9 @@ export default function SchoolUpdateForm(props) {
const studentsToUnLink = [];
const studentsSet = new Set();
const linkedStudentsSet = new Set();
if (!canUnlinkStudents && studentsToUnLink.length > 0) {
throw Error(`${original.id} cannot be unlinked from School because schoolID is a required field.`);
}
Students.forEach((r) => studentsSet.add(r.id));
linkedStudents.forEach((r) => linkedStudentsSet.add(r.id));

Expand All @@ -269,6 +273,11 @@ export default function SchoolUpdateForm(props) {

const promises = [];
studentsToUnLink.forEach((original) => {
if (!canUnlinkStudents) {
throw Error(
`Student ${original.id} cannot be unlinked from School because schoolID is a required field.`,
);
}
promises.push(
DataStore.save(
Student.copyOf(original, (updated) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4078,7 +4078,9 @@ export default function UpdateCompositeDogForm(props) {
const [compositeDogRecord, setCompositeDogRecord] =
React.useState(compositeDog);
const [linkedCompositeToys, setLinkedCompositeToys] = React.useState([]);
const canUnlinkCompositeToys = true;
const [linkedCompositeVets, setLinkedCompositeVets] = React.useState([]);
const canUnlinkCompositeVets = false;
React.useEffect(() => {
const queryData = async () => {
const record = nameProp
Expand Down Expand Up @@ -4329,27 +4331,19 @@ export default function UpdateCompositeDogForm(props) {
}
});
compositeToysToUnLink.forEach((original) => {
try {
promises.push(
DataStore.save(
CompositeToy.copyOf(original, (updated) => {
updated.compositeDogCompositeToysName = null;
updated.compositeDogCompositeToysDescription = null;
})
)
if (!canUnlinkCompositeToys) {
throw Error(
\`CompositeToy \${original.kind} cannot be unlinked from CompositeDog because compositeDogCompositeToysName is a required field.\`
);
} catch (err) {
if (
err.message ===
\\"Field compositeDogCompositeToysName is required\\"
) {
throw Error(
\`\${original.id} cannot be unlinked from CompositeDog because compositeDogCompositeToysName is a required field.\`
);
} else {
throw err;
}
}
promises.push(
DataStore.save(
CompositeToy.copyOf(original, (updated) => {
updated.compositeDogCompositeToysName = null;
updated.compositeDogCompositeToysDescription = null;
})
)
);
});
compositeToysToLink.forEach((original) => {
promises.push(
Expand Down Expand Up @@ -5164,7 +5158,9 @@ export default function UpdateCPKTeacherForm(props) {
};
const [cPKTeacherRecord, setCPKTeacherRecord] = React.useState(cPKTeacher);
const [linkedCPKClasses, setLinkedCPKClasses] = React.useState([]);
const canUnlinkCPKClasses = false;
const [linkedCPKProjects, setLinkedCPKProjects] = React.useState([]);
const canUnlinkCPKProjects = true;
React.useEffect(() => {
const queryData = async () => {
const record = specialTeacherIdProp
Expand Down Expand Up @@ -5414,23 +5410,18 @@ export default function UpdateCPKTeacherForm(props) {
}
});
cPKProjectsToUnLink.forEach((original) => {
try {
promises.push(
DataStore.save(
CPKProject.copyOf(original, (updated) => {
updated.cPKTeacherID = null;
})
)
if (!canUnlinkCPKProjects) {
throw Error(
\`CPKProject \${original.specialProjectId} cannot be unlinked from CPKTeacher because cPKTeacherID is a required field.\`
);
} catch (err) {
if (err.message === \\"Field cPKTeacherID is required\\") {
throw Error(
\`\${original.id} cannot be unlinked from CPKTeacher because cPKTeacherID is a required field.\`
);
} else {
throw err;
}
}
promises.push(
DataStore.save(
CPKProject.copyOf(original, (updated) => {
updated.cPKTeacherID = null;
})
)
);
});
cPKProjectsToLink.forEach((original) => {
promises.push(
Expand Down Expand Up @@ -11992,6 +11983,7 @@ export default function SchoolUpdateForm(props) {
};
const [schoolRecord, setSchoolRecord] = React.useState(school);
const [linkedStudents, setLinkedStudents] = React.useState([]);
const canUnlinkStudents = false;
React.useEffect(() => {
const queryData = async () => {
const record = idProp ? await DataStore.query(School, idProp) : school;
Expand Down Expand Up @@ -12110,23 +12102,18 @@ export default function SchoolUpdateForm(props) {
}
});
studentsToUnLink.forEach((original) => {
try {
promises.push(
DataStore.save(
Student.copyOf(original, (updated) => {
updated.schoolID = null;
})
)
if (!canUnlinkStudents) {
throw Error(
\`Student \${original.id} cannot be unlinked from School because schoolID is a required field.\`
);
} catch (err) {
if (err.message === \\"Field schoolID is required\\") {
throw Error(
\`\${original.id} cannot be unlinked from School because schoolID is a required field.\`
);
} else {
throw err;
}
}
promises.push(
DataStore.save(
Student.copyOf(original, (updated) => {
updated.schoolID = null;
})
)
);
});
studentsToLink.forEach((original) => {
promises.push(
Expand Down Expand Up @@ -13104,6 +13091,7 @@ export default function TagUpdateForm(props) {
};
const [tagRecord, setTagRecord] = React.useState(tag);
const [linkedPosts, setLinkedPosts] = React.useState([]);
const canUnlinkPosts = false;
React.useEffect(() => {
const queryData = async () => {
const record = idProp ? await DataStore.query(Tag, idProp) : tag;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,17 @@ describe('amplify form renderer tests', () => {
expect(declaration).toMatchSnapshot();
});

it('should render an update form with validation for misconfigured schema for hasMany relationship', () => {
const { componentText } = generateWithAmplifyFormRenderer(
'forms/school-datastore-update',
'datastore/school-student',
undefined,
{ isNonModelSupported: true, isRelationshipSupported: true },
);

expect(componentText).toContain('const canUnlinkStudents = false');
});

it('should render an update form for model with composite keys', () => {
const { componentText, declaration } = generateWithAmplifyFormRenderer(
'forms/composite-dog-datastore-update',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
VariableStatement,
ExpressionStatement,
PropertyAssignment,
IfStatement,
} from 'typescript';
import { getSetNameIdentifier, lowerCaseFirst } from '../../helpers';
import { getDisplayValueObjectName } from './model-values';
Expand Down Expand Up @@ -205,7 +206,7 @@ export const buildDataStoreExpression = (
) => {
const thisModelPrimaryKeys = dataSchema.models[modelName].primaryKeys;
// promises.push(...statements that handle hasMany/ manyToMany/ hasOne-belongsTo relationships)
const relationshipsPromisesAccessStatements: (VariableStatement | ExpressionStatement)[] = [];
const relationshipsPromisesAccessStatements: (VariableStatement | ExpressionStatement | IfStatement)[] = [];
const hasManyRelationshipFields: string[] = [];
const nonModelArrayFields: string[] = [];
const savedRecordName = lowerCaseFirst(modelName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export const getRecordName = (modelName: string) => `${lowerCaseFirst(modelName)

export const getLinkedDataName = (modelName: string) => `linked${capitalizeFirstLetter(modelName)}`;

export const getCanUnlinkModelName = (modelName: string) => `canUnlink${capitalizeFirstLetter(modelName)}`;

export const getCurrentValueIdentifier = (fieldName: string) =>
factory.createIdentifier(getCurrentValueName(fieldName));

Expand Down

0 comments on commit ac80c9c

Please sign in to comment.