Skip to content

Commit 69cd61a

Browse files
authored
fix(appsync-modelgen-plugin): include auth rule provider in the Java models, if available (#168)
This change ensures that any providers specified in the @auth rule declaration are reflected as an attribute of the Java annotation.
1 parent d7a564c commit 69cd61a

File tree

3 files changed

+283
-9
lines changed

3 files changed

+283
-9
lines changed

packages/appsync-modelgen-plugin/src/__tests__/visitors/__snapshots__/appsync-java-visitor.test.ts.snap

Lines changed: 265 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import static com.amplifyframework.core.model.query.predicate.QueryField.field;
2525
/** This is an auto generated class representing the customClaim type in your schema. */
2626
@SuppressWarnings(\\"all\\")
2727
@ModelConfig(pluralName = \\"customClaims\\", authRules = {
28-
@AuthRule(allow = AuthStrategy.OWNER, ownerField = \\"owner\\", identityClaim = \\"user_id\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
28+
@AuthRule(allow = AuthStrategy.OWNER, ownerField = \\"owner\\", identityClaim = \\"user_id\\", provider = \\"userPools\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
2929
})
3030
public final class customClaim implements Model {
3131
public static final QueryField ID = field(\\"customClaim\\", \\"id\\");
@@ -241,7 +241,7 @@ import static com.amplifyframework.core.model.query.predicate.QueryField.field;
241241
/** This is an auto generated class representing the customClaim type in your schema. */
242242
@SuppressWarnings(\\"all\\")
243243
@ModelConfig(pluralName = \\"customClaims\\", authRules = {
244-
@AuthRule(allow = AuthStrategy.GROUPS, groupClaim = \\"user_groups\\", groups = { \\"Moderator\\" }, operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
244+
@AuthRule(allow = AuthStrategy.GROUPS, groupClaim = \\"user_groups\\", groups = { \\"Moderator\\" }, provider = \\"userPools\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
245245
})
246246
public final class customClaim implements Model {
247247
public static final QueryField ID = field(\\"customClaim\\", \\"id\\");
@@ -457,8 +457,8 @@ import static com.amplifyframework.core.model.query.predicate.QueryField.field;
457457
/** This is an auto generated class representing the Employee type in your schema. */
458458
@SuppressWarnings(\\"all\\")
459459
@ModelConfig(pluralName = \\"Employees\\", authRules = {
460-
@AuthRule(allow = AuthStrategy.OWNER, ownerField = \\"owner\\", identityClaim = \\"cognito:username\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ }),
461-
@AuthRule(allow = AuthStrategy.GROUPS, groupClaim = \\"cognito:groups\\", groups = { \\"Admins\\" }, operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
460+
@AuthRule(allow = AuthStrategy.OWNER, ownerField = \\"owner\\", identityClaim = \\"cognito:username\\", provider = \\"userPools\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ }),
461+
@AuthRule(allow = AuthStrategy.GROUPS, groupClaim = \\"cognito:groups\\", groups = { \\"Admins\\" }, provider = \\"userPools\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
462462
})
463463
public final class Employee implements Model {
464464
public static final QueryField ID = field(\\"Employee\\", \\"id\\");
@@ -469,7 +469,7 @@ public final class Employee implements Model {
469469
private final @ModelField(targetType=\\"String\\", isRequired = true) String name;
470470
private final @ModelField(targetType=\\"String\\", isRequired = true) String address;
471471
private final @ModelField(targetType=\\"String\\", authRules = {
472-
@AuthRule(allow = AuthStrategy.OWNER, ownerField = \\"owner\\", identityClaim = \\"cognito:username\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
472+
@AuthRule(allow = AuthStrategy.OWNER, ownerField = \\"owner\\", identityClaim = \\"cognito:username\\", provider = \\"userPools\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
473473
}) String ssn;
474474
private @ModelField(targetType=\\"AWSDateTime\\", isReadOnly = true) Temporal.DateTime createdAt;
475475
private @ModelField(targetType=\\"AWSDateTime\\", isReadOnly = true) Temporal.DateTime updatedAt;
@@ -713,7 +713,7 @@ import static com.amplifyframework.core.model.query.predicate.QueryField.field;
713713
/** This is an auto generated class representing the dynamicGroups type in your schema. */
714714
@SuppressWarnings(\\"all\\")
715715
@ModelConfig(pluralName = \\"dynamicGroups\\", authRules = {
716-
@AuthRule(allow = AuthStrategy.GROUPS, groupClaim = \\"cognito:groups\\", groupsField = \\"groups\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
716+
@AuthRule(allow = AuthStrategy.GROUPS, groupClaim = \\"cognito:groups\\", groupsField = \\"groups\\", provider = \\"userPools\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
717717
})
718718
public final class dynamicGroups implements Model {
719719
public static final QueryField ID = field(\\"dynamicGroups\\", \\"id\\");
@@ -904,6 +904,262 @@ public final class dynamicGroups implements Model {
904904
"
905905
`;
906906

907+
exports[`AppSyncModelVisitor Model with Auth should generate class with non-default providers 1`] = `
908+
"package com.amplifyframework.datastore.generated.model;
909+
910+
import com.amplifyframework.core.model.temporal.Temporal;
911+
912+
import java.util.List;
913+
import java.util.UUID;
914+
import java.util.Objects;
915+
916+
import androidx.core.util.ObjectsCompat;
917+
918+
import com.amplifyframework.core.model.AuthStrategy;
919+
import com.amplifyframework.core.model.Model;
920+
import com.amplifyframework.core.model.ModelOperation;
921+
import com.amplifyframework.core.model.annotations.AuthRule;
922+
import com.amplifyframework.core.model.annotations.Index;
923+
import com.amplifyframework.core.model.annotations.ModelConfig;
924+
import com.amplifyframework.core.model.annotations.ModelField;
925+
import com.amplifyframework.core.model.query.predicate.QueryField;
926+
927+
import static com.amplifyframework.core.model.query.predicate.QueryField.field;
928+
929+
/** This is an auto generated class representing the Employee type in your schema. */
930+
@SuppressWarnings(\\"all\\")
931+
@ModelConfig(pluralName = \\"Employees\\", authRules = {
932+
@AuthRule(allow = AuthStrategy.OWNER, ownerField = \\"owner\\", identityClaim = \\"cognito:username\\", provider = \\"userPools\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ }),
933+
@AuthRule(allow = AuthStrategy.PRIVATE, provider = \\"iam\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
934+
})
935+
public final class Employee implements Model {
936+
public static final QueryField ID = field(\\"Employee\\", \\"id\\");
937+
public static final QueryField NAME = field(\\"Employee\\", \\"name\\");
938+
public static final QueryField ADDRESS = field(\\"Employee\\", \\"address\\");
939+
public static final QueryField SSN = field(\\"Employee\\", \\"ssn\\");
940+
private final @ModelField(targetType=\\"ID\\", isRequired = true) String id;
941+
private final @ModelField(targetType=\\"String\\", isRequired = true) String name;
942+
private final @ModelField(targetType=\\"String\\", isRequired = true) String address;
943+
private final @ModelField(targetType=\\"String\\", authRules = {
944+
@AuthRule(allow = AuthStrategy.GROUPS, groupClaim = \\"cognito:groups\\", groups = { \\"Admins\\" }, provider = \\"oidc\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
945+
}) String ssn;
946+
private @ModelField(targetType=\\"AWSDateTime\\", isReadOnly = true) Temporal.DateTime createdAt;
947+
private @ModelField(targetType=\\"AWSDateTime\\", isReadOnly = true) Temporal.DateTime updatedAt;
948+
public String getId() {
949+
return id;
950+
}
951+
952+
public String getName() {
953+
return name;
954+
}
955+
956+
public String getAddress() {
957+
return address;
958+
}
959+
960+
public String getSsn() {
961+
return ssn;
962+
}
963+
964+
public Temporal.DateTime getCreatedAt() {
965+
return createdAt;
966+
}
967+
968+
public Temporal.DateTime getUpdatedAt() {
969+
return updatedAt;
970+
}
971+
972+
private Employee(String id, String name, String address, String ssn) {
973+
this.id = id;
974+
this.name = name;
975+
this.address = address;
976+
this.ssn = ssn;
977+
}
978+
979+
@Override
980+
public boolean equals(Object obj) {
981+
if (this == obj) {
982+
return true;
983+
} else if(obj == null || getClass() != obj.getClass()) {
984+
return false;
985+
} else {
986+
Employee employee = (Employee) obj;
987+
return ObjectsCompat.equals(getId(), employee.getId()) &&
988+
ObjectsCompat.equals(getName(), employee.getName()) &&
989+
ObjectsCompat.equals(getAddress(), employee.getAddress()) &&
990+
ObjectsCompat.equals(getSsn(), employee.getSsn()) &&
991+
ObjectsCompat.equals(getCreatedAt(), employee.getCreatedAt()) &&
992+
ObjectsCompat.equals(getUpdatedAt(), employee.getUpdatedAt());
993+
}
994+
}
995+
996+
@Override
997+
public int hashCode() {
998+
return new StringBuilder()
999+
.append(getId())
1000+
.append(getName())
1001+
.append(getAddress())
1002+
.append(getSsn())
1003+
.append(getCreatedAt())
1004+
.append(getUpdatedAt())
1005+
.toString()
1006+
.hashCode();
1007+
}
1008+
1009+
@Override
1010+
public String toString() {
1011+
return new StringBuilder()
1012+
.append(\\"Employee {\\")
1013+
.append(\\"id=\\" + String.valueOf(getId()) + \\", \\")
1014+
.append(\\"name=\\" + String.valueOf(getName()) + \\", \\")
1015+
.append(\\"address=\\" + String.valueOf(getAddress()) + \\", \\")
1016+
.append(\\"ssn=\\" + String.valueOf(getSsn()) + \\", \\")
1017+
.append(\\"createdAt=\\" + String.valueOf(getCreatedAt()) + \\", \\")
1018+
.append(\\"updatedAt=\\" + String.valueOf(getUpdatedAt()))
1019+
.append(\\"}\\")
1020+
.toString();
1021+
}
1022+
1023+
public static NameStep builder() {
1024+
return new Builder();
1025+
}
1026+
1027+
/**
1028+
* WARNING: This method should not be used to build an instance of this object for a CREATE mutation.
1029+
* This is a convenience method to return an instance of the object with only its ID populated
1030+
* to be used in the context of a parameter in a delete mutation or referencing a foreign key
1031+
* in a relationship.
1032+
* @param id the id of the existing item this instance will represent
1033+
* @return an instance of this model with only ID populated
1034+
* @throws IllegalArgumentException Checks that ID is in the proper format
1035+
*/
1036+
public static Employee justId(String id) {
1037+
try {
1038+
UUID.fromString(id); // Check that ID is in the UUID format - if not an exception is thrown
1039+
} catch (Exception exception) {
1040+
throw new IllegalArgumentException(
1041+
\\"Model IDs must be unique in the format of UUID. This method is for creating instances \\" +
1042+
\\"of an existing object with only its ID field for sending as a mutation parameter. When \\" +
1043+
\\"creating a new object, use the standard builder method and leave the ID field blank.\\"
1044+
);
1045+
}
1046+
return new Employee(
1047+
id,
1048+
null,
1049+
null,
1050+
null
1051+
);
1052+
}
1053+
1054+
public CopyOfBuilder copyOfBuilder() {
1055+
return new CopyOfBuilder(id,
1056+
name,
1057+
address,
1058+
ssn);
1059+
}
1060+
public interface NameStep {
1061+
AddressStep name(String name);
1062+
}
1063+
1064+
1065+
public interface AddressStep {
1066+
BuildStep address(String address);
1067+
}
1068+
1069+
1070+
public interface BuildStep {
1071+
Employee build();
1072+
BuildStep id(String id) throws IllegalArgumentException;
1073+
BuildStep ssn(String ssn);
1074+
}
1075+
1076+
1077+
public static class Builder implements NameStep, AddressStep, BuildStep {
1078+
private String id;
1079+
private String name;
1080+
private String address;
1081+
private String ssn;
1082+
@Override
1083+
public Employee build() {
1084+
String id = this.id != null ? this.id : UUID.randomUUID().toString();
1085+
1086+
return new Employee(
1087+
id,
1088+
name,
1089+
address,
1090+
ssn);
1091+
}
1092+
1093+
@Override
1094+
public AddressStep name(String name) {
1095+
Objects.requireNonNull(name);
1096+
this.name = name;
1097+
return this;
1098+
}
1099+
1100+
@Override
1101+
public BuildStep address(String address) {
1102+
Objects.requireNonNull(address);
1103+
this.address = address;
1104+
return this;
1105+
}
1106+
1107+
@Override
1108+
public BuildStep ssn(String ssn) {
1109+
this.ssn = ssn;
1110+
return this;
1111+
}
1112+
1113+
/**
1114+
* WARNING: Do not set ID when creating a new object. Leave this blank and one will be auto generated for you.
1115+
* This should only be set when referring to an already existing object.
1116+
* @param id id
1117+
* @return Current Builder instance, for fluent method chaining
1118+
* @throws IllegalArgumentException Checks that ID is in the proper format
1119+
*/
1120+
public BuildStep id(String id) throws IllegalArgumentException {
1121+
this.id = id;
1122+
1123+
try {
1124+
UUID.fromString(id); // Check that ID is in the UUID format - if not an exception is thrown
1125+
} catch (Exception exception) {
1126+
throw new IllegalArgumentException(\\"Model IDs must be unique in the format of UUID.\\",
1127+
exception);
1128+
}
1129+
1130+
return this;
1131+
}
1132+
}
1133+
1134+
1135+
public final class CopyOfBuilder extends Builder {
1136+
private CopyOfBuilder(String id, String name, String address, String ssn) {
1137+
super.id(id);
1138+
super.name(name)
1139+
.address(address)
1140+
.ssn(ssn);
1141+
}
1142+
1143+
@Override
1144+
public CopyOfBuilder name(String name) {
1145+
return (CopyOfBuilder) super.name(name);
1146+
}
1147+
1148+
@Override
1149+
public CopyOfBuilder address(String address) {
1150+
return (CopyOfBuilder) super.address(address);
1151+
}
1152+
1153+
@Override
1154+
public CopyOfBuilder ssn(String ssn) {
1155+
return (CopyOfBuilder) super.ssn(ssn);
1156+
}
1157+
}
1158+
1159+
}
1160+
"
1161+
`;
1162+
9071163
exports[`AppSyncModelVisitor Model with Auth should generate class with owner auth 1`] = `
9081164
"package com.amplifyframework.datastore.generated.model;
9091165
@@ -929,7 +1185,7 @@ import static com.amplifyframework.core.model.query.predicate.QueryField.field;
9291185
/** This is an auto generated class representing the simpleOwnerAuth type in your schema. */
9301186
@SuppressWarnings(\\"all\\")
9311187
@ModelConfig(pluralName = \\"simpleOwnerAuths\\", authRules = {
932-
@AuthRule(allow = AuthStrategy.OWNER, ownerField = \\"owner\\", identityClaim = \\"cognito:username\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
1188+
@AuthRule(allow = AuthStrategy.OWNER, ownerField = \\"owner\\", identityClaim = \\"cognito:username\\", provider = \\"userPools\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
9331189
})
9341190
public final class simpleOwnerAuth implements Model {
9351191
public static final QueryField ID = field(\\"simpleOwnerAuth\\", \\"id\\");
@@ -1145,7 +1401,7 @@ import static com.amplifyframework.core.model.query.predicate.QueryField.field;
11451401
/** This is an auto generated class representing the allowRead type in your schema. */
11461402
@SuppressWarnings(\\"all\\")
11471403
@ModelConfig(pluralName = \\"allowReads\\", authRules = {
1148-
@AuthRule(allow = AuthStrategy.OWNER, ownerField = \\"owner\\", identityClaim = \\"cognito:username\\", operations = { ModelOperation.CREATE, ModelOperation.DELETE, ModelOperation.UPDATE })
1404+
@AuthRule(allow = AuthStrategy.OWNER, ownerField = \\"owner\\", identityClaim = \\"cognito:username\\", provider = \\"userPools\\", operations = { ModelOperation.CREATE, ModelOperation.DELETE, ModelOperation.UPDATE })
11491405
})
11501406
public final class allowRead implements Model {
11511407
public static final QueryField ID = field(\\"allowRead\\", \\"id\\");
@@ -2011,7 +2267,7 @@ import static com.amplifyframework.core.model.query.predicate.QueryField.field;
20112267
/** This is an auto generated class representing the staticGroups type in your schema. */
20122268
@SuppressWarnings(\\"all\\")
20132269
@ModelConfig(pluralName = \\"staticGroups\\", authRules = {
2014-
@AuthRule(allow = AuthStrategy.GROUPS, groupClaim = \\"cognito:groups\\", groups = { \\"Admin\\" }, operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
2270+
@AuthRule(allow = AuthStrategy.GROUPS, groupClaim = \\"cognito:groups\\", groups = { \\"Admin\\" }, provider = \\"userPools\\", operations = { ModelOperation.CREATE, ModelOperation.UPDATE, ModelOperation.DELETE, ModelOperation.READ })
20152271
})
20162272
public final class staticGroups implements Model {
20172273
public static final QueryField ID = field(\\"staticGroups\\", \\"id\\");

packages/appsync-modelgen-plugin/src/__tests__/visitors/appsync-java-visitor.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,21 @@ describe('AppSyncModelVisitor', () => {
290290
expect(generatedCode).toMatchSnapshot();
291291
});
292292

293+
it('should generate class with non-default providers', () => {
294+
const schema = /* GraphQL */ `
295+
type Employee @model @auth(rules: [{ allow: owner }, { allow: private, provider:"iam" } ]) {
296+
id: ID!
297+
name: String!
298+
address: String!
299+
ssn: String @auth(rules: [{ allow: groups, provider:"oidc", groups: ["Admins"] }])
300+
}
301+
`;
302+
const visitor = getVisitor(schema, 'Employee');
303+
const generatedCode = visitor.generate();
304+
expect(() => validateJava(generatedCode)).not.toThrow();
305+
expect(generatedCode).toMatchSnapshot();
306+
});
307+
293308
it('should generate class with private authorization and field auth', () => {
294309
const schema = /* GraphQL */ `
295310
type privateType @model @auth(rules: [{ allow: private }]) {

packages/appsync-modelgen-plugin/src/visitors/appsync-java-visitor.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,9 @@ export class AppSyncModelJavaVisitor<
808808
printWarning(`Model has auth with authStrategy ${rule.allow} of which is not yet supported`);
809809
return;
810810
}
811+
if (rule.provider != null) {
812+
authRule.push(`provider = "${rule.provider}"`)
813+
}
811814
authRule.push(`operations = { ${rule.operations?.map(op => operationMapping[op]).join(', ')} }`);
812815
rules.push(`@AuthRule(${authRule.join(', ')})`);
813816
});

0 commit comments

Comments
 (0)