From 50005f45115ea799693ab90bca4f4b8125b7bf9e Mon Sep 17 00:00:00 2001 From: offlineprogrammer Date: Wed, 4 Dec 2024 09:44:35 -0800 Subject: [PATCH] Q workspace command --- cspell.json | 3 +- .../images/gen2/q-developer/authentication.md | 219 +++++++++++++++ public/images/gen2/q-developer/general.md | 6 + .../q-developer/modeling-relationships.md | 262 ++++++++++++++++++ .../gen2/q-developer/modeling-schema.md | 185 +++++++++++++ .../build-a-backend/q-developer/index.mdx | 34 +++ 6 files changed, 708 insertions(+), 1 deletion(-) create mode 100644 public/images/gen2/q-developer/authentication.md create mode 100644 public/images/gen2/q-developer/general.md create mode 100644 public/images/gen2/q-developer/modeling-relationships.md create mode 100644 public/images/gen2/q-developer/modeling-schema.md 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..1807996ac73 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,37 @@ 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. 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 name it something like `context`. Add the files listed 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 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. + + +```