Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: throw validation error when updating models with misconfigured h… #886

Merged
merged 3 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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.`);
hein-j marked this conversation as resolved.
Show resolved Hide resolved
}
Students.forEach((r) => studentsSet.add(r.id));
linkedStudents.forEach((r) => linkedStudentsSet.add(r.id));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3514,7 +3514,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 @@ -3765,27 +3767,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 && compositeToysToUnLink.length > 0) {
throw Error(
\`CompositeToy \${original.id} cannot be unlinked from CompositeDog because compositeDogCompositeToysName is a required field.\`
hein-j marked this conversation as resolved.
Show resolved Hide resolved
);
} 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 @@ -4596,7 +4590,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 @@ -4846,23 +4842,18 @@ export default function UpdateCPKTeacherForm(props) {
}
});
cPKProjectsToUnLink.forEach((original) => {
try {
promises.push(
DataStore.save(
CPKProject.copyOf(original, (updated) => {
updated.cPKTeacherID = null;
})
)
if (!canUnlinkCPKProjects && cPKProjectsToUnLink.length > 0) {
throw Error(
\`CPKProject \${original.id} 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 @@ -11347,6 +11338,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 @@ -11465,23 +11457,18 @@ export default function SchoolUpdateForm(props) {
}
});
studentsToUnLink.forEach((original) => {
try {
promises.push(
DataStore.save(
Student.copyOf(original, (updated) => {
updated.schoolID = null;
})
)
if (!canUnlinkStudents && studentsToUnLink.length > 0) {
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 @@ -12387,6 +12374,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
Loading