diff --git a/cspell.json b/cspell.json index c2c0df0f66b..49baa6ee1e1 100644 --- a/cspell.json +++ b/cspell.json @@ -1617,7 +1617,8 @@ "webauthn", "knowledgebases", "rehype", - "assetlinks" + "assetlinks", + "AMPLIFYRULES" ], "flagWords": ["hte", "full-stack", "Full-stack", "Full-Stack", "sudo"], "patterns": [ diff --git a/public/images/gen2/q-developer/authentication.md b/public/images/gen2/q-developer/authentication.md new file mode 100644 index 00000000000..2ec836d9794 --- /dev/null +++ b/public/images/gen2/q-developer/authentication.md @@ -0,0 +1,219 @@ +# AMPLIFYRULES + +- RULES THAT SHOULD BE ADHERED TO THE LAST WORD. + + 1. EXTERNAL PROVIDERS THAT ARE AVAILABLE ARE LISTED BELOW IN THE EXAMPLE, DON'T CHANGE THE NAMING CONVENTION WHILE USING THOSE IN THE CODE GENERATION. + 2. DON'T FORGET TO IMPORT SECRET FOR ANY AUTHENTICATION BASED QUESTION. + + ```typescript + import { defineAuth, secret } from "@aws-amplify/backend"; + ``` + + 3. CALLBACK AND LOGOUT URLS SHOULD BE INSIDE THE "EXTERNALPROVIDERS" OBJECT. + 4. WHILE ADDING THE CUSTOM ATTRIBUTES, IF THE ATTRIBUTE YOU ARE ADDING DOESNT BELONG TO THE STANDARD USER ATTRIBUTES LIST THEN ADD IT AS A CUSTOM ATTRIBUTE LIKE THIS "CUSTOM:ATTRIBUTE_NAME" AND THIS DOESN'T SUPPORT "REQUIRED" FIELD SO IGNORE IT WHILE GENERATING THE ANSWER. + 5. WHILE ADDING THE CUSTOM ATTRIBUTES, MAKE SURE TO ALWAYS ADD THE "DATATYPE" FIELD AS IT IS A REQUIRED FIELD. + 6. STATNDARD ATTIBUTES THAT ARE ALLOWED: `familyName`, `giveName`, `middleName`, `nickname`, `preferredUsername`, `profile`, `profilePicture`, `website`, `gender`, `birthdate`, `zoneinfo`, `locale`, `updatedAt`, `address`, `email`, `phoneNumber`, `sub`. THE `userAttributes` ARE SUPPOSED TO BE OUTSIDE THE `loginWith` OBJECT + + 7. THE FOLLOWING IS THE REQUIRED SYNTAX FOR `externalProviders`. ONLY THE FOUR LISTED PROVIDERS BELOW ARE SUPPORTED: + + ```typescript + loginWith:{ + //loginMethods + externalProviders: { + google: { + + }, + signInWithApple: { + }, + loginWithAmazon: { + + }, + facebook: { + }, + callbackUrls: [ + // Callback URLs should be included inside the `externalProviders` object only, as per rule. + + ], + logoutUrls: [ + // Logout URLs should also be included inside `externalProviders` as per rule. + + ], + }, + } + ``` + + 8. THE `userAttributes` ARE SUPPOSED TO BE OUTSIDE THE `loginWith` OBJECT + `// Example configuration for user attributes and login methods +loginWith: { + // Specify login methods separately from user attributes, ONLY MENTION THE LOGINMETHODS THAT ARE SUPPOSED TO BE TRUE + email: true, // Enable login with email + phone: false, // Disable login with phone number +},userAttributes: { + email: true, // Attribute set as required + phoneNumber: false, // Attribute not required + givenName: true, // Attribute set as required +},` + 9. THIS IS THE WAY TO SETUP THE MULTIFACTOR AUTHENTHICATION + export const auth = defineAuth({ + loginWith: { + email: true, + phone: true, + }, + multifactor: { + mode: "OPTIONAL", + sms: true, + totp: false, + }, + // IMPORTANT! THE LOGIC TO RESOLVE THIS VALUE CANNOT DETERMINE WHETHER EMAIL MFA IS ENABLED WHEN OVERRIDING THE RESOURCE. + // BE SURE TO PICK A RECOVERY OPTION APPROPRIATE FOR YOUR APPLICATION. + accountRecovery: "EMAIL_AND_PHONE_WITHOUT_MFA", + senders: { + email: { + fromEmail: "registrations@example.com", + }, + }, + }) + 10. DON'T ADD THE `passwordPolicy` IN THE AUTH/RESOURCE.TS FILE. + 11. THE `username` SHOULD BE REPLACED WITH `userAttributes` WHEN REQUESTED TO SETUP USERNAME AS A LOGIN METHOD AS USERNAME IS NOT A VALID LOGIN METHOD ON DEFINEAUTH, FOR EXAMPLE: + + ```typescript + userAttributes: { + preferredUsername: { + mutable: true, + required: false + } + } + ``` + + 12. `loginWith` SUPPORTS ONLY TWO METHODS THAT IS `email` and `phone`. THERE IS NO `username` attribute to that. + 13. THE `callbackUrls` AND `logoutUrls` SHOULD ONLY BE MENTIONED ONCE AS MENTIONED IN RULE #7 AND NOT FOR EACH EXTERNAL PROVIDER + +```typescript +import { defineAuth, secret } from "@aws-amplify/backend"; + +export const auth = defineAuth({ + // Login Methods Configuration + loginWith: { + // Only email and phone are supported as login methods + email: true, + phone: true, + + // External Providers Configuration - all providers shown with required fields + externalProviders: { + // Google Authentication + google: { + clientId: secret("GOOGLE_CLIENT_ID"), + clientSecret: secret("GOOGLE_CLIENT_SECRET"), + }, + // Sign in with Apple + signInWithApple: { + clientId: secret("SIWA_CLIENT_ID"), + keyId: secret("SIWA_KEY_ID"), + privateKey: secret("SIWA_PRIVATE_KEY"), + teamId: secret("SIWA_TEAM_ID"), + }, + // Login with Amazon + loginWithAmazon: { + clientId: secret("LOGINWITHAMAZON_CLIENT_ID"), + clientSecret: secret("LOGINWITHAMAZON_CLIENT_SECRET"), + }, + // Facebook Authentication + facebook: { + clientId: secret("FACEBOOK_CLIENT_ID"), + clientSecret: secret("FACEBOOK_CLIENT_SECRET"), + }, + // Callback and logout URLs must be inside externalProviders + callbackUrls: [ + "http://localhost:3000/profile", + "https://mywebsite.com/profile", + ], + logoutUrls: ["http://localhost:3000/", "https://mywebsite.com"], + }, + }, + + // User Attributes Configuration - outside loginWith + userAttributes: { + // Standard attributes examples + email: { + mutable: true, + required: true, + }, + phoneNumber: { + mutable: true, + required: false, + }, + givenName: { + mutable: true, + required: true, + }, + familyName: { + mutable: true, + required: false, + }, + birthdate: { + mutable: true, + required: false, + }, + // Username configuration using preferredUsername + preferredUsername: { + mutable: true, + required: false, + }, + // Additional standard attributes + address: { + mutable: true, + required: false, + }, + gender: { + mutable: true, + required: false, + }, + locale: { + mutable: true, + required: false, + }, + profilePicture: { + mutable: true, + required: false, + }, + website: { + mutable: true, + required: false, + }, + // Custom attributes examples - note the 'custom:' prefix and required dataType + "custom:organization": { + dataType: "String", + mutable: true, + minLen: 3, + maxLen: 100, + }, + "custom:employeeId": { + dataType: "Number", + mutable: false, + min: 1000, + max: 9999999, + }, + "custom:isVerified": { + dataType: "Boolean", + mutable: true, + }, + }, + + // Multi-factor Authentication Configuration + multifactor: { + mode: "OPTIONAL", // Can be OPTIONAL or REQUIRED + sms: true, + totp: false, + }, + + // Account Recovery Configuration + accountRecovery: "EMAIL_AND_PHONE_WITHOUT_MFA", + + // Email Sender Configuration + senders: { + email: { + fromEmail: "registrations@example.com", + }, + }, +}); +``` diff --git a/public/images/gen2/q-developer/general.md b/public/images/gen2/q-developer/general.md new file mode 100644 index 00000000000..7dd16a2396a --- /dev/null +++ b/public/images/gen2/q-developer/general.md @@ -0,0 +1,6 @@ +# AMPLIFYRULES + +1. ALWAYS MENTION WHICH FILE PATH THE USER NEED TO PASTE THE CODE IN. +2. MAKE SURE IF IT IS IN TWO DIFFERENT FILES, YOU DIVIDE THE CODE SNIPPET AND MENTION THE FILE PATH +3. IF THE FILE DOESN'T ALREADY EXIST, GIVE THE STEPS TO GENERATE THE FILES. +4. COMMENT ON EVERY PIECE OF CODE THAT CAN HELP IMPROVE CODE QUALITY THAT EXPLAINS WHAT THE LINE DOES diff --git a/public/images/gen2/q-developer/modeling-relationships.md b/public/images/gen2/q-developer/modeling-relationships.md new file mode 100644 index 00000000000..fc67053fb50 --- /dev/null +++ b/public/images/gen2/q-developer/modeling-relationships.md @@ -0,0 +1,262 @@ +# AMPLIFYRULES + +title: Modeling relationships - AWS Amplify Gen 2 Documentation +source: https://docs.amplify.aws/typescript/build-a-backend/data/data-modeling/relationships/ +framework: typescript +lastModified: 2024-10-21T23:11:46.997Z + +--- + +WHEN MODELING APPLICATION DATA, YOU OFTEN NEED TO ESTABLISH RELATIONSHIPS BETWEEN DIFFERENT DATA MODELS. IN AMPLIFY DATA, YOU CAN CREATE ONE-TO-MANY, ONE-TO-ONE, AND MANY-TO-MANY RELATIONSHIPS IN YOUR DATA SCHEMA. ON THE CLIENT-SIDE, AMPLIFY DATA ALLOWS YOU TO LAZY OR EAGER LOAD OF RELATED DATA. + +```typescript +const schema = a + .schema({ + Member: a.model({ + name: a.string().required(), // 1. Create a reference field teamId: a.id(), + // 2. Create a belongsTo relationship with the reference field + team: a.belongsTo("Team", "teamId"), + }), + Team: a.model({ + mantra: a.string().required(), // 3. Create a hasMany relationship with the reference field + // from the `Member`s model. + members: a.hasMany("Member", "teamId"), + }), + }) + .authorization((allow) => allow.publicApiKey()); +``` + +CREATE A "HAS MANY" RELATIONSHIP BETWEEN RECORDS + +```typescript +const { data: team } = await client.models.Team.create({ + mantra: "Go Frontend!", +}); +const { data: member } = await client.models.Member.create({ + name: "Tim", + teamId: team.id, +}); +``` + +UPDATE A "HAS MANY" RELATIONSHIP BETWEEN RECORDS + +```typescript +const { data: newTeam } = await client.models.Team.create({ + mantra: "Go Fullstack", +}); +await client.models.Member.update({ id: "MY_MEMBER_ID", teamId: newTeam.id }); +``` + +DELETE A "HAS MANY" RELATIONSHIP BETWEEN RECORDS +IF YOUR REFERENCE FIELD IS NOT REQUIRED, THEN YOU CAN "DELETE" A ONE-TO-MANY RELATIONSHIP BY SETTING THE RELATIONSHIP VALUE TO NULL. + +```typescript +await client.models.Member.update({ id: "MY_MEMBER_ID", teamId: null }); +``` + +LAZY LOAD A "HAS MANY" RELATIONSHIP + +```typescript +const { data: team } = await client.models.Team.get({ id: "MY_TEAM_ID" }); +const { data: members } = await team.members(); +members.forEach((member) => console.log(member.id)); +``` + +EAGERLY LOAD A "HAS MANY" RELATIONSHIP + +```typescript +const { data: teamWithMembers } = await client.models.Team.get( + { id: "MY_TEAM_ID" }, + { selectionSet: ["id", "members.*"] } +); +teamWithMembers.members.forEach((member) => console.log(member.id)); +``` + +```typescript +const schema = a + .schema({ + Cart: a.model({ + items: a.string().required().array(), + // 1. Create reference field + customerId: a.id(), + // 2. Create relationship field with the reference field + customer: a.belongsTo("Customer", "customerId"), + }), + Customer: a.model({ + name: a.string(), + // 3. Create relationship field with the reference field + // from the Cart model + activeCart: a.hasOne("Cart", "customerId"), + }), + }) + .authorization((allow) => allow.publicApiKey()); +``` + +CREATE A "HAS ONE" RELATIONSHIP BETWEEN RECORDS +TO CREATE A "HAS ONE" RELATIONSHIP BETWEEN RECORDS, FIRST CREATE THE PARENT ITEM AND THEN CREATE THE CHILD ITEM AND ASSIGN THE PARENT. + +```typescript +const { data: customer, errors } = await client.models.Customer.create({ + name: "Rene", +}); + +const { data: cart } = await client.models.Cart.create({ + items: ["Tomato", "Ice", "Mint"], + customerId: customer?.id, +}); +``` + +UPDATE A "HAS ONE" RELATIONSHIP BETWEEN RECORDS +TO UPDATE A "HAS ONE" RELATIONSHIP BETWEEN RECORDS, YOU FIRST RETRIEVE THE CHILD ITEM AND THEN UPDATE THE REFERENCE TO THE PARENT TO ANOTHER PARENT. FOR EXAMPLE, TO REASSIGN A CART TO ANOTHER CUSTOMER: + +```typescript +const { data: newCustomer } = await client.models.Customer.create({ + name: "Ian", +}); +await client.models.Cart.update({ id: cart.id, customerId: newCustomer?.id }); +``` + +DELETE A "HAS ONE" RELATIONSHIP BETWEEN RECORDS +YOU CAN SET THE RELATIONSHIP FIELD TO NULL TO DELETE A "HAS ONE" RELATIONSHIP BETWEEN RECORDS. + +```typescript +await client.models.Cart.update({ id: project.id, customerId: null }); +``` + +LAZY LOAD A "HAS ONE" RELATIONSHIP + +```typescript +const { data: cart } = await client.models.Cart.get({ id: "MY_CART_ID" }); +const { data: customer } = await cart.customer(); +``` + +EAGERLY LOAD A "HAS ONE" RELATIONSHIP + +```typescript +const { data: cart } = await client.models.Cart.get( + { id: "MY_CART_ID" }, + { selectionSet: ["id", "customer.*"] } +); +console.log(cart.customer.id); +``` + +MODEL A "MANY-TO-MANY" RELATIONSHIP +IN ORDER TO CREATE A MANY-TO-MANY RELATIONSHIP BETWEEN TWO MODELS, YOU HAVE TO CREATE A MODEL THAT SERVES AS A "JOIN TABLE". THIS "JOIN TABLE" SHOULD CONTAIN TWO ONE-TO-MANY RELATIONSHIPS BETWEEN THE TWO RELATED ENTITIES. FOR EXAMPLE, TO MODEL A POST THAT HAS MANY TAGS AND A TAG HAS MANY POSTS, YOU'LL NEED TO CREATE A NEW POSTTAG MODEL THAT RETYPESCRIPTSENTS THE RELATIONSHIP BETWEEN THESE TWO ENTITIES. + +```typescript +const schema = a + .schema({ + PostTag: a.model({ + // 1. Create reference fields to both ends of + // the many-to-many relationshipCopy highlighted code example + postId: a.id().required(), + tagId: a.id().required(), + // 2. Create relationship fields to both ends of + // the many-to-many relationship using their + // respective reference fieldsCopy highlighted code example + post: a.belongsTo("Post", "postId"), + tag: a.belongsTo("Tag", "tagId"), + }), + Post: a.model({ + title: a.string(), + content: a.string(), + // 3. Add relationship field to the join model + // with the reference of `postId`Copy highlighted code example + tags: a.hasMany("PostTag", "postId"), + }), + Tag: a.model({ + name: a.string(), + // 4. Add relationship field to the join model + // with the reference of `tagId`Copy highlighted code example + posts: a.hasMany("PostTag", "tagId"), + }), + }) + .authorization((allow) => allow.publicApiKey()); +``` + +MODEL MULTIPLE RELATIONSHIPS BETWEEN TWO MODELS +RELATIONSHIPS ARE DEFINED UNIQUELY BY THEIR REFERENCE FIELDS. FOR EXAMPLE, A POST CAN HAVE SEPARATE RELATIONSHIPS WITH A PERSON MODEL FOR AUTHOR AND EDITOR. + +```typescript +const schema = a + .schema({ + Post: a.model({ + title: a.string().required(), + content: a.string().required(), + authorId: a.id(), + author: a.belongsTo("Person", "authorId"), + editorId: a.id(), + editor: a.belongsTo("Person", "editorId"), + }), + Person: a.model({ + name: a.string(), + editedPosts: a.hasMany("Post", "editorId"), + authoredPosts: a.hasMany("Post", "authorId"), + }), + }) + .authorization((allow) => allow.publicApiKey()); +``` + +ON THE CLIENT-SIDE, YOU CAN FETCH THE RELATED DATA WITH THE FOLLOWING CODE: + +```typescript +const client = generateClient(); +const { data: post } = await client.models.Post.get({ id: "SOME_POST_ID" }); +const { data: author } = await post?.author(); +const { data: editor } = await post?.editor(); +``` + +MODEL RELATIONSHIPS FOR MODELS WITH SORT KEYS IN THEIR IDENTIFIER +IN CASES WHERE YOUR DATA MODEL USES SORT KEYS IN THE IDENTIFIER, YOU NEED TO ALSO ADD REFERENCE FIELDS AND STORE THE SORT KEY FIELDS IN THE RELATED DATA MODEL: + +```typescript +const schema = a + .schema({ + Post: a.model({ + title: a.string().required(), + content: a.string().required(), + // Reference fields must correspond to identifier fields. + authorName: a.string(), + authorDoB: a.date(), + // Must pass references in the same order as identifiers. + author: a.belongsTo("Person", ["authorName", "authorDoB"]), + }), + Person: a + .model({ + name: a.string().required(), + dateOfBirth: a.date().required(), + // Must reference all reference fields corresponding to the + // identifier of this model. + authoredPosts: a.hasMany("Post", ["authorName", "authorDoB"]), + }) + .identifier(["name", "dateOfBirth"]), + }) + .authorization((allow) => allow.publicApiKey()); +``` + +MAKE RELATIONSHIPS REQUIRED OR OPTIONAL +AMPLIFY DATA'S RELATIONSHIPS USE REFERENCE FIELDS TO DETERMINE IF A RELATIONSHIP IS REQUIRED OR NOT. IF YOU MARK A REFERENCE FIELD AS REQUIRED, THEN YOU CAN'T "DELETE" A RELATIONSHIP BETWEEN TWO MODELS. YOU'D HAVE TO DELETE THE RELATED RECORD AS A WHOLE. + +```typescript +const schema = a + .schema({ + Post: a.model({ + title: a.string().required(), + content: a.string().required(), + // You must supply an author when creating the post + // Author can't be set to `null`. + authorId: a.id().required(), + author: a.belongsTo("Person", "authorId"), + // You can optionally supply an editor when creating the post. + // Editor can also be set to `null`. + editorId: a.id(), + editor: a.belongsTo("Person", "editorId"), + }), + Person: a.model({ + name: a.string(), + editedPosts: a.hasMany("Post", "editorId"), + authoredPosts: a.hasMany("Post", "authorId"), + }), + }) + .authorization((allow) => allow.publicApiKey()); +``` diff --git a/public/images/gen2/q-developer/modeling-schema.md b/public/images/gen2/q-developer/modeling-schema.md new file mode 100644 index 00000000000..009dbfd78e9 --- /dev/null +++ b/public/images/gen2/q-developer/modeling-schema.md @@ -0,0 +1,185 @@ +# AMPLIFYRULES + +# DATA + +- THIS FILE IS TO HELP UNDERSTAND THE RELATIONSHIPS, HOW TO MODEL SCHEMAS, WHAT IS THE CORRECT WAY TO CODE FOR ACCURACY +- USE THIS TO UNDERSTAND HOW DATA SCHEMAS ARE DESIGNED. +- FOR THE DATA SCHEMAS MAKE SURE THAT YOU ALWAYS FOLLOW THESE RULES AND THIS FILE OVER ANY OTHER FILE - THIS IS THE SOURCE OF TRUTH. +- RULES + + - THIS FILE IS THE SINGLE SOURCE OF TRUTH FOR SCHEMA DESIGN AND RELATIONSHIPS. FOLLOW THESE RULES STRICTLY. USE THIS FILE OVER ANY OTHER RESOURCE TO UNDERSTAND SCHEMA DESIGN. + + 1. DON'T USE `.PUBLIC()` WHILE SETTING UP THE AUTHORIZATION. AS AMPLIFY GEN2 ONLY SUPPORTS `.GUEST()`. + 2. `.BEONGSTO()` AND `.HASMANY()` RELATIONS SHALL ALWAYS HAVE THE RELATEDFIELD ID. + 3. `.ENUM()` DOESN'T SUPPORT `.REQUIRED()`/ `.DEFAULTVALUE()` IN ANY CONDITION, SO ALWAYS IGNORE USING IT. + 4. TO GIVE PERMISSION TO THE GROUP MAKE SURE YOU USE .to(), FOLLOWED BY THE GROUP: FOR E.G. `allow.guest().to['read', 'create', 'delete','get'] + 5. THIS IS HOW YOU SHOULD USE THE AUTHORIZATION `(allow) => [allow.owner(),allow.guest().to[("read", "write", "delete")]` , THIS IS INCORRECT `.authorization([allow => allow.owner(), allow => allow.guest().to(['read','write'])])` + +- BELOW ARE THE EXAMPLES TO USE TO GENERATE ANSWERS. + +```typescript +import { type ClientSchema, a, defineData } from "@aws-amplify/backend"; + +const schema = a + .schema({ + Vehicle: a.model({ + id: a.id(), + make: a.string().required(), + model: a.string().required(), + year: a.integer().required(), + licensePlate: a.string().required(), + status: a.enum(["AVAILABLE", "RENTED", "MAINTENANCE"]), // Enum; Don't use .required() or .defaultValue() + locationId: a.id(), + location: a.belongsTo("Location", "locationId"), // Belongs-to relationship, Requires ID + rentals: a.hasMany("Rental", "vehicleId"), // Has-many relationship with required relatedFieldId + }), + Customer: a.model({ + id: a.id(), + firstName: a.string().required(), + lastName: a.string().required(), + email: a.string().required(), + phone: a.string().required(), + licenseNumber: a.string().required(), + rentals: a.hasMany("Rental", "customerId"), // Has-many relationship with required relatedFieldId + }), + Location: a.model({ + id: a.id(), + name: a.string().required(), + address: a.string().required(), + city: a.string().required(), + state: a.string().required(), + zipCode: a.string().required(), + vehicles: a.hasMany("Vehicle", "locationId"), // Has-many relationship with required relatedFieldId + }), + Rental: a.model({ + id: a.id(), + startDate: a.datetime().required(), + endDate: a.datetime().required(), + status: a.enum(["ACTIVE", "COMPLETED", "CANCELLED"]), // Enum; no .required() or .defaultValue() + vehicleId: a.id(), + customerId: a.id(), + vehicle: a.belongsTo("Vehicle", "vehicleId"), // Belongs-to relationship, Requires ID + customer: a.belongsTo("Customer", "customerId"), // Has-many relationship with required relatedFieldId + }), + }) + .authorization((allow) => [ + allow.owner(), + allow.guest().to[("read", "write", "delete")], + ]); // Owner-based and guest access, `.public()` references are replaced with `.guest()`. Authorizaiton groups can be concatenated, To give the permission use the to() function + +export type Schema = ClientSchema; + +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: "apiKey", + apiKeyAuthorizationMode: { + expiresInDays: 30, + }, + }, +}); +``` + +- Another Example + +```typescript +import { type ClientSchema, a, defineData } from "@aws-amplify/backend"; + +// Define the schema for the ecommerce application +const schema = a.schema({ + Product: a + .model({ + name: a.string().required(), + description: a.string(), + price: a.float().required(), + inventory: a.integer(), + categoryId: a.id(), + category: a.belongsTo("Category", "categoryId"), // belongs to relationship with required relatedFieldId + images: a.string().array(), + }) + .authorization((allow) => [allow.guest()]), + + Category: a + .model({ + name: a.string().required(), + description: a.string(), + products: a.hasMany("Product", "categoryId"), // Has-many relationship with required relatedFieldId + }) + .authorization((allow) => [allow.guest()]), + + Order: a + .model({ + userId: a.id().required(), + status: a.enum(["PENDING", "PROCESSING", "SHIPPED", "DELIVERED"]), // Enum; Don't use .required() or .defaultValue() + total: a.float().required(), + items: a.hasMany("OrderItem", "orderId"), // Has-many relationship with required relatedFieldId + }) + .authorization((allow) => [allow.owner()]), + + OrderItem: a + .model({ + orderId: a.id().required(), + productId: a.id().required(), + quantity: a.integer().required(), + price: a.float().required(), + }) + .authorization((allow) => [allow.owner()]), +}); + +// Define the client schema and data export +export type Schema = ClientSchema; +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: "userPool", + }, +}); +``` + +```typescript +import { type ClientSchema, a, defineData } from "@aws-amplify/backend"; + +const schema = a + .schema({ + Customer: a + .model({ + customerId: a.id().required(), + // fields can be of various scalar types, + // such as string, boolean, float, integers etc. + name: a.string(), + // fields can be of custom types + location: a.customType({ + // fields can be required or optional + lat: a.float().required(), + long: a.float().required(), + }), + // fields can be enums + engagementStage: a.enum(["PROSPECT", "INTERESTED", "PURCHASED"]), //enum doesn't support required + collectionId: a.id(), + collection: a.belongsTo("Collection", "collectionId"), + // Use custom identifiers. By default, it uses an `id: a.id()` field + }) + .identifier(["customerId"]), + Collection: a + .model({ + customers: a.hasMany("Customer", "collectionId"), // setup relationships between types + tags: a.string().array(), // fields can be arrays + representativeId: a.id().required(), + // customize secondary indexes to optimize your query performance + }) + .secondaryIndexes((index) => [index("representativeId")]), + }) + .authorization((allow) => [allow.publicApiKey()]); + +export type Schema = ClientSchema; + +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: "apiKey", + apiKeyAuthorizationMode: { + expiresInDays: 30, + }, + }, +}); +``` diff --git a/src/pages/[platform]/build-a-backend/q-developer/index.mdx b/src/pages/[platform]/build-a-backend/q-developer/index.mdx index bc6807f33b7..bdd9e881c3b 100644 --- a/src/pages/[platform]/build-a-backend/q-developer/index.mdx +++ b/src/pages/[platform]/build-a-backend/q-developer/index.mdx @@ -90,3 +90,36 @@ import { type ClientSchema, a, defineData } from "@aws-amplify/backend"; **Step 4:** Make any required changes to the schema and save the `amplify/data/resource.ts` file. This will trigger a sandbox deployment and your new data model will be deployed + +## Use Q Developer - workspace in your Amplify project + + +Adding `@workspace` to your question in Amazon Q automatically incorporates the most relevant parts of your workspace code as context, using an index that updates periodically. For more information on `@workspace` functionality, please visit [Amazon Q Developer - Workspace Context](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/workspace-context.html). + +The files below provide detailed guides that can be included as context with the `@workspace` command, enhancing Amazon Q's accuracy in generating AWS Amplify Gen 2 code. + +Download the files and follow the steps below to use the `@workspace` in your Amplify project + +- general.md +- authentication.md +- modeling-relationships.md +- modeling-schema.md + + +**Step 1:** Create a folder in the root of your project and give a descriptive name such as `context`. Add the files downloaded above to this folder. + +**Step 2:** Open Amazon Q Developer Chat in your IDE and type `@workspace` to enable workspace indexing. Follow Amazon Q's prompts to set up indexing for your project directory. + +**Step 3:** After successful indexing, reference the markdown file content in your queries to Amazon Q. Examples: + +```bash title="Terminal" + +@workspace follow AMPLIFYRULES to develop a data model schema for a freelance marketplace using Amplify Gen 2. Include models for freelancers, clients, projects, bids, and reviews. Use Amplify Gen 2 to fetch a list of projects + +``` + +```bash title="Terminal" + +@workspace follow AMPLIFYRULES to design a data schema for an event management application using Amplify Gen 2. Include models for users, events, and tickets. Show me how to use Amplify Gen 2 to fetch a list of events. + +```