diff --git a/aws-datastore/src/androidTest/java/com/amplifyframework/datastore/storage/sqlite/SQLiteStorageAdapterQueryTest.java b/aws-datastore/src/androidTest/java/com/amplifyframework/datastore/storage/sqlite/SQLiteStorageAdapterQueryTest.java index e90516747b..56b4900960 100644 --- a/aws-datastore/src/androidTest/java/com/amplifyframework/datastore/storage/sqlite/SQLiteStorageAdapterQueryTest.java +++ b/aws-datastore/src/androidTest/java/com/amplifyframework/datastore/storage/sqlite/SQLiteStorageAdapterQueryTest.java @@ -25,10 +25,16 @@ import com.amplifyframework.datastore.storage.SynchronousStorageAdapter; import com.amplifyframework.testmodels.commentsblog.AmplifyModelProvider; import com.amplifyframework.testmodels.commentsblog.Blog; +import com.amplifyframework.testmodels.commentsblog.Blog3; import com.amplifyframework.testmodels.commentsblog.BlogOwner; +import com.amplifyframework.testmodels.commentsblog.BlogOwner3; import com.amplifyframework.testmodels.commentsblog.Comment; import com.amplifyframework.testmodels.commentsblog.Post; +import com.amplifyframework.testmodels.commentsblog.Post2; import com.amplifyframework.testmodels.commentsblog.PostStatus; +import com.amplifyframework.testmodels.cpk.BlogCPK; +import com.amplifyframework.testmodels.cpk.BlogOwnerCPK; +import com.amplifyframework.testmodels.cpk.PostCPK; import com.amplifyframework.testmodels.phonecall.Call; import com.amplifyframework.testmodels.phonecall.Person; import com.amplifyframework.testmodels.phonecall.Phone; @@ -44,6 +50,7 @@ import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.UUID; import io.reactivex.rxjava3.core.Observable; @@ -89,6 +96,17 @@ public void setupForCallModel() { ); } + /** + * Remove any old database files, and then re-provision a new storage adapter, + * that is able to store the Blog CPK family of models. + */ + public void setupForBlogCPKModel() { + teardown(); + this.adapter = TestStorageAdapter.create( + com.amplifyframework.testmodels.cpk.AmplifyModelProvider.getInstance() + ); + } + /** * Close the open database, and cleanup any database files that it left. */ @@ -225,6 +243,346 @@ public void querySavedDataWithMultipleForeignKeysOfSameType() throws DataStoreEx assertTrue(phoneCalls.contains(phoneCall)); } + /** + * Test that querying the saved item with multiple paths for foreign keys to the same + * model correctly forms the where clause and returns expected data. + * @throws DataStoreException On unexpected failure manipulating items in/out of DataStore. + */ + @Test + public void querySavedDataWithMultipleJoinPaths() throws DataStoreException { + + final String blogOwnerId = UUID.randomUUID().toString(); + final String blogId = UUID.randomUUID().toString(); + final String postId = UUID.randomUUID().toString(); + + final BlogOwner3 blogOwner = BlogOwner3.builder() + .name("Sample Blog") + .id(blogOwnerId) + .build(); + + final Blog3 blog = Blog3.builder() + .name("Sample Blog") + .id(blogId) + .owner(blogOwner) + .build(); + + final Post2 post = Post2.builder() + .title("Placeholder title") + .status(PostStatus.ACTIVE) + .rating(5) + .id(postId) + .blog(blog) + .blogOwner(blogOwner) + .build(); + + adapter.save(blogOwner); + adapter.save(blog); + adapter.save(post); + + final List posts = adapter.query(Post2.class, + Where.matches(Post2.BLOG_OWNER.eq(blogOwnerId))); + assertTrue(posts.contains(post)); + } + + /** + * Test that querying the saved item with multiple paths for foreign keys to the same + * model with missing data for intermediate model in join path correctly forms + * the where clause and returns expected data. + * @throws DataStoreException On unexpected failure manipulating items in/out of DataStore + */ + @Test + public void querySavedDataWithMultipleJoinWithMissingDataForIntermediateJoinPath() + throws DataStoreException { + + final String blogOwnerAlphaId = UUID.randomUUID().toString(); + final String blogOwnerBetaId = UUID.randomUUID().toString(); + final String blogOwnerGammaId = UUID.randomUUID().toString(); + + final String alphaPostId = UUID.randomUUID().toString(); + final String betaPostId = UUID.randomUUID().toString(); + final String gammaPostId = UUID.randomUUID().toString(); + + final BlogOwner3 blogOwnerAlpha = BlogOwner3.builder() + .name("Alpha") + .id(blogOwnerAlphaId) + .build(); + + final BlogOwner3 blogOwnerBeta = BlogOwner3.builder() + .name("Beta") + .id(blogOwnerBetaId) + .build(); + + final BlogOwner3 blogOwnerGamma = BlogOwner3.builder() + .name("Gamma") + .id(blogOwnerGammaId) + .build(); + + final Post2 postByAlpha = Post2.builder() + .title("Alpha Post") + .status(PostStatus.ACTIVE) + .rating(5) + .id(alphaPostId) + .blogOwner(blogOwnerAlpha) + .build(); + + final Post2 postByBeta = Post2.builder() + .title("Beta Post") + .status(PostStatus.ACTIVE) + .rating(5) + .id(betaPostId) + .blogOwner(blogOwnerBeta) + .build(); + + final Post2 postByGamma = Post2.builder() + .title("Gamma Post") + .status(PostStatus.ACTIVE) + .rating(5) + .id(gammaPostId) + .blogOwner(blogOwnerGamma) + .build(); + + adapter.save(blogOwnerAlpha); + adapter.save(blogOwnerBeta); + adapter.save(blogOwnerGamma); + adapter.save(postByAlpha); + adapter.save(postByBeta); + adapter.save(postByGamma); + + List posts = adapter.query(Post2.class, + Where.matches(Post2.BLOG_OWNER.eq(blogOwnerAlphaId))); + assertEquals(1, posts.size()); + assertTrue(posts.contains(postByAlpha)); + + posts = adapter.query(Post2.class, + Where.matches(Post2.BLOG_OWNER.eq(blogOwnerBetaId))); + assertEquals(1, posts.size()); + assertTrue(posts.contains(postByBeta)); + + posts = adapter.query(Post2.class, + Where.matches(Post2.BLOG_OWNER.eq(blogOwnerGammaId))); + assertEquals(1, posts.size()); + assertTrue(posts.contains(postByGamma)); + + } + + /** + * Test that querying the saved item with multiple paths for foreign keys to the same + * model along with Multiple conditions correctly forms the where clause and returns expected + * data. + * @throws DataStoreException On unexpected failure manipulating items in/out of DataStore. + */ + @Test + public void querySavedDataWithMultipleJoinPathsWithMultipleConditions() throws DataStoreException { + + final String blogOwnerId = UUID.randomUUID().toString(); + final String blogId = UUID.randomUUID().toString(); + final String postId = UUID.randomUUID().toString(); + + final BlogOwner3 blogOwner = BlogOwner3.builder() + .name("Sample BlogOwner") + .id(blogOwnerId) + .build(); + + final Blog3 blog = Blog3.builder() + .name("Sample Blog") + .id(blogId) + .owner(blogOwner) + .build(); + + final Post2 post = Post2.builder() + .title("Placeholder title") + .status(PostStatus.ACTIVE) + .rating(5) + .id(postId) + .blog(blog) + .blogOwner(blogOwner) + .build(); + + adapter.save(blogOwner); + adapter.save(blog); + adapter.save(post); + + final List posts = adapter.query(Post2.class, + Where.matches(Post2.BLOG_OWNER.eq(blogOwnerId).and(Post2.RATING.gt(4). + and(Post2.STATUS.eq(PostStatus.ACTIVE))))); + assertTrue(posts.contains(post)); + } + + /** + * Test that querying the saved item with multiple paths for foreign keys to the same + * model with Custom Primary Key correctly forms the where clause + * and returns expected data. + * @throws AmplifyException On unexpected failure manipulating items in/out of DataStore. + */ + @Test + public void querySavedDataWithCPK() throws AmplifyException { + setupForBlogCPKModel(); + final String blogOwnerId = UUID.randomUUID().toString(); + final String blogId = UUID.randomUUID().toString(); + final String postId = UUID.randomUUID().toString(); + + final BlogOwnerCPK blogOwner = BlogOwnerCPK.builder() + .id(blogOwnerId) + .name("Sample BlogOwner") + .build(); + + final BlogCPK blog = BlogCPK.builder() + .id(blogId) + .name("Sample Blog") + .owner(blogOwner) + .build(); + + final PostCPK post = PostCPK.builder() + .id(postId) + .title("Placeholder title") + .rating(5) + .blogOwner(blogOwner) + .blog(blog) + .build(); + + adapter.save(blogOwner); + adapter.save(blog); + adapter.save(post); + + // Basic CPK Query + final List posts = adapter.query(PostCPK.class, + Where.identifier(PostCPK.class, new PostCPK.PostCPKIdentifier(postId, "Placeholder title"))); + assertTrue(posts.contains(post)); + } + + /** + * Tests the retrieval of {@link PostCPK} objects through a query operation without the presence + * of intermediate {@link Blog} records in a complex relationship chain. The relationship chain + * tested here is {@link BlogOwnerCPK} -> {@link Blog} -> {@link PostCPK} and + * {@link BlogOwnerCPK} -> {@link PostCPK}. The test ensures that {@link PostCPK} objects + * associated directly with a {@link BlogOwnerCPK} can be successfully retrieved even when + * the {@link Blog} records, which are typically part of the path in the relationship, are not + * present in the database. + *

+ * This test is important for scenarios where the data model allows for direct associations + * between {@link BlogOwnerCPK} and {@link PostCPK} without the necessity of linking through + * a {@link Blog} record, ensuring data integrity and query capabilities in partial data states. + * + * @throws AmplifyException if an error occurs during the setup or execution of the query + * operation, indicating a failure in the data layer or the query + * construction. + */ + @Test + public void querySavedDataWithMultipleJoinPathsWithMultipleConditionsAndCPK() throws AmplifyException { + setupForBlogCPKModel(); + final String blogOwnerId = UUID.randomUUID().toString(); + final String blogId = UUID.randomUUID().toString(); + final String postId = UUID.randomUUID().toString(); + + final BlogOwnerCPK blogOwner = BlogOwnerCPK.builder() + .id(blogOwnerId) + .name("Sample BlogOwner") + .build(); + + final PostCPK post = PostCPK.builder() + .id(postId) + .title("Placeholder title") + .rating(5) + .blogOwner(blogOwner) + .build(); + adapter.save(blogOwner); + adapter.save(post); + String expectedBlogOwnerCPK = "\"" + blogOwnerId + "\"#\"Sample BlogOwner\""; + final List postsByBlogOwner = adapter.query(PostCPK.class, + Where.identifier(PostCPK.class, new PostCPK.PostCPKIdentifier(postId, "Placeholder title")) + .matches(PostCPK.BLOG_OWNER.eq(expectedBlogOwnerCPK).and(PostCPK.RATING.gt(4) + .and(PostCPK.TITLE.eq("Placeholder title"))))); + assertTrue(postsByBlogOwner.contains(post)); + } + + /** + * Test that querying the saved item with multiple paths for foreign keys to the same + * model with missing data for intermediate model in join path and Custom Primary Key + * correctly forms the where clause and returns expected data. + * @throws AmplifyException On unexpected failure manipulating items in/out of DataStore + */ + @Test + public void queryMultipleJoinWithMultipleRecordsWithMultipleConditionsAndCPK() + throws AmplifyException { + setupForBlogCPKModel(); + final String blogOwnerAlphaId = UUID.randomUUID().toString(); + final String blogOwnerBetaId = UUID.randomUUID().toString(); + final String blogOwnerGammaId = UUID.randomUUID().toString(); + + final String alphaPostId = UUID.randomUUID().toString(); + final String betaPostId = UUID.randomUUID().toString(); + final String gammaPostId = UUID.randomUUID().toString(); + + final BlogOwnerCPK blogOwnerAlpha = BlogOwnerCPK.builder() + .id(blogOwnerAlphaId) + .name("Alpha") + .build(); + + final BlogOwnerCPK blogOwnerBeta = BlogOwnerCPK.builder() + .id(blogOwnerBetaId) + .name("Beta") + .build(); + + final BlogOwnerCPK blogOwnerGamma = BlogOwnerCPK.builder() + .id(blogOwnerGammaId) + .name("Gamma") + .build(); + + final PostCPK postByAlpha = PostCPK.builder() + .id(alphaPostId) + .title("Alpha Post") + .rating(5) + .blogOwner(blogOwnerAlpha) + .build(); + + final PostCPK postByBeta = PostCPK.builder() + .id(betaPostId) + .title("Beta Post") + .rating(5) + .blogOwner(blogOwnerBeta) + .build(); + + final PostCPK postByGamma = PostCPK.builder() + .id(gammaPostId) + .title("Gamma Post") + .rating(5) + .blogOwner(blogOwnerGamma) + .build(); + + adapter.save(blogOwnerAlpha); + adapter.save(blogOwnerBeta); + adapter.save(blogOwnerGamma); + adapter.save(postByAlpha); + adapter.save(postByBeta); + adapter.save(postByGamma); + + String expectedAlphaBlogOwnerCPK = "\"" + blogOwnerAlphaId + "\"#\"Alpha\""; + String expectedBetaBlogOwnerCPK = "\"" + blogOwnerBetaId + "\"#\"Beta\""; + String expectedGammaBlogOwnerCPK = "\"" + blogOwnerGammaId + "\"#\"Gamma\""; + + List posts = adapter.query(PostCPK.class, + Where.identifier(PostCPK.class, new PostCPK.PostCPKIdentifier(alphaPostId, "Alpha Post")) + .matches(PostCPK.BLOG_OWNER.eq(expectedAlphaBlogOwnerCPK).and(PostCPK.RATING.gt(4) + .and(PostCPK.TITLE.eq("Alpha Post"))))); + assertEquals(1, posts.size()); + assertTrue(posts.contains(postByAlpha)); + + posts = adapter.query(PostCPK.class, + Where.identifier(PostCPK.class, new PostCPK.PostCPKIdentifier(betaPostId, "Beta Post")) + .matches(PostCPK.BLOG_OWNER.eq(expectedBetaBlogOwnerCPK).and(PostCPK.RATING.gt(4) + .and(PostCPK.TITLE.eq("Beta Post"))))); + assertEquals(1, posts.size()); + assertTrue(posts.contains(postByBeta)); + + posts = adapter.query(PostCPK.class, + Where.identifier(PostCPK.class, new PostCPK.PostCPKIdentifier(gammaPostId, "Gamma Post")) + .matches(PostCPK.BLOG_OWNER.eq(expectedGammaBlogOwnerCPK).and(PostCPK.RATING.gt(4) + .and(PostCPK.TITLE.eq("Gamma Post"))))); + assertEquals(1, posts.size()); + assertTrue(posts.contains(postByGamma)); + + } + /** * Test that querying the saved item with a foreign key * also populates that instance variable with object. diff --git a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java index d30f5e504b..765407a315 100644 --- a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java +++ b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java @@ -48,9 +48,11 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Queue; import java.util.Set; /** @@ -178,7 +180,7 @@ public SqlCommand queryFor(@NonNull ModelSchema modelSchema, tableCount.put(tableName, 1); // Joins the foreign keys - recursivelyBuildJoins(table, columns, joinStatement, tableCount, tableName); + buildJoinsUsingBFS(table, columns, joinStatement, tableCount, tableName); // Convert columns to comma-separated column names boolean firstTable = true; @@ -499,67 +501,6 @@ private List extractFieldValues(@NonNull Model model) throws DataStoreEx return bindings; } - /** - * Recursively build joins for multilevel nested joins. - * - */ - private void recursivelyBuildJoins(SQLiteTable table, Map> columns, - StringBuilder joinStatement, Map tableCount, - String tableAlias) { - // Joins the foreign keys - // LEFT JOIN if foreign key is optional, INNER JOIN otherwise. - final Iterator foreignKeyIterator = table.getForeignKeys().iterator(); - while (foreignKeyIterator.hasNext()) { - final SQLiteColumn foreignKey = foreignKeyIterator.next(); - final String ownedTableName = foreignKey.getOwnedType(); - final ModelSchema ownedSchema = schemaRegistry.getModelSchemaForModelClass(ownedTableName); - final SQLiteTable ownedTable = SQLiteTable.fromSchema(ownedSchema); - - int newOwnedTableCount = 1; - String ownedTableAlias = ownedTableName; - if (tableCount.containsKey(ownedTableName)) { - Integer currentOwnedTableCount = tableCount.get(ownedTableName); - newOwnedTableCount += currentOwnedTableCount == null ? 0 : currentOwnedTableCount; - ownedTableAlias += newOwnedTableCount; - } - tableCount.put(ownedTableName, newOwnedTableCount); - columns.put(ownedTableAlias, ownedTable.getSortedColumns()); - - SqlKeyword joinType = foreignKey.isNonNull() - ? SqlKeyword.INNER_JOIN - : SqlKeyword.LEFT_JOIN; - - joinStatement.append(joinType) - .append(SqlKeyword.DELIMITER) - .append(Wrap.inBackticks(ownedTableName)) - .append(SqlKeyword.DELIMITER); - - if (!ownedTableName.equals(ownedTableAlias)) { - joinStatement.append(SqlKeyword.AS) - .append(SqlKeyword.DELIMITER) - .append(Wrap.inBackticks(ownedTableAlias)) - .append(SqlKeyword.DELIMITER); - } - - // Reference the foreign key and primary key using the corresponding table's alias. - String foreignKeyName = foreignKey.getQuotedColumnName().replaceFirst(table.getName(), tableAlias); - String ownedTablePrimaryKeyName = ownedTable.getPrimaryKeyColumnName().replaceFirst(ownedTableName, - ownedTableAlias); - joinStatement.append(SqlKeyword.ON) - .append(SqlKeyword.DELIMITER) - .append(foreignKeyName) - .append(SqlKeyword.EQUAL) - .append(ownedTablePrimaryKeyName); - - if (foreignKeyIterator.hasNext()) { - joinStatement.append(SqlKeyword.DELIMITER); - } - - // important that this comes last to maintain the order of the joins - recursivelyBuildJoins(ownedTable, columns, joinStatement, tableCount, ownedTableAlias); - } - } - // Utility method to parse columns in CREATE TABLE private StringBuilder parseColumns(SQLiteTable table) { final StringBuilder builder = new StringBuilder(); @@ -656,4 +597,134 @@ private boolean shouldCreateIndex(ModelIndex modelIndex, MapImplementation Notes:

+ *
    + *
  • Multiple Foreign Key References: This method can process multiple + * references from a single table to another by incorporating the foreign key name into the + * unique combination key. This allows the method to differentiate between different join paths + * and process each unique path accordingly.
  • + * + *
  • Preventing Infinite Loops: To avoid infinite loops in the presence of + * cyclic relationships, the method tracks processed joins using a combination of the current + * table alias, the target table name, and the foreign key name. + * This ensures that each join operation is processed only once, even in complex schemas + * with potential cycles.
  • + *
+ * + * @param rootTable The starting table for join construction. + * @param columns A map to maintain a list of columns for each table encountered during BFS + * traversal. Updated as new tables are processed. + * @param joinStatement The StringBuilder to append JOIN clauses to. This will be modified to + * include the generated JOIN statements. + * @param tableCount A map to track the occurrence count of each table, used for generating + * unique table aliases. + * @param rootTableAlias The alias for the root table, used in the initial JOIN statement. + * @implNote In highly nested or complex cyclic schemas, additional safeguards such as + * traversal depth limits may be advisable to ensure optimal performance and prevent excessively + * deep traversal. + */ + private void buildJoinsUsingBFS(final SQLiteTable rootTable, final Map> columns, + StringBuilder joinStatement, final Map tableCount, + final String rootTableAlias) { + Queue queue = new LinkedList<>(); + queue.add(new TableInfo(rootTable, rootTableAlias)); + + // Use a Set to track visited (table, foreign key) combinations to allow multiple references. + Set visitedCombinations = new HashSet<>(); + + while (!queue.isEmpty()) { + final TableInfo currentInfo = queue.poll(); + final SQLiteTable table = currentInfo.getSQLiteTable(); + final String tableAlias = currentInfo.getAlias(); + + final Iterator foreignKeyIterator = table.getForeignKeys().iterator(); + while (foreignKeyIterator.hasNext()) { + final SQLiteColumn foreignKey = foreignKeyIterator.next(); + final String ownedTableName = foreignKey.getOwnedType(); + final String combinationKey = tableAlias + "->" + ownedTableName + ":" + foreignKey.getName(); + + // Skip if this table-foreignKey combination has been processed. + if (!visitedCombinations.add(combinationKey)) { + continue; + } + + final ModelSchema ownedSchema = schemaRegistry.getModelSchemaForModelClass(ownedTableName); + final SQLiteTable ownedTable = SQLiteTable.fromSchema(ownedSchema); + + int newOwnedTableCount = 1; + String ownedTableAlias = ownedTableName; + if (tableCount.containsKey(ownedTableName)) { + Integer currentOwnedTableCount = tableCount.get(ownedTableName); + newOwnedTableCount += currentOwnedTableCount == null ? 0 : currentOwnedTableCount; + ownedTableAlias += newOwnedTableCount; + } + tableCount.put(ownedTableName, newOwnedTableCount); + columns.put(ownedTableAlias, ownedTable.getSortedColumns()); + + SqlKeyword joinType = foreignKey.isNonNull() + ? SqlKeyword.INNER_JOIN + : SqlKeyword.LEFT_JOIN; + + joinStatement.append(joinType) + .append(SqlKeyword.DELIMITER) + .append(Wrap.inBackticks(ownedTableName)) + .append(SqlKeyword.DELIMITER); + + if (!ownedTableName.equals(ownedTableAlias)) { + joinStatement.append(SqlKeyword.AS) + .append(SqlKeyword.DELIMITER) + .append(Wrap.inBackticks(ownedTableAlias)) + .append(SqlKeyword.DELIMITER); + } + + String foreignKeyName = foreignKey.getQuotedColumnName().replaceFirst(table.getName(), tableAlias); + String ownedTablePrimaryKeyName = ownedTable.getPrimaryKeyColumnName().replaceFirst( + ownedTableName, ownedTableAlias); + joinStatement.append(SqlKeyword.ON) + .append(SqlKeyword.DELIMITER) + .append(foreignKeyName) + .append(SqlKeyword.EQUAL) + .append(ownedTablePrimaryKeyName); + + if (!queue.isEmpty() || foreignKeyIterator.hasNext()) { + joinStatement.append(SqlKeyword.DELIMITER); + } + queue.add(new TableInfo(ownedTable, ownedTableAlias)); + } + } + } + + /** + * Represents information about a table in the context of building SQL JOIN statements. + * This class holds a reference to a SQLiteTable instance and its associated alias. + * It is primarily used in the context of BFS traversal for SQL join generation, + * where it is necessary to keep track of each table and its alias while processing the graph of tables. + * + * @implNote This class is a simple container used for organizing table data and its corresponding alias + * during the BFS traversal in the join building process. It includes basic getter methods for both fields. + */ + private static class TableInfo { + private final SQLiteTable table; + private final String alias; + + TableInfo(SQLiteTable table, String alias) { + this.table = Objects.requireNonNull(table, "Table cannot be null"); + this.alias = Objects.requireNonNull(alias, "Alias cannot be null"); + } + + public SQLiteTable getSQLiteTable() { + return table; + } + + public String getAlias() { + return alias; + } + } } diff --git a/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/AmplifyModelProvider.java b/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/AmplifyModelProvider.java index 0360ce9f00..78bbd264f3 100644 --- a/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/AmplifyModelProvider.java +++ b/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/AmplifyModelProvider.java @@ -49,7 +49,9 @@ public static AmplifyModelProvider getInstance() { @Override public Set> models() { final Set> modifiableSet = new HashSet<>( - Arrays.>asList(Blog.class, Post.class, Comment.class, Author.class, BlogOwner.class, OtherBlog.class, BlogOwnerWithCustomPK.class) + Arrays.>asList(Blog.class, Post.class, Comment.class, Author.class, + BlogOwner.class, OtherBlog.class, BlogOwnerWithCustomPK.class, BlogOwner3.class, + Blog3.class, Post2.class) ); return Immutable.of(modifiableSet); diff --git a/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/Blog3.java b/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/Blog3.java new file mode 100644 index 0000000000..fbef01c2a2 --- /dev/null +++ b/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/Blog3.java @@ -0,0 +1,241 @@ +package com.amplifyframework.testmodels.commentsblog; + +import static com.amplifyframework.core.model.query.predicate.QueryField.field; + +import androidx.core.util.ObjectsCompat; + +import com.amplifyframework.core.model.Model; +import com.amplifyframework.core.model.ModelIdentifier; +import com.amplifyframework.core.model.annotations.BelongsTo; +import com.amplifyframework.core.model.annotations.HasMany; +import com.amplifyframework.core.model.annotations.Index; +import com.amplifyframework.core.model.annotations.ModelConfig; +import com.amplifyframework.core.model.annotations.ModelField; +import com.amplifyframework.core.model.query.predicate.QueryField; +import com.amplifyframework.core.model.temporal.Temporal; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +/** This is an auto generated class representing the Blog3 type in your schema. */ +@SuppressWarnings("all") +@ModelConfig(pluralName = "Blog3s", type = Model.Type.USER, version = 1) +@Index(name = "byBlogOwner3", fields = {"blogOwnerID"}) +public final class Blog3 implements Model { + public static final QueryField ID = field("Blog3", "id"); + public static final QueryField NAME = field("Blog3", "name"); + public static final QueryField CREATED_AT = field("Blog3", "createdAt"); + public static final QueryField OWNER = field("Blog3", "blogOwnerID"); + private final @ModelField(targetType="ID", isRequired = true) String id; + private final @ModelField(targetType="String", isRequired = true) String name; + private final @ModelField(targetType="AWSDateTime") Temporal.DateTime createdAt; + private final @ModelField(targetType="Post2") @HasMany(associatedWith = "blog", type = Post2.class) List posts = null; + private final @ModelField(targetType="BlogOwner3") @BelongsTo(targetName = "blogOwnerID", targetNames = {"blogOwnerID"}, type = BlogOwner3.class) BlogOwner3 owner; + private @ModelField(targetType="AWSDateTime", isReadOnly = true) Temporal.DateTime updatedAt; + /** @deprecated This API is internal to Amplify and should not be used. */ + @Deprecated + public String resolveIdentifier() { + return id; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public Temporal.DateTime getCreatedAt() { + return createdAt; + } + + public List getPosts() { + return posts; + } + + public BlogOwner3 getOwner() { + return owner; + } + + public Temporal.DateTime getUpdatedAt() { + return updatedAt; + } + + private Blog3(String id, String name, Temporal.DateTime createdAt, BlogOwner3 owner) { + this.id = id; + this.name = name; + this.createdAt = createdAt; + this.owner = owner; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if(obj == null || getClass() != obj.getClass()) { + return false; + } else { + Blog3 blog3 = (Blog3) obj; + return ObjectsCompat.equals(getId(), blog3.getId()) && + ObjectsCompat.equals(getName(), blog3.getName()) && + ObjectsCompat.equals(getCreatedAt(), blog3.getCreatedAt()) && + ObjectsCompat.equals(getOwner(), blog3.getOwner()) && + ObjectsCompat.equals(getUpdatedAt(), blog3.getUpdatedAt()); + } + } + + @Override + public int hashCode() { + return new StringBuilder() + .append(getId()) + .append(getName()) + .append(getCreatedAt()) + .append(getOwner()) + .append(getUpdatedAt()) + .toString() + .hashCode(); + } + + @Override + public String toString() { + return new StringBuilder() + .append("Blog3 {") + .append("id=" + String.valueOf(getId()) + ", ") + .append("name=" + String.valueOf(getName()) + ", ") + .append("createdAt=" + String.valueOf(getCreatedAt()) + ", ") + .append("owner=" + String.valueOf(getOwner()) + ", ") + .append("updatedAt=" + String.valueOf(getUpdatedAt())) + .append("}") + .toString(); + } + + public static NameStep builder() { + return new Builder(); + } + + /** + * WARNING: This method should not be used to build an instance of this object for a CREATE mutation. + * This is a convenience method to return an instance of the object with only its ID populated + * to be used in the context of a parameter in a delete mutation or referencing a foreign key + * in a relationship. + * @param id the id of the existing item this instance will represent + * @return an instance of this model with only ID populated + */ + public static Blog3 justId(String id) { + return new Blog3( + id, + null, + null, + null + ); + } + + public CopyOfBuilder copyOfBuilder() { + return new CopyOfBuilder(id, + name, + createdAt, + owner); + } + public interface NameStep { + BuildStep name(String name); + } + + + public interface BuildStep { + Blog3 build(); + BuildStep id(String id); + BuildStep createdAt(Temporal.DateTime createdAt); + BuildStep owner(BlogOwner3 owner); + } + + + public static class Builder implements NameStep, BuildStep { + private String id; + private String name; + private Temporal.DateTime createdAt; + private BlogOwner3 owner; + public Builder() { + + } + + private Builder(String id, String name, Temporal.DateTime createdAt, BlogOwner3 owner) { + this.id = id; + this.name = name; + this.createdAt = createdAt; + this.owner = owner; + } + + @Override + public Blog3 build() { + String id = this.id != null ? this.id : UUID.randomUUID().toString(); + + return new Blog3( + id, + name, + createdAt, + owner); + } + + @Override + public BuildStep name(String name) { + Objects.requireNonNull(name); + this.name = name; + return this; + } + + @Override + public BuildStep createdAt(Temporal.DateTime createdAt) { + this.createdAt = createdAt; + return this; + } + + @Override + public BuildStep owner(BlogOwner3 owner) { + this.owner = owner; + return this; + } + + /** + * @param id id + * @return Current Builder instance, for fluent method chaining + */ + public BuildStep id(String id) { + this.id = id; + return this; + } + } + + + public final class CopyOfBuilder extends Builder { + private CopyOfBuilder(String id, String name, Temporal.DateTime createdAt, BlogOwner3 owner) { + super(id, name, createdAt, owner); + Objects.requireNonNull(name); + } + + @Override + public CopyOfBuilder name(String name) { + return (CopyOfBuilder) super.name(name); + } + + @Override + public CopyOfBuilder createdAt(Temporal.DateTime createdAt) { + return (CopyOfBuilder) super.createdAt(createdAt); + } + + @Override + public CopyOfBuilder owner(BlogOwner3 owner) { + return (CopyOfBuilder) super.owner(owner); + } + } + + + public static class Blog3Identifier extends ModelIdentifier { + private static final long serialVersionUID = 1L; + public Blog3Identifier(String id) { + super(id); + } + } + +} diff --git a/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/BlogOwner3.java b/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/BlogOwner3.java new file mode 100644 index 0000000000..3f615cca6d --- /dev/null +++ b/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/BlogOwner3.java @@ -0,0 +1,216 @@ +package com.amplifyframework.testmodels.commentsblog; + +import static com.amplifyframework.core.model.query.predicate.QueryField.field; + +import androidx.core.util.ObjectsCompat; + +import com.amplifyframework.core.model.Model; +import com.amplifyframework.core.model.ModelIdentifier; +import com.amplifyframework.core.model.annotations.HasMany; +import com.amplifyframework.core.model.annotations.ModelConfig; +import com.amplifyframework.core.model.annotations.ModelField; +import com.amplifyframework.core.model.query.predicate.QueryField; +import com.amplifyframework.core.model.temporal.Temporal; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +/** This is an auto generated class representing the BlogOwner3 type in your schema. */ +@SuppressWarnings("all") +@ModelConfig(pluralName = "BlogOwner3s", type = Model.Type.USER, version = 1) +public final class BlogOwner3 implements Model { + public static final QueryField ID = field("BlogOwner3", "id"); + public static final QueryField NAME = field("BlogOwner3", "name"); + public static final QueryField CREATED_AT = field("BlogOwner3", "createdAt"); + private final @ModelField(targetType="ID", isRequired = true) String id; + private final @ModelField(targetType="String", isRequired = true) String name; + private final @ModelField(targetType="Blog3") @HasMany(associatedWith = "owner", type = Blog3.class) List blogs = null; + private final @ModelField(targetType="Post2") @HasMany(associatedWith = "blogOwner", type = Post2.class) List posts = null; + private final @ModelField(targetType="AWSDateTime") Temporal.DateTime createdAt; + private @ModelField(targetType="AWSDateTime", isReadOnly = true) Temporal.DateTime updatedAt; + /** @deprecated This API is internal to Amplify and should not be used. */ + @Deprecated + public String resolveIdentifier() { + return id; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public List getBlogs() { + return blogs; + } + + public List getPosts() { + return posts; + } + + public Temporal.DateTime getCreatedAt() { + return createdAt; + } + + public Temporal.DateTime getUpdatedAt() { + return updatedAt; + } + + private BlogOwner3(String id, String name, Temporal.DateTime createdAt) { + this.id = id; + this.name = name; + this.createdAt = createdAt; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if(obj == null || getClass() != obj.getClass()) { + return false; + } else { + BlogOwner3 blogOwner3 = (BlogOwner3) obj; + return ObjectsCompat.equals(getId(), blogOwner3.getId()) && + ObjectsCompat.equals(getName(), blogOwner3.getName()) && + ObjectsCompat.equals(getCreatedAt(), blogOwner3.getCreatedAt()) && + ObjectsCompat.equals(getUpdatedAt(), blogOwner3.getUpdatedAt()); + } + } + + @Override + public int hashCode() { + return new StringBuilder() + .append(getId()) + .append(getName()) + .append(getCreatedAt()) + .append(getUpdatedAt()) + .toString() + .hashCode(); + } + + @Override + public String toString() { + return new StringBuilder() + .append("BlogOwner3 {") + .append("id=" + String.valueOf(getId()) + ", ") + .append("name=" + String.valueOf(getName()) + ", ") + .append("createdAt=" + String.valueOf(getCreatedAt()) + ", ") + .append("updatedAt=" + String.valueOf(getUpdatedAt())) + .append("}") + .toString(); + } + + public static NameStep builder() { + return new Builder(); + } + + /** + * WARNING: This method should not be used to build an instance of this object for a CREATE mutation. + * This is a convenience method to return an instance of the object with only its ID populated + * to be used in the context of a parameter in a delete mutation or referencing a foreign key + * in a relationship. + * @param id the id of the existing item this instance will represent + * @return an instance of this model with only ID populated + */ + public static BlogOwner3 justId(String id) { + return new BlogOwner3( + id, + null, + null + ); + } + + public CopyOfBuilder copyOfBuilder() { + return new CopyOfBuilder(id, + name, + createdAt); + } + public interface NameStep { + BuildStep name(String name); + } + + + public interface BuildStep { + BlogOwner3 build(); + BuildStep id(String id); + BuildStep createdAt(Temporal.DateTime createdAt); + } + + + public static class Builder implements NameStep, BuildStep { + private String id; + private String name; + private Temporal.DateTime createdAt; + public Builder() { + + } + + private Builder(String id, String name, Temporal.DateTime createdAt) { + this.id = id; + this.name = name; + this.createdAt = createdAt; + } + + @Override + public BlogOwner3 build() { + String id = this.id != null ? this.id : UUID.randomUUID().toString(); + + return new BlogOwner3( + id, + name, + createdAt); + } + + @Override + public BuildStep name(String name) { + Objects.requireNonNull(name); + this.name = name; + return this; + } + + @Override + public BuildStep createdAt(Temporal.DateTime createdAt) { + this.createdAt = createdAt; + return this; + } + + /** + * @param id id + * @return Current Builder instance, for fluent method chaining + */ + public BuildStep id(String id) { + this.id = id; + return this; + } + } + + + public final class CopyOfBuilder extends Builder { + private CopyOfBuilder(String id, String name, Temporal.DateTime createdAt) { + super(id, name, createdAt); + Objects.requireNonNull(name); + } + + @Override + public CopyOfBuilder name(String name) { + return (CopyOfBuilder) super.name(name); + } + + @Override + public CopyOfBuilder createdAt(Temporal.DateTime createdAt) { + return (CopyOfBuilder) super.createdAt(createdAt); + } + } + + + public static class BlogOwner3Identifier extends ModelIdentifier { + private static final long serialVersionUID = 1L; + public BlogOwner3Identifier(String id) { + super(id); + } + } + +} diff --git a/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/Post2.java b/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/Post2.java new file mode 100644 index 0000000000..6ff1b50f2f --- /dev/null +++ b/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/Post2.java @@ -0,0 +1,328 @@ +package com.amplifyframework.testmodels.commentsblog; + +import static com.amplifyframework.core.model.query.predicate.QueryField.field; + +import androidx.core.util.ObjectsCompat; + +import com.amplifyframework.core.model.Model; +import com.amplifyframework.core.model.ModelIdentifier; +import com.amplifyframework.core.model.annotations.BelongsTo; +import com.amplifyframework.core.model.annotations.Index; +import com.amplifyframework.core.model.annotations.ModelConfig; +import com.amplifyframework.core.model.annotations.ModelField; +import com.amplifyframework.core.model.query.predicate.QueryField; +import com.amplifyframework.core.model.temporal.Temporal; + +import java.util.Objects; +import java.util.UUID; + +/** This is an auto generated class representing the Post2 type in your schema. */ +@SuppressWarnings("all") +@ModelConfig(pluralName = "Post2s", type = Model.Type.USER, version = 1) +@Index(name = "byBlog3", fields = {"blogID"}) +@Index(name = "byBlogOwner3", fields = {"blogOwnerID"}) +public final class Post2 implements Model { + public static final QueryField ID = field("Post2", "id"); + public static final QueryField TITLE = field("Post2", "title"); + public static final QueryField STATUS = field("Post2", "status"); + public static final QueryField RATING = field("Post2", "rating"); + public static final QueryField CREATED_AT = field("Post2", "createdAt"); + public static final QueryField BLOG = field("Post2", "blogID"); + public static final QueryField BLOG_OWNER = field("Post2", "blogOwnerID"); + private final @ModelField(targetType="ID", isRequired = true) String id; + private final @ModelField(targetType="String", isRequired = true) String title; + private final @ModelField(targetType="PostStatus", isRequired = true) PostStatus status; + private final @ModelField(targetType="Int", isRequired = true) Integer rating; + private final @ModelField(targetType="AWSDateTime") Temporal.DateTime createdAt; + private final @ModelField(targetType="Blog3") @BelongsTo(targetName = "blogID", targetNames = {"blogID"}, type = Blog3.class) Blog3 blog; + private final @ModelField(targetType="BlogOwner3") @BelongsTo(targetName = "blogOwnerID", targetNames = {"blogOwnerID"}, type = BlogOwner3.class) BlogOwner3 blogOwner; + private @ModelField(targetType="AWSDateTime", isReadOnly = true) Temporal.DateTime updatedAt; + /** @deprecated This API is internal to Amplify and should not be used. */ + @Deprecated + public String resolveIdentifier() { + return id; + } + + public String getId() { + return id; + } + + public String getTitle() { + return title; + } + + public PostStatus getStatus() { + return status; + } + + public Integer getRating() { + return rating; + } + + public Temporal.DateTime getCreatedAt() { + return createdAt; + } + + public Blog3 getBlog() { + return blog; + } + + public BlogOwner3 getBlogOwner() { + return blogOwner; + } + + public Temporal.DateTime getUpdatedAt() { + return updatedAt; + } + + private Post2(String id, String title, PostStatus status, Integer rating, Temporal.DateTime createdAt, Blog3 blog, BlogOwner3 blogOwner) { + this.id = id; + this.title = title; + this.status = status; + this.rating = rating; + this.createdAt = createdAt; + this.blog = blog; + this.blogOwner = blogOwner; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if(obj == null || getClass() != obj.getClass()) { + return false; + } else { + Post2 post2 = (Post2) obj; + return ObjectsCompat.equals(getId(), post2.getId()) && + ObjectsCompat.equals(getTitle(), post2.getTitle()) && + ObjectsCompat.equals(getStatus(), post2.getStatus()) && + ObjectsCompat.equals(getRating(), post2.getRating()) && + ObjectsCompat.equals(getCreatedAt(), post2.getCreatedAt()) && + ObjectsCompat.equals(getBlog(), post2.getBlog()) && + ObjectsCompat.equals(getBlogOwner(), post2.getBlogOwner()) && + ObjectsCompat.equals(getUpdatedAt(), post2.getUpdatedAt()); + } + } + + @Override + public int hashCode() { + return new StringBuilder() + .append(getId()) + .append(getTitle()) + .append(getStatus()) + .append(getRating()) + .append(getCreatedAt()) + .append(getBlog()) + .append(getBlogOwner()) + .append(getUpdatedAt()) + .toString() + .hashCode(); + } + + @Override + public String toString() { + return new StringBuilder() + .append("Post2 {") + .append("id=" + String.valueOf(getId()) + ", ") + .append("title=" + String.valueOf(getTitle()) + ", ") + .append("status=" + String.valueOf(getStatus()) + ", ") + .append("rating=" + String.valueOf(getRating()) + ", ") + .append("createdAt=" + String.valueOf(getCreatedAt()) + ", ") + .append("blog=" + String.valueOf(getBlog()) + ", ") + .append("blogOwner=" + String.valueOf(getBlogOwner()) + ", ") + .append("updatedAt=" + String.valueOf(getUpdatedAt())) + .append("}") + .toString(); + } + + public static TitleStep builder() { + return new Builder(); + } + + /** + * WARNING: This method should not be used to build an instance of this object for a CREATE mutation. + * This is a convenience method to return an instance of the object with only its ID populated + * to be used in the context of a parameter in a delete mutation or referencing a foreign key + * in a relationship. + * @param id the id of the existing item this instance will represent + * @return an instance of this model with only ID populated + */ + public static Post2 justId(String id) { + return new Post2( + id, + null, + null, + null, + null, + null, + null + ); + } + + public CopyOfBuilder copyOfBuilder() { + return new CopyOfBuilder(id, + title, + status, + rating, + createdAt, + blog, + blogOwner); + } + public interface TitleStep { + StatusStep title(String title); + } + + + public interface StatusStep { + RatingStep status(PostStatus status); + } + + + public interface RatingStep { + BuildStep rating(Integer rating); + } + + + public interface BuildStep { + Post2 build(); + BuildStep id(String id); + BuildStep createdAt(Temporal.DateTime createdAt); + BuildStep blog(Blog3 blog); + BuildStep blogOwner(BlogOwner3 blogOwner); + } + + + public static class Builder implements TitleStep, StatusStep, RatingStep, BuildStep { + private String id; + private String title; + private PostStatus status; + private Integer rating; + private Temporal.DateTime createdAt; + private Blog3 blog; + private BlogOwner3 blogOwner; + public Builder() { + + } + + private Builder(String id, String title, PostStatus status, Integer rating, Temporal.DateTime createdAt, Blog3 blog, BlogOwner3 blogOwner) { + this.id = id; + this.title = title; + this.status = status; + this.rating = rating; + this.createdAt = createdAt; + this.blog = blog; + this.blogOwner = blogOwner; + } + + @Override + public Post2 build() { + String id = this.id != null ? this.id : UUID.randomUUID().toString(); + + return new Post2( + id, + title, + status, + rating, + createdAt, + blog, + blogOwner); + } + + @Override + public StatusStep title(String title) { + Objects.requireNonNull(title); + this.title = title; + return this; + } + + @Override + public RatingStep status(PostStatus status) { + Objects.requireNonNull(status); + this.status = status; + return this; + } + + @Override + public BuildStep rating(Integer rating) { + Objects.requireNonNull(rating); + this.rating = rating; + return this; + } + + @Override + public BuildStep createdAt(Temporal.DateTime createdAt) { + this.createdAt = createdAt; + return this; + } + + @Override + public BuildStep blog(Blog3 blog) { + this.blog = blog; + return this; + } + + @Override + public BuildStep blogOwner(BlogOwner3 blogOwner) { + this.blogOwner = blogOwner; + return this; + } + + /** + * @param id id + * @return Current Builder instance, for fluent method chaining + */ + public BuildStep id(String id) { + this.id = id; + return this; + } + } + + + public final class CopyOfBuilder extends Builder { + private CopyOfBuilder(String id, String title, PostStatus status, Integer rating, Temporal.DateTime createdAt, Blog3 blog, BlogOwner3 blogOwner) { + super(id, title, status, rating, createdAt, blog, blogOwner); + Objects.requireNonNull(title); + Objects.requireNonNull(status); + Objects.requireNonNull(rating); + } + + @Override + public CopyOfBuilder title(String title) { + return (CopyOfBuilder) super.title(title); + } + + @Override + public CopyOfBuilder status(PostStatus status) { + return (CopyOfBuilder) super.status(status); + } + + @Override + public CopyOfBuilder rating(Integer rating) { + return (CopyOfBuilder) super.rating(rating); + } + + @Override + public CopyOfBuilder createdAt(Temporal.DateTime createdAt) { + return (CopyOfBuilder) super.createdAt(createdAt); + } + + @Override + public CopyOfBuilder blog(Blog3 blog) { + return (CopyOfBuilder) super.blog(blog); + } + + @Override + public CopyOfBuilder blogOwner(BlogOwner3 blogOwner) { + return (CopyOfBuilder) super.blogOwner(blogOwner); + } + } + + + public static class Post2Identifier extends ModelIdentifier { + private static final long serialVersionUID = 1L; + public Post2Identifier(String id) { + super(id); + } + } + +} diff --git a/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/schema.graphql b/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/schema.graphql index 9037fd789b..b37df63600 100644 --- a/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/schema.graphql +++ b/testmodels/src/main/java/com/amplifyframework/testmodels/commentsblog/schema.graphql @@ -79,3 +79,33 @@ type BlogOwner2 @model createdAt: AWSDateTime } +type BlogOwner3 @model +{ + id: ID! + name: String! + blogs: [Blog3] @hasMany(indexName: "byBlogOwner3", fields: ["id"]) + posts: [Post2] @hasMany(indexName: "byBlogOwner3", fields: ["id"]) + createdAt: AWSDateTime +} + +type Blog3 @model { + id: ID! + blogOwnerID: ID! @index(name: "byBlogOwner3") + name: String! + createdAt: AWSDateTime + posts: [Post2] @hasMany(indexName: "byBlog3", fields: ["id"]) + owner: BlogOwner3 @belongsTo(fields: ["blogOwnerID"]) +} + +type Post2 @model { + id: ID! + blogID: ID! @index(name: "byBlog3") + blogOwnerID: ID! @index(name: "byBlogOwner3") + title: String! + status: PostStatus! + rating: Int! + createdAt: AWSDateTime + blog: Blog3 @belongsTo(fields: ["blogID"]) + blogOwner: BlogOwner3 @belongsTo(fields: ["blogOwnerID"]) +} + diff --git a/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/AmplifyModelProvider.java b/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/AmplifyModelProvider.java index 9ccc8481c1..93f8a88660 100644 --- a/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/AmplifyModelProvider.java +++ b/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/AmplifyModelProvider.java @@ -49,7 +49,9 @@ public static AmplifyModelProvider getInstance() { @Override public Set> models() { final Set> modifiableSet = new HashSet<>( - Arrays.>asList(Blog.class, Post.class, User.class, Comment.class, Item.class, IntModelWithIdentifier.class, StringModelWithIdentifier.class) + Arrays.>asList(Blog.class, Post.class, User.class, Comment.class, Item.class, + IntModelWithIdentifier.class, StringModelWithIdentifier.class, BlogOwnerCPK.class, + BlogCPK.class, PostCPK.class) ); return Immutable.of(modifiableSet); diff --git a/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/BlogCPK.java b/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/BlogCPK.java new file mode 100644 index 0000000000..055e78cfbc --- /dev/null +++ b/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/BlogCPK.java @@ -0,0 +1,235 @@ +package com.amplifyframework.testmodels.cpk; + +import static com.amplifyframework.core.model.query.predicate.QueryField.field; + +import androidx.core.util.ObjectsCompat; + +import com.amplifyframework.core.model.Model; +import com.amplifyframework.core.model.ModelIdentifier; +import com.amplifyframework.core.model.annotations.BelongsTo; +import com.amplifyframework.core.model.annotations.HasMany; +import com.amplifyframework.core.model.annotations.Index; +import com.amplifyframework.core.model.annotations.ModelConfig; +import com.amplifyframework.core.model.annotations.ModelField; +import com.amplifyframework.core.model.query.predicate.QueryField; +import com.amplifyframework.core.model.temporal.Temporal; + +import java.util.List; +import java.util.Objects; + +/** This is an auto generated class representing the BlogCPK type in your schema. */ +@SuppressWarnings("all") +@ModelConfig(pluralName = "BlogCPKS", type = Model.Type.USER, version = 1) +@Index(name = "undefined", fields = {"id","name"}) +@Index(name = "byBlogOwner3", fields = {"blogOwnerID"}) +public final class BlogCPK implements Model { + public static final QueryField ID = field("BlogCPK", "id"); + public static final QueryField NAME = field("BlogCPK", "name"); + public static final QueryField CREATED_AT = field("BlogCPK", "createdAt"); + public static final QueryField OWNER = field("BlogCPK", "blogOwnerID"); + private final @ModelField(targetType="ID", isRequired = true) String id; + private final @ModelField(targetType="String", isRequired = true) String name; + private final @ModelField(targetType="AWSDateTime") Temporal.DateTime createdAt; + private final @ModelField(targetType="PostCPK") @HasMany(associatedWith = "blog", type = PostCPK.class) List posts = null; + private final @ModelField(targetType="BlogOwnerCPK") @BelongsTo(targetName = "blogOwnerID", targetNames = {"blogOwnerID"}, type = BlogOwnerCPK.class) BlogOwnerCPK owner; + private @ModelField(targetType="AWSDateTime", isReadOnly = true) Temporal.DateTime updatedAt; + private BlogCPKIdentifier blogCPKIdentifier; + /** @deprecated This API is internal to Amplify and should not be used. */ + @Deprecated + public BlogCPKIdentifier resolveIdentifier() { + if (blogCPKIdentifier == null) { + this.blogCPKIdentifier = new BlogCPKIdentifier(id, name); + } + return blogCPKIdentifier; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public Temporal.DateTime getCreatedAt() { + return createdAt; + } + + public List getPosts() { + return posts; + } + + public BlogOwnerCPK getOwner() { + return owner; + } + + public Temporal.DateTime getUpdatedAt() { + return updatedAt; + } + + private BlogCPK(String id, String name, Temporal.DateTime createdAt, BlogOwnerCPK owner) { + this.id = id; + this.name = name; + this.createdAt = createdAt; + this.owner = owner; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if(obj == null || getClass() != obj.getClass()) { + return false; + } else { + BlogCPK blogCpk = (BlogCPK) obj; + return ObjectsCompat.equals(getId(), blogCpk.getId()) && + ObjectsCompat.equals(getName(), blogCpk.getName()) && + ObjectsCompat.equals(getCreatedAt(), blogCpk.getCreatedAt()) && + ObjectsCompat.equals(getOwner(), blogCpk.getOwner()) && + ObjectsCompat.equals(getUpdatedAt(), blogCpk.getUpdatedAt()); + } + } + + @Override + public int hashCode() { + return new StringBuilder() + .append(getId()) + .append(getName()) + .append(getCreatedAt()) + .append(getOwner()) + .append(getUpdatedAt()) + .toString() + .hashCode(); + } + + @Override + public String toString() { + return new StringBuilder() + .append("BlogCPK {") + .append("id=" + String.valueOf(getId()) + ", ") + .append("name=" + String.valueOf(getName()) + ", ") + .append("createdAt=" + String.valueOf(getCreatedAt()) + ", ") + .append("owner=" + String.valueOf(getOwner()) + ", ") + .append("updatedAt=" + String.valueOf(getUpdatedAt())) + .append("}") + .toString(); + } + + public static IdStep builder() { + return new Builder(); + } + + public CopyOfBuilder copyOfBuilder() { + return new CopyOfBuilder(id, + name, + createdAt, + owner); + } + public interface IdStep { + NameStep id(String id); + } + + + public interface NameStep { + BuildStep name(String name); + } + + + public interface BuildStep { + BlogCPK build(); + BuildStep createdAt(Temporal.DateTime createdAt); + BuildStep owner(BlogOwnerCPK owner); + } + + + public static class Builder implements IdStep, NameStep, BuildStep { + private String id; + private String name; + private Temporal.DateTime createdAt; + private BlogOwnerCPK owner; + public Builder() { + + } + + private Builder(String id, String name, Temporal.DateTime createdAt, BlogOwnerCPK owner) { + this.id = id; + this.name = name; + this.createdAt = createdAt; + this.owner = owner; + } + + @Override + public BlogCPK build() { + + return new BlogCPK( + id, + name, + createdAt, + owner); + } + + @Override + public NameStep id(String id) { + Objects.requireNonNull(id); + this.id = id; + return this; + } + + @Override + public BuildStep name(String name) { + Objects.requireNonNull(name); + this.name = name; + return this; + } + + @Override + public BuildStep createdAt(Temporal.DateTime createdAt) { + this.createdAt = createdAt; + return this; + } + + @Override + public BuildStep owner(BlogOwnerCPK owner) { + this.owner = owner; + return this; + } + } + + + public final class CopyOfBuilder extends Builder { + private CopyOfBuilder(String id, String name, Temporal.DateTime createdAt, BlogOwnerCPK owner) { + super(id, name, createdAt, owner); + Objects.requireNonNull(id); + Objects.requireNonNull(name); + } + + @Override + public CopyOfBuilder id(String id) { + return (CopyOfBuilder) super.id(id); + } + + @Override + public CopyOfBuilder name(String name) { + return (CopyOfBuilder) super.name(name); + } + + @Override + public CopyOfBuilder createdAt(Temporal.DateTime createdAt) { + return (CopyOfBuilder) super.createdAt(createdAt); + } + + @Override + public CopyOfBuilder owner(BlogOwnerCPK owner) { + return (CopyOfBuilder) super.owner(owner); + } + } + + + public static class BlogCPKIdentifier extends ModelIdentifier { + private static final long serialVersionUID = 1L; + public BlogCPKIdentifier(String id, String name) { + super(id, name); + } + } + +} diff --git a/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/BlogOwnerCPK.java b/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/BlogOwnerCPK.java new file mode 100644 index 0000000000..4dc0ed4bdf --- /dev/null +++ b/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/BlogOwnerCPK.java @@ -0,0 +1,212 @@ +package com.amplifyframework.testmodels.cpk; + +import static com.amplifyframework.core.model.query.predicate.QueryField.field; + +import androidx.core.util.ObjectsCompat; + +import com.amplifyframework.core.model.Model; +import com.amplifyframework.core.model.ModelIdentifier; +import com.amplifyframework.core.model.annotations.HasMany; +import com.amplifyframework.core.model.annotations.Index; +import com.amplifyframework.core.model.annotations.ModelConfig; +import com.amplifyframework.core.model.annotations.ModelField; +import com.amplifyframework.core.model.query.predicate.QueryField; +import com.amplifyframework.core.model.temporal.Temporal; + +import java.util.List; +import java.util.Objects; + +/** This is an auto generated class representing the BlogOwnerCPK type in your schema. */ +@SuppressWarnings("all") +@ModelConfig(pluralName = "BlogOwnerCPKS", type = Model.Type.USER, version = 1) +@Index(name = "undefined", fields = {"id","name"}) +public final class BlogOwnerCPK implements Model { + public static final QueryField ID = field("BlogOwnerCPK", "id"); + public static final QueryField NAME = field("BlogOwnerCPK", "name"); + public static final QueryField CREATED_AT = field("BlogOwnerCPK", "createdAt"); + private final @ModelField(targetType="ID", isRequired = true) String id; + private final @ModelField(targetType="String", isRequired = true) String name; + private final @ModelField(targetType="BlogCPK") @HasMany(associatedWith = "owner", type = BlogCPK.class) List blogs = null; + private final @ModelField(targetType="PostCPK") @HasMany(associatedWith = "blogOwner", type = PostCPK.class) List posts = null; + private final @ModelField(targetType="AWSDateTime") Temporal.DateTime createdAt; + private @ModelField(targetType="AWSDateTime", isReadOnly = true) Temporal.DateTime updatedAt; + private BlogOwnerCPKIdentifier blogOwnerCPKIdentifier; + /** @deprecated This API is internal to Amplify and should not be used. */ + @Deprecated + public BlogOwnerCPKIdentifier resolveIdentifier() { + if (blogOwnerCPKIdentifier == null) { + this.blogOwnerCPKIdentifier = new BlogOwnerCPKIdentifier(id, name); + } + return blogOwnerCPKIdentifier; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public List getBlogs() { + return blogs; + } + + public List getPosts() { + return posts; + } + + public Temporal.DateTime getCreatedAt() { + return createdAt; + } + + public Temporal.DateTime getUpdatedAt() { + return updatedAt; + } + + private BlogOwnerCPK(String id, String name, Temporal.DateTime createdAt) { + this.id = id; + this.name = name; + this.createdAt = createdAt; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if(obj == null || getClass() != obj.getClass()) { + return false; + } else { + BlogOwnerCPK blogOwnerCpk = (BlogOwnerCPK) obj; + return ObjectsCompat.equals(getId(), blogOwnerCpk.getId()) && + ObjectsCompat.equals(getName(), blogOwnerCpk.getName()) && + ObjectsCompat.equals(getCreatedAt(), blogOwnerCpk.getCreatedAt()) && + ObjectsCompat.equals(getUpdatedAt(), blogOwnerCpk.getUpdatedAt()); + } + } + + @Override + public int hashCode() { + return new StringBuilder() + .append(getId()) + .append(getName()) + .append(getCreatedAt()) + .append(getUpdatedAt()) + .toString() + .hashCode(); + } + + @Override + public String toString() { + return new StringBuilder() + .append("BlogOwnerCPK {") + .append("id=" + String.valueOf(getId()) + ", ") + .append("name=" + String.valueOf(getName()) + ", ") + .append("createdAt=" + String.valueOf(getCreatedAt()) + ", ") + .append("updatedAt=" + String.valueOf(getUpdatedAt())) + .append("}") + .toString(); + } + + public static IdStep builder() { + return new Builder(); + } + + public CopyOfBuilder copyOfBuilder() { + return new CopyOfBuilder(id, + name, + createdAt); + } + public interface IdStep { + NameStep id(String id); + } + + + public interface NameStep { + BuildStep name(String name); + } + + + public interface BuildStep { + BlogOwnerCPK build(); + BuildStep createdAt(Temporal.DateTime createdAt); + } + + + public static class Builder implements IdStep, NameStep, BuildStep { + private String id; + private String name; + private Temporal.DateTime createdAt; + public Builder() { + + } + + private Builder(String id, String name, Temporal.DateTime createdAt) { + this.id = id; + this.name = name; + this.createdAt = createdAt; + } + + @Override + public BlogOwnerCPK build() { + + return new BlogOwnerCPK( + id, + name, + createdAt); + } + + @Override + public NameStep id(String id) { + Objects.requireNonNull(id); + this.id = id; + return this; + } + + @Override + public BuildStep name(String name) { + Objects.requireNonNull(name); + this.name = name; + return this; + } + + @Override + public BuildStep createdAt(Temporal.DateTime createdAt) { + this.createdAt = createdAt; + return this; + } + } + + + public final class CopyOfBuilder extends Builder { + private CopyOfBuilder(String id, String name, Temporal.DateTime createdAt) { + super(id, name, createdAt); + Objects.requireNonNull(id); + Objects.requireNonNull(name); + } + + @Override + public CopyOfBuilder id(String id) { + return (CopyOfBuilder) super.id(id); + } + + @Override + public CopyOfBuilder name(String name) { + return (CopyOfBuilder) super.name(name); + } + + @Override + public CopyOfBuilder createdAt(Temporal.DateTime createdAt) { + return (CopyOfBuilder) super.createdAt(createdAt); + } + } + + + public static class BlogOwnerCPKIdentifier extends ModelIdentifier { + private static final long serialVersionUID = 1L; + public BlogOwnerCPKIdentifier(String id, String name) { + super(id, name); + } + } + +} diff --git a/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/PostCPK.java b/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/PostCPK.java new file mode 100644 index 0000000000..2ebe2ae973 --- /dev/null +++ b/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/PostCPK.java @@ -0,0 +1,287 @@ +package com.amplifyframework.testmodels.cpk; + +import static com.amplifyframework.core.model.query.predicate.QueryField.field; + +import androidx.core.util.ObjectsCompat; + +import com.amplifyframework.core.model.Model; +import com.amplifyframework.core.model.ModelIdentifier; +import com.amplifyframework.core.model.annotations.BelongsTo; +import com.amplifyframework.core.model.annotations.Index; +import com.amplifyframework.core.model.annotations.ModelConfig; +import com.amplifyframework.core.model.annotations.ModelField; +import com.amplifyframework.core.model.query.predicate.QueryField; +import com.amplifyframework.core.model.temporal.Temporal; + +import java.util.Objects; + +/** This is an auto generated class representing the PostCPK type in your schema. */ +@SuppressWarnings("all") +@ModelConfig(pluralName = "PostCPKS", type = Model.Type.USER, version = 1) +@Index(name = "undefined", fields = {"id","title"}) +@Index(name = "byBlog3", fields = {"blogID"}) +@Index(name = "byBlogOwner3", fields = {"blogOwnerID"}) +public final class PostCPK implements Model { + public static final QueryField ID = field("PostCPK", "id"); + public static final QueryField TITLE = field("PostCPK", "title"); + public static final QueryField RATING = field("PostCPK", "rating"); + public static final QueryField CREATED_AT = field("PostCPK", "createdAt"); + public static final QueryField BLOG = field("PostCPK", "blogID"); + public static final QueryField BLOG_OWNER = field("PostCPK", "blogOwnerID"); + private final @ModelField(targetType="ID", isRequired = true) String id; + private final @ModelField(targetType="String", isRequired = true) String title; + private final @ModelField(targetType="Int", isRequired = true) Integer rating; + private final @ModelField(targetType="AWSDateTime") Temporal.DateTime createdAt; + private final @ModelField(targetType="BlogCPK") @BelongsTo(targetName = "blogID", targetNames = {"blogID"}, type = BlogCPK.class) BlogCPK blog; + private final @ModelField(targetType="BlogOwnerCPK") @BelongsTo(targetName = "blogOwnerID", targetNames = {"blogOwnerID"}, type = BlogOwnerCPK.class) BlogOwnerCPK blogOwner; + private @ModelField(targetType="AWSDateTime", isReadOnly = true) Temporal.DateTime updatedAt; + private PostCPKIdentifier postCPKIdentifier; + /** @deprecated This API is internal to Amplify and should not be used. */ + @Deprecated + public PostCPKIdentifier resolveIdentifier() { + if (postCPKIdentifier == null) { + this.postCPKIdentifier = new PostCPKIdentifier(id, title); + } + return postCPKIdentifier; + } + + public String getId() { + return id; + } + + public String getTitle() { + return title; + } + + public Integer getRating() { + return rating; + } + + public Temporal.DateTime getCreatedAt() { + return createdAt; + } + + public BlogCPK getBlog() { + return blog; + } + + public BlogOwnerCPK getBlogOwner() { + return blogOwner; + } + + public Temporal.DateTime getUpdatedAt() { + return updatedAt; + } + + private PostCPK(String id, String title, Integer rating, Temporal.DateTime createdAt, BlogCPK blog, BlogOwnerCPK blogOwner) { + this.id = id; + this.title = title; + this.rating = rating; + this.createdAt = createdAt; + this.blog = blog; + this.blogOwner = blogOwner; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if(obj == null || getClass() != obj.getClass()) { + return false; + } else { + PostCPK postCpk = (PostCPK) obj; + return ObjectsCompat.equals(getId(), postCpk.getId()) && + ObjectsCompat.equals(getTitle(), postCpk.getTitle()) && + ObjectsCompat.equals(getRating(), postCpk.getRating()) && + ObjectsCompat.equals(getCreatedAt(), postCpk.getCreatedAt()) && + ObjectsCompat.equals(getBlog(), postCpk.getBlog()) && + ObjectsCompat.equals(getBlogOwner(), postCpk.getBlogOwner()) && + ObjectsCompat.equals(getUpdatedAt(), postCpk.getUpdatedAt()); + } + } + + @Override + public int hashCode() { + return new StringBuilder() + .append(getId()) + .append(getTitle()) + .append(getRating()) + .append(getCreatedAt()) + .append(getBlog()) + .append(getBlogOwner()) + .append(getUpdatedAt()) + .toString() + .hashCode(); + } + + @Override + public String toString() { + return new StringBuilder() + .append("PostCPK {") + .append("id=" + String.valueOf(getId()) + ", ") + .append("title=" + String.valueOf(getTitle()) + ", ") + .append("rating=" + String.valueOf(getRating()) + ", ") + .append("createdAt=" + String.valueOf(getCreatedAt()) + ", ") + .append("blog=" + String.valueOf(getBlog()) + ", ") + .append("blogOwner=" + String.valueOf(getBlogOwner()) + ", ") + .append("updatedAt=" + String.valueOf(getUpdatedAt())) + .append("}") + .toString(); + } + + public static IdStep builder() { + return new Builder(); + } + + public CopyOfBuilder copyOfBuilder() { + return new CopyOfBuilder(id, + title, + rating, + createdAt, + blog, + blogOwner); + } + public interface IdStep { + TitleStep id(String id); + } + + + public interface TitleStep { + RatingStep title(String title); + } + + + public interface RatingStep { + BuildStep rating(Integer rating); + } + + + public interface BuildStep { + PostCPK build(); + BuildStep createdAt(Temporal.DateTime createdAt); + BuildStep blog(BlogCPK blog); + BuildStep blogOwner(BlogOwnerCPK blogOwner); + } + + + public static class Builder implements IdStep, TitleStep, RatingStep, BuildStep { + private String id; + private String title; + private Integer rating; + private Temporal.DateTime createdAt; + private BlogCPK blog; + private BlogOwnerCPK blogOwner; + public Builder() { + + } + + private Builder(String id, String title, Integer rating, Temporal.DateTime createdAt, BlogCPK blog, BlogOwnerCPK blogOwner) { + this.id = id; + this.title = title; + this.rating = rating; + this.createdAt = createdAt; + this.blog = blog; + this.blogOwner = blogOwner; + } + + @Override + public PostCPK build() { + + return new PostCPK( + id, + title, + rating, + createdAt, + blog, + blogOwner); + } + + @Override + public TitleStep id(String id) { + Objects.requireNonNull(id); + this.id = id; + return this; + } + + @Override + public RatingStep title(String title) { + Objects.requireNonNull(title); + this.title = title; + return this; + } + + @Override + public BuildStep rating(Integer rating) { + Objects.requireNonNull(rating); + this.rating = rating; + return this; + } + + @Override + public BuildStep createdAt(Temporal.DateTime createdAt) { + this.createdAt = createdAt; + return this; + } + + @Override + public BuildStep blog(BlogCPK blog) { + this.blog = blog; + return this; + } + + @Override + public BuildStep blogOwner(BlogOwnerCPK blogOwner) { + this.blogOwner = blogOwner; + return this; + } + } + + + public final class CopyOfBuilder extends Builder { + private CopyOfBuilder(String id, String title, Integer rating, Temporal.DateTime createdAt, BlogCPK blog, BlogOwnerCPK blogOwner) { + super(id, title, rating, createdAt, blog, blogOwner); + Objects.requireNonNull(id); + Objects.requireNonNull(title); + Objects.requireNonNull(rating); + } + + @Override + public CopyOfBuilder id(String id) { + return (CopyOfBuilder) super.id(id); + } + + @Override + public CopyOfBuilder title(String title) { + return (CopyOfBuilder) super.title(title); + } + + @Override + public CopyOfBuilder rating(Integer rating) { + return (CopyOfBuilder) super.rating(rating); + } + + @Override + public CopyOfBuilder createdAt(Temporal.DateTime createdAt) { + return (CopyOfBuilder) super.createdAt(createdAt); + } + + @Override + public CopyOfBuilder blog(BlogCPK blog) { + return (CopyOfBuilder) super.blog(blog); + } + + @Override + public CopyOfBuilder blogOwner(BlogOwnerCPK blogOwner) { + return (CopyOfBuilder) super.blogOwner(blogOwner); + } + } + + + public static class PostCPKIdentifier extends ModelIdentifier { + private static final long serialVersionUID = 1L; + public PostCPKIdentifier(String id, String title) { + super(id, title); + } + } + +} diff --git a/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/schema.graphql b/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/schema.graphql index c386e79ebe..4ddaa791eb 100644 --- a/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/schema.graphql +++ b/testmodels/src/main/java/com/amplifyframework/testmodels/cpk/schema.graphql @@ -42,3 +42,33 @@ type IntModelWithIdentifier @model { type StringModelWithIdentifier @model { customKey: String! @primaryKey } + +type BlogOwnerCPK @model +{ + id: ID! @primaryKey(sortKeyFields: ["name"]) + name: String! + blogs: [BlogCPK] @hasMany(indexName: "byBlogOwner3", fields: ["id"]) + posts: [PostCPK] @hasMany(indexName: "byBlogOwner3", fields: ["id"]) + createdAt: AWSDateTime +} + +type BlogCPK @model { + id: ID! @primaryKey(sortKeyFields: ["name"]) + blogOwnerID: ID! @index(name: "byBlogOwner3") + name: String! + createdAt: AWSDateTime + posts: [PostCPK] @hasMany(indexName: "byBlog3", fields: ["id"]) + owner: BlogOwnerCPK @belongsTo(fields: ["blogOwnerID"]) +} + +type PostCPK @model { + id: ID! @primaryKey(sortKeyFields: ["title"]) + blogID: ID! @index(name: "byBlog3") + blogOwnerID: ID! @index(name: "byBlogOwner3") + title: String! + rating: Int! + createdAt: AWSDateTime + blog: BlogCPK @belongsTo(fields: ["blogID"]) + blogOwner: BlogOwnerCPK @belongsTo(fields: ["blogOwnerID"]) +} +