├── .gitignore ├── LICENSE ├── NOTICE.txt ├── README.md ├── README ├── README-CN.md ├── README-DE.md ├── README-ES.md ├── README-FR.md ├── README-IT.md ├── README-JP.md ├── README-KR.md ├── README-PT.md ├── README-RU.md └── README-TW.md ├── apigateway-models ├── CreateNoteRequest.txt └── CreateNoteResponse.txt ├── assets └── cloudsearch-attributes.png ├── cloudformation ├── config-helper.template └── mobile-backend.template ├── ios-sample ├── MobileBackendIOS-Bridging-Header.h ├── MobileBackendIOS.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ └── oladehin.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── MobileBackendIOS │ ├── APICreateNoteRequest.h │ ├── APICreateNoteRequest.m │ ├── APICreateNoteResponse.h │ ├── APICreateNoteResponse.m │ ├── APINotesApiClient.h │ ├── APINotesApiClient.m │ ├── AddNoteViewController.swift │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Constants.swift │ ├── Info.plist │ ├── MobileBackendApi.swift │ └── UploadPhotoViewController.swift └── Podfile └── lambda-functions ├── package ├── search └── index.js ├── stream-handler └── index.js ├── upload └── upload-note └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ios-sample/Pods/* 3 | ios-sample/Podfile.lock 4 | ios-sample/MobileBackendIOS.xcodeproj/xcuserdata 5 | ios-sample/MobileBackendIOS.xcworkspace 6 | lambda-functions/*.zip 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | AWS Lambda Reference Architecture: Mobile Backend 2 | lambda-refarch-mobilebackend 3 | Copyright 2015 Amazon.com, Inc. or its affiliates. 4 | All Rights Reserved. 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serverless Reference Architecture: Mobile Backend 2 | README Languages: [DE](README/README-DE.md) | [ES](README/README-ES.md) | [FR](README/README-FR.md) | [IT](README/README-IT.md) | [JP](README/README-JP.md) | [KR](README/README-KR.md) | 3 | [PT](README/README-PT.md) | [RU](README/README-RU.md) | 4 | [CN](README/README-CN.md) | [TW](README/README-TW.md) 5 | ## Introduction 6 | 7 | The Mobile Backend reference architecture ([diagram](https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/lambda-refarch-mobilebackend.pdf)) demonstrates how to use [AWS Lambda](http://aws.amazon.com/lambda/) along with other services to build a serverless backend for a mobile application. The specific example application provided in this repository enables users to upload photos and notes using Amazon Simple Storage Service (Amazon S3) and Amazon API Gateway respectively. The notes are stored in Amazon DynamoDB, and are processed asynchronously using DynamoDB streams and a Lambda function to add them to an Amazon CloudSearch domain. In addition to the source code for the Lambda functions, this repository also contains a prototype iOS application that provides examples for how to use the AWS Mobile SDK for iOS to interface with the backend resources defined in the architecture. 8 | 9 | ## Running the example 10 | 11 | To run the full example application, you must first deploy the backend resources, and then compile and run the example iOS application. 12 | 13 | ### Deploying the Backend 14 | 15 | The provided AWS CloudFormation template creates most of the backend resources that you need for this example, but you still need to create the Amazon CloudSearch domain, API Gateway REST API, and Cognito identity pool outside of AWS CloudFormation. 16 | 17 | #### Step 1: Create a CloudSearch Domain 18 | 19 | 1. Using the [AWS CLI](https://aws.amazon.com/cli/), create a new CloudSearch domain providing a domain name of your choice. 20 | 21 | ``` 22 | aws cloudsearch create-domain --domain-name [YOUR_DOMAIN_NAME] 23 | ``` 24 | 25 | 1. Note the ARN of the new domain in the output document. You will use this as an input when launching the CloudFormation stack. 26 | 27 | 1. Define indexes for the `headline` and `note_text` fields. 28 | 29 | ``` 30 | aws cloudsearch define-index-field --name headline --type text --domain-name [YOUR_DOMAIN_NAME] 31 | aws cloudsearch define-index-field --name note_text --type text --domain-name [YOUR_DOMAIN_NAME] 32 | ``` 33 | Note: You will need to reindex your Domain via CLI or Console. Here is the CLI command: 34 | 35 | ``` 36 | aws cloudsearch index-documents --domain-name [YOUR_DOMAIN_NAME] 37 | ``` 38 | 39 | #### Step 2: Create an API Gateway REST API 40 | 41 | 1. Using the [AWS CLI](https://aws.amazon.com/cli/), create a new API providing a name of your choice. 42 | 43 | ``` 44 | aws apigateway create-rest-api --name [YOUR_API_NAME] 45 | ``` 46 | 47 | 1. Note the `API ID` provided in the output document. You will use this as an input when launching the CloudFormation stack. 48 | 49 | #### Step 3: Create an Amazon Cognito Identity Pool 50 | 51 | 1. Using the [AWS CLI](https://aws.amazon.com/cli/), create a new identity pool providing a name of your choice. 52 | 53 | ``` 54 | aws cognito-identity create-identity-pool --allow-unauthenticated-identities --identity-pool-name [YOUR_POOL_NAME] 55 | ``` 56 | 57 | 1. Note the `IdentityPoolId` from the output document. You will use this as a parameter when launching the CloudFormation stack. 58 | 59 | #### Step 4: Launch the CloudFormation Template 60 | 61 | You can deploy the entire example in the us-east-1 region using the provided CloudFormation template and S3 bucket by Launching the stack below. 62 | If you would like to deploy the template to a different region, you must create an Amazon S3 bucket in that region, then in the file `lambda-functions/upload`, modify the BUCKET variable with the bucket you created, run `./upload`, and copy config-helper.template and mobile-backend.template into the bucket. 63 | 64 | Choose **Launch Stack** to launch the template in the us-east-1 region in your account: 65 | 66 | [![Launch Lambda Mobile Backend into North Virginia with CloudFormation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/images/cloudformation-launch-stack-button.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=lambda-mobile-backend&templateURL=https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/mobile-backend.template) 67 | 68 | When prompted, enter the parameter values for the CloudSearch domain, CloudSearch Search/Document Endpoints, API Gateway REST API ID, and Amazon Cognito identity pool resources you created in the previous steps. 69 | 70 | Details about the resources created by this template are provided in the *CloudFormation Template Resources* section of this document. 71 | 72 | #### Step 5: Update Your API Gateway REST API 73 | 74 | After you have created the CloudFormation stack, you need to update the API you created previously to use the newly created `NotesApiFunction`. 75 | 76 | 1. In the [Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis), choose your API. 77 | 1. Choose **Create Resource** to create a new child resource under /. 78 | 1. Type `notes` as the resource name and `/notes` as the resource path. 79 | 1. Choose **Create Resource**. 80 | 1. With the new `/notes` resource selected, choose **Create Method**. 81 | 1. Choose `POST` and select the check box. 82 | 1. Choose **Lambda Function** as the integration type and then choose the region where you launched the CloudFormation stack as the Lambda region. 83 | 1. in **Lambda Function**, type **`NotesApiFunction`** and then select the function created by the CloudFormation Stack. 84 | 1. Choose **Save** and grant API Gateway permissions to execute the Lambda function. 85 | 1. Choose **Method Request** to edit the request configuration. 86 | 1. For **Authorization type**, select `AWS_IAM`. 87 | 1. For **API Key Required**, select `true`. 88 | 1. Select the Actions dropdown, Choose **Deploy API**. 89 | 1. For **Deployment stage**, choose `New Stage` and then type a name in **Stage name**. 90 | 1. Note the **Invoke URL** for the new stage. You will use this value when running the sample iOS app. 91 | 92 | #### Step 6: Create an API Key 93 | 94 | 1. In the [Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis), choose **API Keys**. 95 | 1. Choose **Create API Key**. 96 | 1. Type a name for the key and then select **Save**. 97 | 1. Note the **API key**. You will use this when running the mobile application. 98 | 1. Click on **Usage Plans** and type a name for the plan. Disable Throttling and Quotas. Click **Next**. 99 | 1. From the dropdowns, seleect your API and Stage created in Step 4. Click the Checkbox and then **Next**. 100 | 1. Click **Add API Key to Usage Plan** and enter the Key you created earlier then choose **Done**. 101 | 102 | #### Step 7: Update Your Amazon Cognito Identity Pool 103 | 104 | 1. In the [Amazon Cognito Console](https://console.aws.amazon.com/cognito/home?region=us-east-1), select your identity pool. 105 | 1. Choose **Edit Identity Pool**. 106 | 1. For both the **Unauthenticated role** and the **Authenticated role**, select the **MobileClientRole** created by the CloudFormation stack. The full ARN for the role is provided in the outputs of the stack. 107 | 1. Choose **Save Changes**. 108 | 109 | 110 | ### Running the Sample iOS Application 111 | 112 | #### Prerequisites 113 | 114 | To run the provided iOS sample application, you must be running Mac OS X 10.10 (Yosemite) or a more recent version. You must also have the latest version of [Xcode](https://itunes.apple.com/us/app/xcode/id497799835) and [Cocoa Pods](https://cocoapods.org/) installed. 115 | 116 | #### Building and Running the Application 117 | 118 | 1. Check out or download the source code for the **ios-sample** in this repository. 119 | 1. Update `MobileBackendIOS/Constants.swift` with the values for your backend deployment. Most of the values can be found in the outputs of the CloudFormation stack. The API Gateway key and endpoint URL values are available in the details for your API in the AWS Management Console. 120 | 1. Run Cocoa Pods from the root `ios-sample` directory. 121 | 122 | ``` 123 | pod install 124 | ``` 125 | 126 | 1. Open the generated `MobileBackendIOS.xcworkspace` file in Xcode. 127 | 128 | ``` 129 | open -a Xcode MobileBackendIOS.xcworkspace 130 | ``` 131 | 132 | 1. Build and run the project from Xcode by clicking the play button at the top of the window. 133 | 134 | ## Testing the Application 135 | 136 | The sample application provides two features: uploading an image and posting a note. 137 | 138 | ### To upload an image 139 | 140 | 1. Choose **Upload Image** in the application. 141 | 1. Choose the camera icon, select an image from the camera roll, and then choose **Choose**. 142 | 1. Choose the **Upload** button. 143 | 144 | #### Validating that the image was uploaded 145 | 146 | You should see a log entry that the image has been uploaded to Amazon S3 in the output pane of Xcode. 147 | 148 | You can also browse the bucket created by the CloudFormation stack using the AWS Management Console to verify that the image was correctly uploaded. 149 | 150 | ### To post a note 151 | 152 | 1. Choose **Post a Note**. 153 | 1. In the note, type a headline and text. 154 | 1. Choose **Save Note**. 155 | 156 | #### Validating that the note was posted 157 | 158 | You should see a log entry that the note was saved successfully in the output pane of Xcode. 159 | 160 | When the note is uploaded, the `NotesApiFunction` is invoked by the mobile application. You can view the logs for this function in Amazon CloudWatch. 161 | 162 | When the function is successfully invoked, it adds an entry to the DynamoDB table created in the CloudFormation stack. You can verify that the note you posted in the application has been persisted in the created table. 163 | 164 | Finally, when the note is persisted in the DynamoDB table a record is added to the table's stream which is in turn processed by the `DynamoStreamHandlerFunction`. You can view the logs for this function in CloudWatch and verify that a new document has been added to the CloudSearch domain you created. 165 | 166 | 167 | ## Cleaning Up the Application Resources 168 | 169 | To remove all resources created by this example, do the following: 170 | 171 | 1. Delete all objects from the S3 bucket created by the CloudFormation stack. 172 | 1. Delete the CloudFormation stack. 173 | 1. Delete the Amazon Cognito identity pool, API Gateway, and CloudSearch domain. 174 | 1. Delete the CloudWatch log groups associated with each Lambda function created by the CloudFormation stack. 175 | 176 | ## CloudFormation Template Resources 177 | 178 | ### Lambda Functions 179 | 180 | - **NotesApiFunction** - A function to handle posted notes from the mobile application via API Gateway. 181 | 182 | - **SearchApiFunction** - A function that uses the CloudSearch domain to find indexed notes based on search terms. 183 | 184 | - **DynamoStreamHandlerFunction** - A function that adds an indexed document to the provided CloudSearch domain based on records in the `PhotoNotesTable` stream. 185 | 186 | ### AWS Identity and Access Management (IAM) Roles 187 | 188 | - **NotesApiRole** - A role for the `NotesApiFunction`. This role grants permission for logging and working with items in the `PhotoNotesTable`. 189 | 190 | - **SearchApiRole** - A role for the `SearchApiFunction`. This role grants permissions for logging and searching the provided CloudSearch domain. 191 | 192 | - **DynamoStreamHandlerRole** - A role for the DynamoStreamHandlerFunction. This role grants permissions for logging and adding documents to the provided CloudSearch domain. 193 | 194 | - **MobileClientRole** - A role used by your Amazon Cognito identity pool for both unauthenticated and authenticated users. This role provides access to the provided API Gateway REST API as well as permissions for putting objects to the `MobileUploadsBucket`. 195 | 196 | ### Other Resources 197 | 198 | - **MobileUploadsBucket** - An S3 bucket for user uploaded photos. 199 | 200 | - **CloudFrontDistribution** - A CDN distribution with the `MobileUploadsBucket` configured as an origin. 201 | 202 | - **PhotoNotesTable** - A DynamoDB table that stores notes uploaded by users from the mobile application. 203 | 204 | ### Configuration 205 | 206 | - **ConfigTable** - A DynamoDB table to hold configuration values read by the various Lambda functions. The name of this table, "MobileRefArchConfig", is hard coded into each function's code and cannot be modified without updating the code as well. 207 | 208 | - **ConfigHelperStack** - A sub-stack that creates a custom resource for writing entries to the `ConfigTable`. This stack creates a Lambda function and execution role that grants UpdateItem permission on the `ConfigTable`. 209 | 210 | - **NotesTableConfig** - A configuration entry that identifies the `PhotoNotesTable` name. 211 | 212 | - **SearchEndpointConfig** - A configuration entry that identifies the search endpoint of the CloudSearch domain passed as a parameter. 213 | 214 | - **DocumentEndpointConfig** - A configuration entry that identifies document endpoint of the CloudSearch domain passed as a parameter. 215 | 216 | ## License 217 | 218 | This reference architecture sample is licensed under Apache 2.0. 219 | -------------------------------------------------------------------------------- /README/README-CN.md: -------------------------------------------------------------------------------- 1 | # 无服务器参考架构:移动后端 2 | 3 | ## 简介 4 | 5 | 移动后端参考架构 ([示意图](https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/lambda-refarch-mobilebackend.pdf)) 演示了如何使用 [AWS Lambda](http://aws.amazon.com/lambda/) 以及其他服务来构建移动应用程序的无服务器后端。此存储库中提供的特定示例应用程序支持用户相应地使用 Amazon Simple Storage Service (Amazon S3) 和 Amazon API Gateway 上传照片和备注。备注存储在 Amazon DynamoDB 中,并使用 DynamoDB 流异步处理,使用 Lambda 函数添加到 Amazon CloudSearch 域中。除 Lambda 函数的源代码外,此存储库还包含原型 iOS 应用程序,该应用程序提供了示例来说明如何使用适用于 iOS 的 AWS Mobile SDK 与架构中定义的后端资源交互。 6 | 7 | ## 运行示例 8 | 9 | 要运行完整的示例应用程序,您必须先部署后端资源,然后编译并运行示例 iOS 应用程序。 10 | 11 | ### 部署后端 12 | 13 | 使用提供的 AWS CloudFormation 模板可以创建此示例需要的大多数后端资源,但您仍然需要在 AWS CloudFormation 之外创建 Amazon CloudSearch 域、API Gateway REST API 和 Cognito 身份池。 14 | 15 | #### 步骤 1:创建 CloudSearch 域 16 | 17 | 1.使用 [AWS CLI](https://aws.amazon.com/cli/) 创建新 CloudSearch 域,自己选择一个域名。 18 | 19 | ``` 20 | aws cloudsearch create-domain --domain-name [YOUR_DOMAIN_NAME] 21 | ``` 22 | 23 | 1.记下输出文档中新域的 ARN。启动 CloudFormation 堆栈时,您将使用此信息作为输入。 24 | 25 | 1.定义 `headline` 和 `note_text` 字段的索引。 26 | 27 | ``` 28 | aws cloudsearch define-index-field --name headline --type text --domain-name [YOUR_DOMAIN_NAME] 29 | aws cloudsearch define-index-field --name note_text --type text --domain-name [YOUR_DOMAIN_NAME] 30 | ``` 31 | 32 | ``` 33 | aws cloudsearch index-documents --domain-name [YOUR_DOMAIN_NAME] 34 | ``` 35 | 36 | #### 步骤 2:创建 API Gateway REST API 37 | 38 | 1.使用 [AWS CLI](https://aws.amazon.com/cli/) 创建新 API,自己选择一个名称。 39 | 40 | ``` 41 | aws apigateway create-rest-api --name [YOUR_API_NAME] 42 | ``` 43 | 44 | 1.记下输出文档中提供的 `API ID`。启动 CloudFormation 堆栈时,您将使用此信息作为输入。 45 | 46 | #### 步骤 3:创建 Amazon Cognito 身份池 47 | 48 | 1.使用 [AWS CLI](https://aws.amazon.com/cli/) 创建新身份池,自己选择一个名称。 49 | 50 | ``` 51 | aws cognito-identity create-identity-pool --allow-unauthenticated-identities --identity-pool-name [YOUR_POOL_NAME] 52 | ``` 53 | 54 | 1.记下输出文档中的 `IdentityPoolId`。启动 CloudFormation 堆栈时,您将使用此信息作为参数。 55 | 56 | #### 步骤 4:启动 CloudFormation 模板 57 | 58 | 您可以使用提供的 CloudFormation 模板和 S3 存储桶在 us-east-1 区域中部署整个示例。如果您希望将模板部署到不同区域,必须在该区域中创建 Amazon S3 存储桶,然后将模板和 Lambda 函数定义复制到其中。 59 | 60 | 选择 **Launch Stack** 以在您账户的 us-east-1 区域中启动模板: 61 | 62 | [![使用 CloudFormation 在北弗吉尼亚区域中启动 Lambda 移动后端](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/images/cloudformation-launch-stack-button.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=lambda-mobile-backend&templateURL=https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/mobile-backend.template) 63 | 64 | 在系统提示时,输入您在前面步骤中创建的 CloudSearch 域、API Gateway REST API 和 Amazon Cognito 身份池资源的参数值。 65 | 66 | 在本文档的 *CloudFormation 模板资源* 部分中提供了有关通过该模板创建的资源的详细信息。 67 | 68 | #### 步骤 5:更新您的 API Gateway REST API 69 | 70 | 创建 CloudFormation 堆栈后,您需要更新先前创建的 API 以使用新创建的 `NotesApiFunction`。 71 | 72 | 1.在 [Amazon API Gateway 控制台](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis) 中选择您的 API。 73 | 1.选择 **Create Resource** 以在 / 下面创建新的子资源。 74 | 1.键入 `notes` 作为资源名称,并键入 `/notes` 作为资源路径。 75 | 1.选择 **Create Resource**。 76 | 1.选中 `/notes` 资源后,选择 **Create Method**。 77 | 1.选择 `POST` 并选中复选框。 78 | 1.选择 **Lambda Function** 作为集成类型,然后选择启动 CloudFormation 堆栈时所在的区域作为 Lambda 区域。 79 | 1.在 **Lambda Function** 中,键入 **`NotesApiFunction`**,然后选择 CloudFormation 堆栈创建的函数。 80 | 1.选择 **Save**,并授予 API Gateway 权限以执行 Lambda 函数。 81 | 1.选择 **Method Request** 以编辑请求配置。 82 | 1.对于 **Authorization type**,选择 `AWS_IAM`。 83 | 1.对于 **API Key Required**,选择 `true`。 84 | 1.选择 **Deploy API**。 85 | 1.对于 **Deployment stage**,选择 `New Stage`,然后在 **Stage name** 中键入名称。 86 | 1.记下新阶段的 **Invoke URL**。运行示例 iOS 应用程序时,您将使用此值。 87 | 88 | #### 步骤 6:创建 API 密钥 89 | 90 | 1.在 [Amazon API Gateway 控制台](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis) 中选择 **APIs**,然后选择 **API Keys**。 91 | 1.选择 **Create API Key**。 92 | 1.为密钥键入名称,然后选择 **Enabled**。 93 | 1.选择 **Save** 94 | 1.在 **API Stage Association** 部分中,选择您的 API,然后选择您在上一步中创建的阶段。 95 | 1.选择 **Add**。 96 | 1.记下 **API key**。运行移动应用程序时,您将使用此信息。 97 | 98 | #### 步骤 7:更新您的 Amazon Cognito 身份池 99 | 100 | 1.在 [Amazon Cognito 控制台](https://console.aws.amazon.com/cognito/home?region=us-east-1) 中,选择您的身份池。 101 | 1.选择 **Edit Identity Pool**。 102 | 1.对于 **Unauthenticated role** 和 **Authenticated role**,选择 CloudFormation 堆栈创建的 **MobileClientRole**。堆栈输出中提供角色的完整 ARN。 103 | 1.选择 **Save Changes**。 104 | 105 | 106 | ### 运行示例 iOS 应用程序 107 | 108 | #### 先决条件 109 | 110 | 要运行提供的 iOS 示例应用程序,必须运行 Mac OS X 10.10 (Yosemite) 或更新版本。您还必须安装最新版本的 [Xcode](https://itunes.apple.com/us/app/xcode/id497799835) 和 [Cocoa Pods](https://cocoapods.org/)。 111 | 112 | #### 构建和运行应用程序 113 | 114 | 1.签出或下载此存储库中的 **ios-sample** 源代码。 115 | 1.使用后端部署的值更新 `MobileBackendIOS/Constants.swift`。在 CloudFormation 堆栈输出中可以找到大多数值。在 AWS 管理控制台中,可以在 API 详细信息中找到 API Gateway 密钥和终端节点 URL 值。 116 | 1.从 `ios-sample` 根目录运行 Cocoa Pods。 117 | 118 | ``` 119 | pod install 120 | ``` 121 | 122 | 1.在 Xcode 中打开生成的 `MobileBackendIOS.xcworkspace` 文件。 123 | 124 | ``` 125 | open -a Xcode MobileBackendIOS.xcworkspace 126 | ``` 127 | 128 | 1.通过单击窗口顶部的播放按钮,从 Xcode 构建并运行项目。 129 | 130 | ## 测试应用程序 131 | 132 | 示例应用程序提供了两项功能:上传图像和发布备注。 133 | 134 | ### 上传图像 135 | 136 | 1.在应用程序中,选择 **Upload Image**。 137 | 1.选择相机图标,从相册中选择图像,然后选择 **Choose**。 138 | 1.选择 **Upload** 按钮。 139 | 140 | #### 验证是否已上传图像 141 | 142 | 您应该会在 Xcode 输出窗格中看到一个日志条目,指出图像已上传到 Amazon S3。 143 | 144 | 您还可以使用 AWS 管理控制台浏览 CloudFormation 堆栈创建的存储桶,以验证是否已正确上传图像。 145 | 146 | ### 发布备注 147 | 148 | 1.选择 **Post a Note**。 149 | 1.在备注中键入标题和文本。 150 | 1.选择 **Save Note**。 151 | 152 | #### 验证是否已发布备注 153 | 154 | 您应该会在 Xcode 输出窗格中看到一个日志条目,指出备注已成功保存。 155 | 156 | 上传备注之后,移动应用程序会调用 `NotesApiFunction`。您可以在 Amazon CloudWatch 中查看此函数的日志。 157 | 158 | 成功调用该函数后,它会将一个条目添加到 CloudFormation 堆栈创建的 DynamoDB 表中。您可以验证应用程序中发布的备注是否已保存在创建的表中。 159 | 160 | 最后,当在 DynamoDB 表中保存备注时,会将一个记录添加到该表的流中,而该流又会由 `DynamoStreamHandlerFunction` 处理。您可以在 CloudWatch 中查看此函数的日志,并验证新文档是否已添加到您创建的 CloudSearch 域中。 161 | 162 | 163 | ## 清理应用程序资源 164 | 165 | 要删除此示例创建的所有资源,请执行以下操作: 166 | 167 | 1.删除 CloudFormation 堆栈创建的 S3 存储桶中的所有对象。 168 | 1.删除 CloudFormation 堆栈。 169 | 1.删除 Amazon Cognito 身份池、API Gateway 和 CloudSearch 域。 170 | 1.删除与 CloudFormation 堆栈创建的每个 Lambda 函数关联的 CloudWatch 日志组。 171 | 172 | ## CloudFormation 模板资源 173 | 174 | ### Lambda 函数 175 | 176 | - **NotesApiFunction** - 一个函数,用于通过 API Gateway 处理移动应用程序中发布的备注。 177 | 178 | - **SearchApiFunction** - 一个函数,该函数使用 CloudSearch 域根据搜索词来查找已建立索引的备注。 179 | 180 | - **DynamoStreamHandlerFunction** - 一个函数,该函数根据 `PhotoNotesTable` 流中的记录将已建立索引的文档添加到提供的 CloudSearch 域。 181 | 182 | ### AWS Identity and Access Management (IAM) 角色 183 | 184 | - **NotesApiRole** - `NotesApiFunction` 的角色。此角色授予在 `PhotoNotesTable` 中记录项以及使用其中项的权限。 185 | 186 | - **SearchApiRole** - `SearchApiFunction` 的角色。此角色授予记录和搜索提供的 CloudSearch 域的权限。 187 | 188 | - **DynamoStreamHandlerRole** - DynamoStreamHandlerFunction 的角色。此角色授予记录文档并将文档添加到提供的 CloudSearch 域的权限。 189 | 190 | - **MobileClientRole** - 一个角色,该角色由您的 Amazon Cognito 身份池用于未经身份验证和已进行身份验证的用户。此角色授予对所提供 API Gateway REST API 的访问权限,并提供将对象放入 `MobileUploadsBucket` 的权限。 191 | 192 | ### 其他资源 193 | 194 | - **MobileUploadsBucket** - 用于保存用户上传的照片的 S3 存储桶。 195 | 196 | - **CloudFrontDistribution** - 将 `MobileUploadsBucket` 配置为源的 CDN 分配。 197 | 198 | - **PhotoNotesTable** - 存储用户从移动应用程序上传的备注的 DynamoDB 表。 199 | 200 | ### 配置 201 | 202 | - **ConfigTable** - 存放各个 Lambda 函数读取的配置值的 DynamoDB 表。此表的名称 "MobileRefArchConfig" 已硬编码到每个函数的代码中;在不更新代码的情况下无法修改。 203 | 204 | - **ConfigHelperStack** - 创建自定义资源以便向 `ConfigTable` 写入条目的子堆栈。此堆栈创建 Lambda 函数和执行角色,该执行角色可授予对 `ConfigTable` 的 UpdateItem 权限。 205 | 206 | - **NotesTableConfig** - 标识 `PhotoNotesTable` 名称的配置条目。 207 | 208 | - **SearchEndpointConfig** - 一个配置条目,用于标识以参数形式传递的 CloudSearch 域的搜索终端节点。 209 | 210 | - **DocumentEndpointConfig** - 一个配置条目,用于标识以参数形式传递的 CloudSearch 域的文档终端节点。 211 | 212 | ## 许可证 213 | 214 | 此示例参考架构已获得 Apache 2.0 许可。 215 | -------------------------------------------------------------------------------- /README/README-DE.md: -------------------------------------------------------------------------------- 1 | # Serverlose Referenzarchitektur: Mobiles Backend 2 | 3 | ## Einführung 4 | 5 | Die Referenzarchitektur für mobiles Backend ([Diagramm](https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/lambda-refarch-mobilebackend.pdf)) zeigt, wie [AWS Lambda](http://aws.amazon.com/lambda/) gemeinsam mit anderen Services verwendet wird, um ein serverloses Backend für eine mobile Anwendung zu erstellen. Über die in diesem Repository bereitgestellte spezifische Beispielanwendung können Benutzer Fotos und Notizen mithilfe von Amazon Simple Storage Service (Amazon S3) bzw. Amazon API Gateway hochladen. Die Notizen werden in Amazon DynamoDB gespeichert und asynchron durch DynamoDB Streams und eine Lambda-Funktion verarbeitet, um sie einer Amazon CloudSearch-Domain hinzuzufügen. Zusätzlich zum Quellcode für die Lambda-Funktionen enthält dieses Repository eine iOS-Prototyp-Anwendung, die Beispiele zeigt, wie das AWS Mobile SDK für iOS mit den in der Architektur definierten Backend-Ressourcen verknüpft wird. 6 | 7 | ## Ausführen der Beispielanwendung 8 | 9 | Um die vollständige Beispielanwendung auszuführen, müssen Sie zuerst die Backend-Ressourcen bereitstellen und dann die iOS-Beispielanwendung kompilieren und ausführen. 10 | 11 | ### Bereitstellen des Backends 12 | 13 | Die bereitgestellte AWS CloudFormation-Vorlage erstellt die meisten Backend-Ressourcen, die Sie für dieses Beispiel benötigen. Sie müssen jedoch trotzdem die Amazon CloudSearch-Domain, die REST-API in API Gateway und den Identitäts-Pool für Cognito außerhalb der AWS CloudFormation erstellen. 14 | 15 | #### Schritt 1: Erstellen einer CloudSearch-Domain 16 | 17 | 1. Erstellen Sie mithilfe der [AWS CLI](https://aws.amazon.com/cli/) eine neue CloudSearch-Domain mit einem Domain-Namen Ihrer Wahl. 18 | 19 | ``` 20 | aws cloudsearch create-domain --domain-name [YOUR_DOMAIN_NAME] 21 | ``` 22 | 23 | 1. Notieren Sie den ARN der neuen Domain aus dem Ausgabedokument. Sie müssen ihn beim Starten des CloudFormation-Stapels eingeben. 24 | 25 | 1. Definieren Sie Indizes für die Felder "headline" und "note_text". 26 | 27 | ``` 28 | aws cloudsearch define-index-field --name headline --type text --domain-name [YOUR_DOMAIN_NAME] 29 | aws cloudsearch define-index-field --name note_text --type text --domain-name [YOUR_DOMAIN_NAME] 30 | ``` 31 | 32 | ``` 33 | aws cloudsearch index-documents --domain-name [YOUR_DOMAIN_NAME] 34 | ``` 35 | 36 | #### Schritt 2: Erstellen einer REST-API in API Gateway 37 | 38 | 1. Erstellen Sie mithilfe der [AWS CLI](https://aws.amazon.com/cli/) eine neue API mit einem Namen Ihrer Wahl. 39 | 40 | ``` 41 | aws apigateway create-rest-api --name [YOUR_API_NAME] 42 | ``` 43 | 44 | 1. Notieren Sie die "API ID" der neuen Domain aus dem Ausgabedokument. Sie müssen sie beim Starten des CloudFormation-Stapels eingeben. 45 | 46 | #### Schritt 3: Erstellen eines Identitäts-Pools für Amazon Cognito 47 | 48 | 1. Erstellen Sie mithilfe der [AWS CLI](https://aws.amazon.com/cli/) einen neuen Identitäts-Pool mit einem Namen Ihrer Wahl. 49 | 50 | ``` 51 | aws cognito-identity create-identity-pool --allow-unauthenticated-identities --identity-pool-name [YOUR_POOL_NAME] 52 | ``` 53 | 54 | 1. Notieren Sie die "IdentityPoolId" aus dem Ausgabedokument. Sie müssen sie beim Starten des CloudFormation-Stapels als Parameter eingeben. 55 | 56 | #### Schritt 4: Starten der CloudFormation-Vorlage 57 | 58 | Sie können das gesamte Beispiel mithilfe der CloudFormation-Vorlage und dem S3-Bucket für die Region "us-east-1" bereitstellen. Wenn Sie die Vorlage für eine andere Region bereitstellen möchten, müssen Sie einen Amazon S3-Bucket für diese Region erstellen und dann die Vorlage und die Lambda-Funktionsdefinitionen in die Region kopieren. 59 | 60 | Wählen Sie **Launch Stack**, um die Vorlage für die Region "us-east-1" in Ihrem Konto zu starten: 61 | 62 | [![Starten des mobilen Lambda-Backends mit CloudFormation in Nord-Virginia](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/images/cloudformation-launch-stack-button.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=lambda-mobile-backend&templateURL=https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/mobile-backend.template) 63 | 64 | Geben Sie nach Aufforderung die Parameterwerte für die CloudSearch-Domain, die REST-API in API Gateway und die Ressourcen des Identitäts-Pools für Amazon Cognito ein, die Sie in den vorherigen Schritten erstellt haben. 65 | 66 | Details über die mithilfe dieser Vorlage erstellen Ressourcen finden Sie im Abschnitt *Ressourcen der CloudFormation-Vorlage* in diesem Dokument. 67 | 68 | #### Schritt 5: Aktualisieren Ihrer REST-API in API Gateway 69 | 70 | Nachdem Sie den CloudFormation-Stapel erstellt haben, müssen Sie die zuvor erstellte API aktualisieren, damit diese die neu erstellte "NotesApiFunction" verwendet. 71 | 72 | 1. Wählen Sie Ihre API in der [Amazon API Gateway-Konsole](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis) aus. 73 | 1. Wählen Sie **Create Resource** aus, um eine neue, untergeordnete Ressource unter "/" zu erstellen. 74 | 1. Geben Sie "notes" als Ressourcenname und "/notes" als Ressourcenpfad ein. 75 | 1. Wählen Sie **Create Resource** aus. 76 | 1. Wählen Sie die neue Ressource "/notes" und dann **Create Method** aus. 77 | 1. Wählen Sie "POST" aus und aktivieren Sie das Kontrollkästchen. 78 | 1. Wählen Sie als Integrationstyp **Lambda Function** und dann die Region aus, in der Sie den CloudFormation-Stapel als Lambda-Region gestartet haben. 79 | 1. Geben Sie in **Lambda Function** die Zeichenfolge **NotesApiFunction** ein und wählen Sie die vom CloudFormation-Stapel erstellte Funktion aus. 80 | 1. Klicken Sie auf **Save** und erteilen Sie API Gateway die Berechtigung zur Ausführung der Lambda-Funktion. 81 | 1. Klicken Sie auf **Method Request**, um die Anforderungskonfiguration zu bearbeiten. 82 | 1. Wählen Sie als **Authorization type** den Typ "AWS_IAM" aus. 83 | 1. Wählen Sie bei **API Key Required** die Option "true" aus. 84 | 1. Klicken Sie auf **Deploy API**. 85 | 1. Wählen Sie unter **Deployment stage** die Option "New Stage" aus und geben Sie dann bei **Stage name** einen Namen ein. 86 | 1. Notieren Sie die **Invoke URL** für die neue Stufe. Sie benötigen diesen Wert, wenn Sie die iOS-Beispielanwendung ausführen. 87 | 88 | #### Schritt 6: Erstellen eines API-Schlüssels 89 | 90 | 1. Wählen Sie in der [Amazon API Gateway-Konsole](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis) **APIs** und dann **API Keys** aus. 91 | 1. Klicken Sie auf **Create API Key**. 92 | 1. Geben Sie einen Namen für den Schlüssel ein und wählen Sie dann **Enabled** aus. 93 | 1. Klicken Sie auf **Save**. 94 | 1. Wählen Sie im Abschnitt **API Stage Association** Ihre API und dann die Stufe aus, die Sie im vorherigen Schritt erstellt haben. 95 | 1. Klicken Sie auf **Add**. 96 | 1. Notieren Sie den **API key**. Sie benötigen ihn, wenn Sie die mobile Anwendung ausführen. 97 | 98 | #### Schritt 7: Aktualisieren Ihres Identitäts-Pools für Amazon Cognito 99 | 100 | 1. Wählen Sie in der [Amazon Cognito-Konsole](https://console.aws.amazon.com/cognito/home?region=us-east-1) Ihren Identitäts-Pool aus. 101 | 1. Klicken Sie auf **Edit Identity Pool**. 102 | 1. Wählen Sie die vom CloudFormation-Stapel erstellte **MobileClientRole** sowohl für die **Unauthenticated role** als auch für die **Authenticated role** aus. In den Ausgaben des Stapels wird der vollständige ARN für die Rolle bereitgestellt. 103 | 1. Klicken Sie auf **Save Changes**. 104 | 105 | 106 | ### Ausführen der iOS-Beispielanwendung 107 | 108 | #### Voraussetzungen 109 | 110 | Um die bereitgestellte iOS-Beispielanwendung auszuführen, ist Mac OS X 10.10 (Yosemite) oder eine neuere Version erforderlich. Es müssen ebenfalls die neuesten Versionen von [Xcode](https://itunes.apple.com/us/app/xcode/id497799835) und [Cocoa Pods](https://cocoapods.org/) installiert sein. 111 | 112 | #### Erstellen und Ausführen der Anwendung 113 | 114 | 1. Sehen Sie sich den Quellcode für das **ios-sample** in diesem Repository an oder laden Sie ihn herunter. 115 | 1. Aktualisieren Sie "MobileBackendIOS/Constants.swift" dahingehend, dass die Werte für Ihre Backend-Bereitstellung enthalten sind. Sie finden die meisten der Werte in den Ausgaben des CloudFormation-Stapels. Der API Gateway-Schlüssel und die Werte der Endpunkt-URL stehen in der AWS Management Console in den Details Ihrer API zur Verfügung. 116 | 1. Führen Sie Cocoa Pods vom Stammverzeichnis "ios-sample" aus. 117 | 118 | ``` 119 | pod install 120 | ``` 121 | 122 | 1. Öffnen Sie die generierte Datei "MobileBackendIOS.xcworkspace" in Xcode. 123 | 124 | ``` 125 | open -a Xcode MobileBackendIOS.xcworkspace 126 | ``` 127 | 128 | 1. Erstellen Sie das Projekt in Xcode, indem Sie auf die Wiedergabe-Schaltfläche oben im Fenster klicken. 129 | 130 | ## Testen der Anwendung 131 | 132 | Die Beispielanwendung bietet zwei Funktionen: Hochladen eines Bildes und Posten einer Notiz. 133 | 134 | ### Hochladen eines Bildes 135 | 136 | 1. Wählen Sie in der Anwendung **Upload Image** aus. 137 | 1. Wählen Sie das Kamerasymbol und ein Bild aus den eigenen Aufnahmen aus und klicken Sie dann auf **Choose**. 138 | 1. Klicken Sie auf die Schaltfläche **Upload**. 139 | 140 | #### Überprüfen, ob das Bild hochgeladen wurde 141 | 142 | Im Ausgabebereich von Xcode sollten Sie einen Protokolleintrag sehen, der angibt, dass das Bild zu Amazon S3 hochgeladen wurde. 143 | 144 | Sie können ebenfalls den vom CloudFormation-Stapel erstellten Bucket mithilfe der AWS Management Console durchsuchen, um zu überprüfen, ob das Bild ordnungsgemäß hochgeladen wurde. 145 | 146 | ### Posten einer Notiz 147 | 148 | 1. Wählen Sie **Post a Note** aus. 149 | 1. Geben Sie eine Überschrift und Text in die Notiz ein. 150 | 1. Klicken Sie auf **Save Note**. 151 | 152 | #### Überprüfen, ob die Notiz gepostet wurde 153 | 154 | Im Ausgabebereich von Xcode sollten Sie einen Protokolleintrag sehen, der angibt, dass die Notiz erfolgreich gespeichert wurde. 155 | 156 | Beim Hochladen der Notiz wird die "NotesApiFunction" von der mobilen Anwendung aufgerufen. Sie können die Protokolle für diese Funktion in Amazon CloudWatch anzeigen. 157 | 158 | Wenn die Funktion erfolgreich aufgerufen wurde, fügt sie der im CloudFormation-Stapel erstellten DynamoDB-Tabelle einen Eintrag hinzu. Sie können überprüfen, ob die Notiz, die Sie in der Anwendung gepostet haben, in der erstellten Tabelle vorhanden ist. 159 | 160 | Wenn die Notiz in der DynamoDB-Tabelle vorhanden ist, wird dem Stream der Tabelle abschließend ein Datensatz hinzugefügt, der wiederum von der "DynamoStreamHandlerFunction" verarbeitet wird. Sie können die Protokolle für diese Funktion in CloudWatch aufrufen und überprüfen, ob der von Ihnen erstellten CloudSearch-Domain ein neues Dokument hinzugefügt wurde. 161 | 162 | 163 | ## Bereinigen der Anwendungsressourcen 164 | 165 | Gehen Sie wie folgt vor, um alle in diesem Beispiel erstellten Ressourcen zu entfernen: 166 | 167 | 1. Löschen Sie alle Objekte aus dem vom CloudFormation-Stapel erstellten S3-Bucket. 168 | 1. Löschen Sie den CloudFormation-Stapel. 169 | 1. Löschen Sie den Identitäts-Pool für Amazon Cognito, das API Gateway und die CloudSearch-Domain. 170 | 1. Löschen Sie die CloudWatch-Protokollgruppen, die mit den einzelnen vom CloudFormation-Stapel erstellten Lambda-Funktionen verknüpft sind. 171 | 172 | ## Ressourcen der CloudFormation-Vorlage 173 | 174 | ### Lambda-Funktionen 175 | 176 | - **NotesApiFunction** – eine Funktion, die von der mobilen Anwendung gepostete Notizen über das API Gateway verarbeitet. 177 | 178 | - **SearchApiFunction** – eine Funktion, die die CloudSearch-Domain verwendet, um indizierte Notizen basierend auf Suchbegriffen zu finden. 179 | 180 | - **DynamoStreamHandlerFunction** – eine Funktion, die der bereitgestellten CloudSearch-Domain ein indiziertes Dokument basierend auf den Datensätzen im "PhotoNotesTable"-Stream hinzufügt. 181 | 182 | ### Identity and Access Management (IAM)-Rollen 183 | 184 | - **NotesApiRole** – eine Rolle für die "NotesApiFunction". Mit dieser Rolle wird die Berechtigung zur Protokollerstellung und zur Bearbeitung von Elementen im "PhotoNotesTable" erteilt. 185 | 186 | - **SearchApiRole** – eine Rolle für die "SearchApiFunction". Mit dieser Rolle werden Berechtigungen für die Protokollerstellung und die Suche in der bereitgestellten CloudSearch-Domain erteilt. 187 | 188 | - **DynamoStreamHandlerRole** – eine Rolle für die "DynamoStreamHandlerFunction". Mit dieser Rolle werden Berechtigungen für die Protokollerstellung und zum Hinzufügen von Dokumenten in der bereitgestellten CloudSearch-Domain erteilt. 189 | 190 | - **MobileClientRole** – eine Rolle, die von Ihrem Identitäts-Pool für Amazon Cognito sowohl für nicht authentifizierte als auch für authentifizierte Benutzer verwendet wird. Diese Rolle bietet Zugriff auf die bereitgestellte REST-API in API Gateway und erteilt Berechtigungen zum Ablegen von Objekten im "MobileUploadsBucket". 191 | 192 | ### Sonstige Ressourcen 193 | 194 | - **MobileUploadsBucket** – ein S3-Bucket für von Benutzern hochgeladene Fotos. 195 | 196 | - **CloudFrontDistribution** – eine CDN-Verteilung, in der "MobileUploadsBucket" als ein Ausgangspunkt konfiguriert ist. 197 | 198 | - **PhotoNotesTable** – eine DynamoDB-Tabelle, die von Benutzern über die mobile Anwendung hochgeladene Notizen speichert. 199 | 200 | ### Konfiguration 201 | 202 | - **ConfigTable** – eine DynamoDB-Tabelle, die von den verschiedenen Lambda-Funktionen ausgelesene Konfigurationswerte enthält. Der Name dieser Tabelle, "MobileRefArchConfig", ist im Code jeder einzelnen Funktion hardcodiert und kann nicht ohne Aktualisierung des Codes geändert werden. 203 | 204 | - **ConfigHelperStack** – ein untergeordneter Stapel, der eine benutzerdefinierte Ressource zum Schreiben von Einträgen in "ConfigTable" erstellt. Dieser Stapel erstellt eine Lambda-Funktion und eine Ausführungsrolle, die "UpdateItem" eine Berechtigung für "ConfigTable" erteilt. 205 | 206 | - **NotesTableConfig** – ein Konfigurationseintrag, der den Namen "PhotoNotesTable" bezeichnet. 207 | 208 | - **SearchEndpointConfig** – ein Konfigurationseintrag, der den Suchendpunkt der als Parameter übergebenen CloudSearch-Domain bezeichnet. 209 | 210 | - **DocumentEndpointConfig** – ein Konfigurationseintrag, der den Dokumentendpunkt der als Parameter übergebenen CloudSearch-Domain bezeichnet. 211 | 212 | ## Lizensierung 213 | 214 | Dieses Beispiel einer Referenzarchitektur ist unter Apache 2.0 lizensiert. 215 | -------------------------------------------------------------------------------- /README/README-ES.md: -------------------------------------------------------------------------------- 1 | # Arquitectura de referencia sin servidor: Back-end móvil 2 | 3 | ## Introducción 4 | 5 | La arquitectura de referencia Back-end móvil ([diagrama](https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/lambda-refarch-mobilebackend.pdf)) muestra cómo usar [AWS Lambda](http://aws.amazon.com/lambda/) junto con otros servicios para crear un backend sin servidor para una aplicación móvil. La aplicación de ejemplo concreta proporcionada en este repositorio permite a los usuarios cargar fotos y notas mediante Amazon Simple Storage Service (Amazon S3) y Amazon API Gateway, respectivamente. Las notas se almacenan en Amazon DynamoDB y se procesan de forma asincrónica mediante flujos de DynamoDB y una función Lambda para añadirlas a un dominio de Amazon CloudSearch. Además del código fuente de las funciones Lambda, este repositorio contiene también una aplicación iOS modelo que proporciona ejemplos de cómo usar el SDK de AWS Mobile para iOS para interactuar con los recursos del back-end definidos en la arquitectura. 6 | 7 | ## Ejecución del ejemplo 8 | 9 | Para ejecutar la aplicación de ejemplo completa, primero debe implementar los recursos del back-end y, a continuación, compilar y ejecutar la aplicación iOS de ejemplo. 10 | 11 | ### Implementación del back-end 12 | 13 | La plantilla de AWS CloudFormation proporcionada crea la mayoría de los recursos del backend que necesita para este ejemplo, pero tendrá que crear el dominio de Amazon CloudSearch, la API REST de API Gateway y el grupo de identidades de Cognito fuera de AWS CloudFormation. 14 | 15 | #### Paso 1: Crear un dominio de CloudSearch 16 | 17 | 1. Mediante [AWS CLI](https://aws.amazon.com/cli/), cree un nuevo dominio de CloudSearch proporcionando el nombre de dominio que desee. 18 | 19 | ``` 20 | aws cloudsearch create-domain --domain-name [YOUR_DOMAIN_NAME] 21 | ``` 22 | 23 | 1. Anote el ARN del nuevo dominio en el documento de salida. Lo usará como dato de entrada cuando lance la pila de CloudFormation. 24 | 25 | 1. Defina índices para los campos `headline` y `note_text`. 26 | 27 | ``` 28 | aws cloudsearch define-index-field --name headline --type text --domain-name [YOUR_DOMAIN_NAME] 29 | aws cloudsearch define-index-field --name note_text --type text --domain-name [YOUR_DOMAIN_NAME] 30 | ``` 31 | 32 | ``` 33 | aws cloudsearch index-documents --domain-name [YOUR_DOMAIN_NAME] 34 | ``` 35 | 36 | #### Paso 2: Crear una API REST de API Gateway 37 | 38 | 1. Mediante [AWS CLI](https://aws.amazon.com/cli/), cree una nueva API proporcionando el nombre que desee. 39 | 40 | ``` 41 | aws apigateway create-rest-api --name [YOUR_API_NAME] 42 | ``` 43 | 44 | 1. Anote el `API ID` proporcionado en el documento de salida. Lo usará como dato de entrada cuando lance la pila de CloudFormation. 45 | 46 | #### Paso 3: Crear un grupo de identidades de Amazon Cognito 47 | 48 | 1. Mediante [AWS CLI](https://aws.amazon.com/cli/), cree un nuevo grupo de identidades proporcionando el nombre que desee. 49 | 50 | ``` 51 | aws cognito-identity create-identity-pool --allow-unauthenticated-identities --identity-pool-name [YOUR_POOL_NAME] 52 | ``` 53 | 54 | 1. Anote el `IdentityPoolId` del documento de salida. Lo usará como un parámetro cuando lance la pila de CloudFormation. 55 | 56 | #### Paso 4: Lanzar la plantilla de CloudFormation 57 | 58 | Puede implementar todo el ejemplo en la región us-east-1 mediante la plantilla de CloudFormation y el bucket de S3 proporcionados. Si desea implementar la plantilla en otra región, debe crear un bucket de Amazon S3 en esa región y después copiar en él la plantilla y las definiciones de funciones Lambda. 59 | 60 | Elija **Launch Stack** para lanzar la plantilla en la región us-east-1 de su cuenta: 61 | 62 | [![Launch Lambda Mobile Backend into North Virginia with CloudFormation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/images/cloudformation-launch-stack-button.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=lambda-mobile-backend&templateURL=https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/mobile-backend.template) 63 | 64 | Cuando se le pida, escriba los valores de parámetro de los recursos de dominio de CloudSearch, API REST de API Gateway y grupo de identidades de Amazon Cognito que creó en los pasos anteriores. 65 | 66 | En la sección *Recursos de la plantilla de CloudFormation* de este documento encontrará información detallada sobre los recursos creados por esta plantilla. 67 | 68 | #### Paso 5: Actualizar la API REST de API Gateway 69 | 70 | Una vez creada la pila de CloudFormation, necesita actualizar la API que creó previamente para que use la función `NotesApiFunction` recién creada. 71 | 72 | 1. En la [consola de Amazon API Gateway](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis), elija su API. 73 | 1. Elija **Create Resource** para crear un recurso secundario bajo /. 74 | 1. Escriba `notes` como el nombre del recurso y `/notes` como la ruta del recurso. 75 | 1. Elija **Create Resource**. 76 | 1. Con el nuevo recurso `/notes` seleccionado, elija **Create Method**. 77 | 1. Elija `POST` y active la casilla de verificación. 78 | 1. Elija **Lambda Function** como tipo de integración y después elija la región donde lanzó la pila de CloudFormation como la región Lambda. 79 | 1. en **Lambda Function**, escriba **`NotesApiFunction`** y seleccione la función creada por la pila de CloudFormation. 80 | 1. Elija **Save** y otorgue permisos a API Gateway para ejecutar la función Lambda. 81 | 1. Elija **Method Request** para editar la configuración de la solicitud. 82 | 1. Para **Authorization type**, seleccione `AWS_IAM`. 83 | 1. Para **API Key Required**, seleccione `true`. 84 | 1. Elija **Deploy API**. 85 | 1. Para **Deployment stage**, elija `New Stage` y después escriba un nombre en **Stage name**. 86 | 1. Anote el valor de **Invoke URL** para la nueva etapa. Usará este valor cuando ejecute la aplicación iOS de ejemplo. 87 | 88 | #### Paso 6: Crear una clave API 89 | 90 | 1. En la [consola de Amazon API Gateway](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis), elija **APIs** y después **API Keys**. 91 | 1. Elija **Create API Key**. 92 | 1. Escriba un nombre para la clave y seleccione **Enabled**. 93 | 1. Elija **Save** 94 | 1. En la sección **API Stage Association**, elija su API y después elija la etapa que creó en el paso anterior. 95 | 1. Elija **Add**. 96 | 1. Anote el valor de **API key**. Usará este valor cuando ejecute la aplicación móvil. 97 | 98 | #### Paso 7: Actualizar el grupo de identidades de Amazon Cognito 99 | 100 | 1. En la [consola de Amazon Cognito](https://console.aws.amazon.com/cognito/home?region=us-east-1), seleccione el grupo de identidades. 101 | 1. Elija **Edit Identity Pool**. 102 | 1. Para **Unauthenticated role** y **Authenticated role**, seleccione el **MobileClientRole** creado por la pila de CloudFormation. El ARN completo del rol se proporciona en los resultados de la pila. 103 | 1. Elija **Save Changes**. 104 | 105 | 106 | ### Ejecución de la aplicación iOS de ejemplo 107 | 108 | #### Requisitos previos 109 | 110 | Para ejecutar la aplicación iOS de ejemplo proporcionada, debe ejecutar Mac OS X 10.10 (Yosemite) o una versión más reciente. También debe tener la última versión de [Xcode](https://itunes.apple.com/us/app/xcode/id497799835) y [Cocoa Pods](https://cocoapods.org/) instalada. 111 | 112 | #### Compilación y ejecución de la aplicación 113 | 114 | 1. Revise o descargue el código fuente de **ios-sample** de este repositorio. 115 | 1. Actualice `MobileBackendIOS/Constants.swift` con los valores de su implementación del back-end. La mayoría de los valores se encuentran en los resultados de la pila de CloudFormation. Los valores de URL de punto de enlace y clave de API Gateway están disponibles para su API en la consola de administración de AWS. 116 | 1. Ejecute Cocoa Pods desde el directorio raíz `ios-sample`. 117 | 118 | ``` 119 | pod install 120 | ``` 121 | 122 | 1. Abra el archivo `MobileBackendIOS.xcworkspace` generado en Xcode. 123 | 124 | ``` 125 | open -a Xcode MobileBackendIOS.xcworkspace 126 | ``` 127 | 128 | 1. Compile y ejecute el proyecto en Xcode haciendo clic en el botón de reproducción situado en la parte superior de la ventana. 129 | 130 | ## Probar la aplicación 131 | 132 | La aplicación de ejemplo proporciona dos funcionalidades: cargar una imagen y publicar una nota. 133 | 134 | ### Para cargar una imagen 135 | 136 | 1. Elija **Upload Image** en la aplicación. 137 | 1. Elija el icono de cámara, seleccione una imagen del álbum de la cámara y después elija **Choose**. 138 | 1. Elija el botón **Upload**. 139 | 140 | #### Comprobar que la imagen se ha cargado 141 | 142 | Debería ver una entrada de log en la que se indica que la imagen se ha cargado en Amazon S3 en el panel de resultados de Xcode. 143 | 144 | También puede examinar el bucket creado por la pila de CloudFormation mediante la consola de administración de AWS para comprobar que la imagen se ha cargado correctamente. 145 | 146 | ### Para publicar una nota 147 | 148 | 1. Elija **Post a Note**. 149 | 1. En la nota, escriba un título y el texto. 150 | 1. Elija **Save Note**. 151 | 152 | #### Comprobar que la nota se ha publicado 153 | 154 | Debería ver una entrada de log en la que se indica que la nota se ha guardado correctamente en el panel de resultados de Xcode. 155 | 156 | Cuando se carga la nota, la aplicación móvil llama a `NotesApiFunction`. Puede ver los logs de esta función en Amazon CloudWatch. 157 | 158 | Cuando la función se invoca correctamente, añade una entrada a la tabla de DynamoDB creada en la pila de CloudFormation. Puede verificar que la nota publicada en la aplicación permanece en la tabla creada. 159 | 160 | Por último, si la nota permanece en la tabla DynamoDB, se añade un registro al flujo de la tabla que, a su vez, procesa la función `DynamoStreamHandlerFunction`. Puede ver los logs de esta función en CloudWatch y verificar que se ha añadido un nuevo documento al dominio de CloudSearch que ha creado. 161 | 162 | 163 | ## Borrado de los recursos de la aplicación 164 | 165 | Para eliminar todos los recursos creados por este ejemplo, proceda del modo siguiente: 166 | 167 | 1. Elimine todos los objetos del bucket de S3 creados por la pila de CloudFormation. 168 | 1. Elimine la pila de CloudFormation. 169 | 1. Elimine el grupo de identidades de Amazon Cognito, API Gateway y el dominio de CloudSearch. 170 | 1. Elimine los grupos de logs de CloudWatch asociados a cada función Lambda creada por la pila de CloudFormation. 171 | 172 | ## Recursos de la plantilla de CloudFormation 173 | 174 | ### Funciones Lambda 175 | 176 | - **NotesApiFunction**: una función para gestionar la notas publicadas desde la aplicación móvil a través de API Gateway. 177 | 178 | - **SearchApiFunction**: una función que usa el dominio de CloudSearch para buscar notas indexadas a partir de términos de búsqueda. 179 | 180 | - **DynamoStreamHandlerFunction**: una función que añade un documento indexado al dominio de CloudSearch proporcionado en función de los registros del flujo de `PhotoNotesTable`. 181 | 182 | ### Roles de AWS Identity and Access Management (IAM) 183 | 184 | - **NotesApiRole**: un rol para la función `NotesApiFunction`. Este rol otorga permiso para registrar y trabajar con los elementos de `PhotoNotesTable`. 185 | 186 | - **SearchApiRole**: un rol para la función `SearchApiFunction`. Este rol otorga permisos para registrar y buscar el dominio de CloudSearch proporcionado. 187 | 188 | - **DynamoStreamHandlerRole**: un rol para la función DynamoStreamHandlerFunction. Este rol otorga permisos para registrar y añadir documentos al dominio de CloudSearch proporcionado. 189 | 190 | - **MobileClientRole**: un rol usado por su grupo de identidades de Amazon Cognito para los usuarios autenticados y no autenticados. Este rol proporciona acceso a la API REST de API Gateway, además de permisos para colocar objetos en `MobileUploadsBucket`. 191 | 192 | ### Otros recursos 193 | 194 | - **MobileUploadsBucket**: un bucket de S3 para las fotos cargadas por los usuarios. 195 | 196 | - **CloudFrontDistribution**: una distribución de CDN con `MobileUploadsBucket` configurado como origen. 197 | 198 | - **PhotoNotesTable**: una tabla de DynamoDB que almacena las notas cargadas por los usuarios desde la aplicación móvil. 199 | 200 | ### Configuración 201 | 202 | - **ConfigTable**: una tabla de DynamoDB para almacenar los valores de configuración leídos por las distintas funciones Lambda. El nombre de esta tabla, "MobileRefArchConfig", está codificado de forma rígida en el código de cada función y no se puede modificar sin actualizar también el código. 203 | 204 | - **ConfigHelperStack**: una pila secundaria que crea un recurso personalizado para escribir entradas en `ConfigTable`. Esta pila crea una función Lambda y un rol de ejecución que otorga permiso a UpdateItem en `ConfigTable`. 205 | 206 | - **NotesTableConfig**: una entrada de configuración que identifica el nombre `PhotoNotesTable`. 207 | 208 | - **SearchEndpointConfig**: una entrada de configuración que identifica el punto de enlace de búsqueda del dominio de CloudSearch pasado como parámetro. 209 | 210 | - **DocumentEndpointConfig**: una entrada de configuración que identifica el punto de enlace de documento del dominio de CloudSearch pasado como parámetro. 211 | 212 | ## Licencia 213 | 214 | Este ejemplo de arquitectura de referencia tiene licencia de Apache 2.0. 215 | -------------------------------------------------------------------------------- /README/README-FR.md: -------------------------------------------------------------------------------- 1 | # Architecture de référence sans serveur : Backend mobile 2 | 3 | ## Introduction 4 | 5 | L'architecture de référence de backend mobile ([diagramme](https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/lambda-refarch-mobilebackend.pdf)) montre comment utiliser [AWS Lambda](http://aws.amazon.com/lambda/) avec d'autres services pour créer un backend sans serveur pour une application mobile. L'exemple d'application spécifique fourni dans ce référentiel permet aux utilisateurs de charger des photos et des notes à l'aide d'Amazon Simple Storage Service (Amazon S3) et Amazon API Gateway respectivement. Les notes sont stockées dans Amazon DynamoDB, et sont traitées en mode asynchrone à l'aide de flux DynamoDB et d'une fonction Lambda pour les ajouter dans un domaine Amazon CloudSearch. En plus du code source pour les fonctions Lambda, ce référentiel contient un prototype d'application iOS qui fournit des exemples montrant comment utiliser l'AWS Mobile SDK pour iOS afin de servir d'interface avec les ressources backend définies dans l'architecture. 6 | 7 | ## Exécution de l'exemple 8 | 9 | Pour exécuter l'exemple d'application complet, vous devez d'abord déployer les ressources de backend, puis compiler et lancer l'exemple d'application iOS. 10 | 11 | ### Déploiement du backend 12 | 13 | Le template AWS CloudFormation fourni crée la plupart des ressources backend dont vous avez besoin dans cet exemple, mais vous devrez encore créer le domaine Amazon CloudSearch, l'API Gateway REST API et le pool d'identités Cognito en dehors d'AWS CloudFormation. 14 | 15 | #### Étape 1 : Créer un domaine CloudSearch 16 | 17 | 1. À l'aide de [AWS CLI](https://aws.amazon.com/cli/), créez un domaine CloudSearch en fournissant le nom de domaine de votre choix. 18 | 19 | ``` 20 | aws cloudsearch create-domain --domain-name [YOUR_DOMAIN_NAME] 21 | ``` 22 | 23 | 1. Notez l'ARN du nouveau domaine dans le document de sortie. Vous l'utiliserez comme entrée lors du lancement de la stack CloudFormation. 24 | 25 | 1. Définissez des index pour les champs `headline` et `note_text`. 26 | 27 | ``` 28 | aws cloudsearch define-index-field --name headline --type text --domain-name [YOUR_DOMAIN_NAME] 29 | aws cloudsearch define-index-field --name note_text --type text --domain-name [YOUR_DOMAIN_NAME] 30 | ``` 31 | 32 | ``` 33 | aws cloudsearch index-documents --domain-name [YOUR_DOMAIN_NAME] 34 | ``` 35 | 36 | #### Étape 2 : Créer une API Gateway REST API 37 | 38 | 1. À l'aide de [AWS CLI](https://aws.amazon.com/cli/), créez une API en fournissant le nom de votre choix. 39 | 40 | ``` 41 | aws apigateway create-rest-api --name [YOUR_API_NAME] 42 | ``` 43 | 44 | 1. Notez l'`API ID` fourni dans le document de sortie. Vous l'utiliserez comme entrée lors du lancement de la stack CloudFormation. 45 | 46 | #### Étape 3 : Créer un pool d'identités Amazon Cognito 47 | 48 | 1. À l'aide de [AWS CLI](https://aws.amazon.com/cli/), créez un pool d'identités en fournissant le nom de votre choix. 49 | 50 | ``` 51 | aws cognito-identity create-identity-pool --allow-unauthenticated-identities --identity-pool-name [YOUR_POOL_NAME] 52 | ``` 53 | 54 | 1. Notez la valeur de `IdentityPoolId` dans le document de sortie. Vous l'utiliserez comme paramètre lors du lancement de la stack CloudFormation. 55 | 56 | #### Étape 4 : Lancer le template CloudFormation 57 | 58 | Vous pouvez déployer la totalité de l'exemple dans la région us-east-1 à l'aide du template CloudFormation fourni et du bucket (compartiment) S3. Si vous souhaitez déployer le template dans une autre région, vous devez créer un bucket (compartiment) Amazon S3 dans cette région, puis copier le template et les définitions de fonction Lambda dans celui-ci. 59 | 60 | Sélectionnez **Launch Stack** pour lancer le template dans la région us-east-1 dans votre compte : 61 | 62 | [![Lancement de fonction Lambda Backend mobile en Virginie du Nord) avec CloudFormation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/images/cloudformation-launch-stack-button.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=lambda-mobile-backend&templateURL=https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/mobile-backend.template) 63 | 64 | À l'invite, entrez les valeurs de paramètre pour les ressources de domaine CloudSearch, d'API Gateway REST API et de pool d'identités Amazon Cognito que vous avez créées dans les étapes précédentes. 65 | 66 | Vous trouverez des détails sur les ressources créées par ce template dans la section *Ressources de template CloudFormation* de ce document. 67 | 68 | #### Étape 5 : Mettre à jour votre API Gateway REST API 69 | 70 | Une fois que vous avez créé la stack CloudFormation, vous devez mettre à jour l'API que vous avez créée précédemment pour utiliser la `NotesApiFunction` nouvellement créée. 71 | 72 | 1. Sur la [console Amazon API Gateway](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis), sélectionnez votre API. 73 | 1. Sélectionnez **Create Resource** pour créer une ressource enfant sous /. 74 | 1. Tapez `notes` comme nom de ressource et `/notes` comme chemin de ressource. 75 | 1. Sélectionnez **Create Resource**. 76 | 1. Avec la nouvelle ressource `/notes` sélectionnée, choisissez **Create Method**. 77 | 1. Sélectionnez `POST` et cochez la case. 78 | 1. Sélectionnez **Lambda Function** comme type d'intégration et choisissez la région dans laquelle vous avez lancé la stack CloudFormation comme région Lambda. 79 | 1. dans **Lambda Function**, tapez **`NotesApiFunction`** et sélectionnez la fonction créée par la stack CloudFormation. 80 | 1. Sélectionnez **Save** et accordez les permissions API Gateway pour exécuter la fonction Lambda. 81 | 1. Sélectionnez **Method Request** pour modifier la configuration de demande. 82 | 1. Pour **Authorization type**, sélectionnez `AWS_IAM`. 83 | 1. Pour **API Key Required**, sélectionnez `true`. 84 | 1. Sélectionnez **Deploy API**. 85 | 1. Pour **Deployment stage**, sélectionnez `New Stage` et tapez un nom dans **Stage name**. 86 | 1. Notez la valeur de **Invoke URL** pour la nouvelle étape. Vous utiliserez cette valeur lors de l'exécution de l'exemple d'application iOS. 87 | 88 | #### Étape 6 : Créer une clé API 89 | 90 | 1. Dans la [console Amazon API Gateway](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis), sélectionnez **APIs**, puis **API Keys**. 91 | 1. Sélectionnez **Create API Key**. 92 | 1. Entrez un nom pour la clé, puis sélectionnez **Enabled**. 93 | 1. Sélectionnez **Save** 94 | 1. Dans la section **API Stage Association**, sélectionnez votre API, puis l'étape que vous avez créée dans l'étape précédente. 95 | 1. Sélectionnez **Add**. 96 | 1. Notez la valeur de **API key**. Vous l'utiliserez lors de l'exécution de l'application mobile. 97 | 98 | #### Étape 7 : Mettre à jour votre pool d'identités Amazon Cognito 99 | 100 | 1. Sur la [console Amazon Cognito](https://console.aws.amazon.com/cognito/home?region=us-east-1), sélectionnez votre pool d'identités. 101 | 1. Sélectionnez **Edit Identity Pool**. 102 | 1. Pour **Unauthenticated role** et **Authenticated role**, sélectionnez le rôle **MobileClientRole** créé par la stack CloudFormation. L'ARN complet pour le rôle est fourni dans les sorties de la stack. 103 | 1. Sélectionnez **Save Changes**. 104 | 105 | 106 | ### Exécution de l'exemple d'application iOS 107 | 108 | #### Prérequis 109 | 110 | Pour exécuter l'exemple d'application iOS fourni, vous devez utiliser Mac OS X 10.10 (Yosemite) ou une version plus récente. La dernière version de [Xcode](https://itunes.apple.com/us/app/xcode/id497799835) et [Cocoa Pods](https://cocoapods.org/) doit également être installée. 111 | 112 | ### Création et exécution de l'application 113 | 114 | 1. Consultez ou téléchargez le code source pour l'exemple **ios-sample** dans ce référentiel. 115 | 1. Mettez à jour `MobileBackendIOS/Constants.swift` avec les valeurs pour votre déploiement de backend. La plupart des valeurs peuvent être trouvées dans les sorties de la stack CloudFormation. Les valeurs de clé d'API Gateway et d'URL d'Endpoint sont disponibles dans les détails de votre API dans AWS Management Console. 116 | 1. Exécutez Cocoa Pods à partir de la racine du répertoire `ios-sample`. 117 | 118 | ``` 119 | pod install 120 | ``` 121 | 122 | 1. Ouvrez le fichier `MobileBackendIOS.xcworkspace` généré dans Xcode. 123 | 124 | ``` 125 | open -a Xcode MobileBackendIOS.xcworkspace 126 | ``` 127 | 128 | 1. Créez et exécutez le projet depuis Xcode en cliquant sur le bouton de lecture en haut de la fenêtre. 129 | 130 | ## Test de l'application 131 | 132 | L'exemple d'application fournit deux fonctions : chargement d'une image et publication d'une note. 133 | 134 | ### Pour charger une image 135 | 136 | 1. Sélectionnez **Upload Image** dans l'application. 137 | 1. Sélectionnez l'icône de caméra, choisissez une image dans la pellicule, puis sélectionnez **Choose**. 138 | 1. Sélectionnez le bouton **Upload**. 139 | 140 | #### Confirmation que l'image a été chargée 141 | 142 | Vous devez voir une entrée de journal indiquant que l'image a été chargée dans Amazon S3 dans le volet de sortie de Xcode. 143 | 144 | Vous pouvez également parcourir le bucket (compartiment) créé par la stack CloudFormation à l'aide d'AWS Management Console pour vérifier que l'image a été chargée correctement. 145 | 146 | ### Pour publier une note 147 | 148 | 1. Sélectionnez **Post a Note**. 149 | 1. Dans la note, tapez un titre et un texte. 150 | 1. Sélectionnez **Save Note**. 151 | 152 | #### Confirmation que la note a été publiée 153 | 154 | Vous devez voir une entrée de journal indiquant que la note a été enregistrée avec succès dans le volet de sortie de Xcode. 155 | 156 | Lorsque la note est chargée, la fonction `NotesApiFunction` est appelée par l'application mobile. Vous pouvez afficher les journaux pour cette fonction dans Amazon CloudWatch. 157 | 158 | Lorsque la fonction est appelée avec succès, elle ajoute une entrée dans la table DynamoDB créée dans la stack CloudFormation. Vous pouvez vérifier que la note que vous avez publiée dans l'application a été conservée dans la table créée. 159 | 160 | Enfin, lorsque la note est conservée dans la table DynamoDB, un enregistrement est ajouté au flux de la table qui sera à son tour traité par la fonction `DynamoStreamHandlerFunction`. Vous pouvez afficher les journaux pour cette fonction dans CloudWatch et vérifier qu'un nouveau document a été ajouté dans le domaine CloudSearch que vous avez créé. 161 | 162 | 163 | ## Nettoyage des ressources de l'application 164 | 165 | Pour supprimer toutes les ressources créées par cet exemple, procédez comme suit : 166 | 167 | 1. Supprimez tous les objets du bucket (compartiment) S3 créé par la stack CloudFormation. 168 | 1. Supprimez la stack CloudFormation. 169 | 1. Supprimez le pool d'identités Amazon Cognito, l'API Gateway et le domaine CloudSearch. 170 | 1. Supprimez les groupes de journaux CloudWatch associés à la fonction Lambda créée par la stack CloudFormation. 171 | 172 | ## Ressources du template CloudFormation 173 | 174 | ### Fonctions Lambda 175 | 176 | - **NotesApiFunction** - Une fonction qui traite les notes publiées à partir de l'application mobile via API Gateway. 177 | 178 | - **SearchApiFunction** - Une fonction qui utilise le domaine CloudSearch pour trouver des notes indexées en fonction de termes de recherche. 179 | 180 | - **DynamoStreamHandlerFunction** - Une fonction qui ajoute un document indexé au domaine CloudSearch fourni en fonction d'enregistrements dans le flux `PhotoNotesTable`. 181 | 182 | ### Rôles AWS Identity and Access Management (IAM) 183 | 184 | - **NotesApiRole** - Un rôle pour la fonction `NotesApiFunction`. Ce rôle accorde la permission permettant de se connecter et d'utiliser des éléments de la table `PhotoNotesTable`. 185 | 186 | - **SearchApiRole** - Un rôle pour la fonction `SearchApiFunction`. Ce rôle accorde des permissions pour se connecter et faire des recherches dans le domaine CloudSearch fourni. 187 | 188 | - **DynamoStreamHandlerRole** - Un rôle pour la fonction DynamoStreamHandlerFunction. Ce rôle accorde des permissions pour se connecter et ajouter des documents dans le domaine CloudSearch fourni. 189 | 190 | - **MobileClientRole** - Un rôle utilisé par votre pool d'identités Amazon Cognito pour les utilisateurs non authentifiés et authentifiés. Ce rôle donne accès à l'API Gateway REST API fournis et accorde des permissions pour placer des objets dans le bucket (compartiment) `MobileUploadsBucket`. 191 | 192 | ### Autres ressources 193 | 194 | - **MobileUploadsBucket** - Un bucket (compartiment) S3 pour les photos chargées des utilisateurs. 195 | 196 | - **CloudFrontDistribution** - Une distribution CDN avec le bucket (compartiment) `MobileUploadsBucket` configuré comme origine. 197 | 198 | - **PhotoNotesTable** - Une table DynamoDB qui stocke les notes chargées par les utilisateurs à partir de l'application mobile. 199 | 200 | ### Configuration 201 | 202 | - **ConfigTable** - Une table DynamoDB qui stocke les valeurs de configuration lues par les différentes fonctions Lambda. Le nom de cette table, « MobileRefArchConfig », est codé de manière irréversible dans le code de chaque fonction et ne peut pas être modifié sans mettre également à jour le code. 203 | 204 | - **ConfigHelperStack** - Une sous-stack qui crée une ressource personnalisée pour écrire des entrées dans la table `ConfigTable`. Cette stack crée une fonction Lambda et un rôle d'exécution qui accorde la permission UpdateItem sur la table `ConfigTable`. 205 | 206 | - **NotesTableConfig** - Une entrée de configuration qui identifie le nom de `PhotoNotesTable`. 207 | 208 | - **SearchEndpointConfig** - Une entrée de configuration qui identifie l'endpoint de la recherche (Search) du domaine CloudSearch transmis comme paramètre. 209 | 210 | - **DocumentEndpointConfig** - Une entrée de configuration qui identifie l'Endpoint de document du domaine CloudSearch transmis comme paramètre. 211 | 212 | ## Licence 213 | 214 | Cet exemple d'architecture de référence est fourni sous licence sous Apache 2.0. 215 | -------------------------------------------------------------------------------- /README/README-IT.md: -------------------------------------------------------------------------------- 1 | # Architettura di riferimento senza server: backend per dispositivi mobili 2 | 3 | ## Introduzione 4 | 5 | L'architettura di riferimento per backend per dispositivi mobili ([diagramma](https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/lambda-refarch-mobilebackend.pdf)) mostra come utilizzare [AWS Lambda](http://aws.amazon.com/lambda/) insieme ad altri servizi per creare un backend senza server per un'applicazione per dispositivi mobili. La specifica applicazione fornita come esempio in questo repository consente agli utenti di caricare foto e appunti utilizzando rispettivamente Amazon Simple Storage Service (Amazon S3) e Amazon API Gateway. Gli appunti vengono archiviati in Amazon DynamoDB ed elaborati in modo asincrono tramite i flussi di DynamoDB e una funzione di Lambda che consente di aggiungerli a un dominio di Amazon CloudSearch. Oltre al codice sorgente per le funzioni di Lambda, questo repository contiene anche un'applicazione iOS modello che fornisce esempi di come utilizzare AWS Mobile SDK per iOS per interfacciarsi con le risorse di backend definite nell'architettura. 6 | 7 | ## Esecuzione dell'esempio 8 | 9 | Per eseguire l'intera applicazione esempio, è necessario per prima cosa distribuire le risorse di backend e successivamente compilare ed eseguire l'applicazione iOS esempio. 10 | 11 | ### Distribuzione del backend 12 | 13 | Il modello di AWS CloudFormation fornito crea la maggior parte delle risorse di backend necessaria per questo esempio; tuttavia è necessario creare il dominio di Amazon CloudSearch, l'API REST di API Gateway e il pool di identità di Cognito al di fuori di AWS CloudFormation. 14 | 15 | #### Passaggio 1: creare un dominio di CloudSearch 16 | 17 | 1. Tramite [AWS CLI](https://aws.amazon.com/cli/), creare un nuovo dominio di CloudSearch e assegnargli un nome a scelta. 18 | 19 | ``` 20 | aws cloudsearch create-domain --domain-name [YOUR_DOMAIN_NAME] 21 | ``` 22 | 23 | 1. Prendere nota dell'ARN del nuovo dominio nel documento di output. L'ARN verrà utilizzato come input per avviare lo stack di CloudFormation. 24 | 25 | 1. Definire gli indici per i campi "headline" e "note_text". 26 | 27 | ``` 28 | aws cloudsearch define-index-field --name headline --type text --domain-name [YOUR_DOMAIN_NAME] 29 | aws cloudsearch define-index-field --name note_text --type text --domain-name [YOUR_DOMAIN_NAME] 30 | ``` 31 | 32 | ``` 33 | aws cloudsearch index-documents --domain-name [YOUR_DOMAIN_NAME] 34 | ``` 35 | 36 | #### Passaggio 2: creare un'API REST di API Gateway 37 | 38 | 1. Tramite [AWS CLI](https://aws.amazon.com/cli/), creare una nuova API e assegnarle un nome a scelta. 39 | 40 | ``` 41 | aws apigateway create-rest-api --name [YOUR_API_NAME] 42 | ``` 43 | 44 | 1. Prendere nota dell'"API ID" fornito nel documento di output. Questo valore verrà utilizzato come input per avviare lo stack di CloudFormation. 45 | 46 | #### Passaggio 3: creare un pool di identità di Amazon Cognito 47 | 48 | 1. Tramite [AWS CLI](https://aws.amazon.com/cli/), creare un nuovo pool di identità e assegnargli un nome a scelta. 49 | 50 | ``` 51 | aws cognito-identity create-identity-pool --allow-unauthenticated-identities --identity-pool-name [YOUR_POOL_NAME] 52 | ``` 53 | 54 | 1. Prendere nota dell'"IdentityPoolId" nel documento di output. Questo valore verrà utilizzato come parametro per avviare lo stack di CloudFormation. 55 | 56 | #### Passaggio 4: avviare il modello di CloudFormation 57 | 58 | È possibile distribuire l'intero esempio nella regione Stati Uniti orientali 1 utilizzando il modello di CloudFormation e il bucket S3 forniti. Se si desidera distribuire il modello in un'altra regione, è necessario creare un bucket Amazon S3 in quella regione, quindi copiarvi il modello e le definizioni delle funzioni di Lambda. 59 | 60 | Selezionare **Launch Stack** per avviare il modello nella regione Stati Uniti orientali 1 nell'account personale: 61 | 62 | [![Launch Lambda Mobile Backend into North Virginia with CloudFormation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/images/cloudformation-launch-stack-button.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=lambda-mobile-backend&templateURL=https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/mobile-backend.template) 63 | 64 | Quando richiesto, inserire i parametri per il dominio di CloudSearch, l'API REST di API Gateway e le risorse del pool di identità di Amazon Cognito creati nei passaggi precedenti. 65 | 66 | I dettagli sulle risorse create da questo modello sono disponibili nella sezione *CloudFormation Template Resources* di questo documento. 67 | 68 | #### Passaggio 5: aggiornare l'API REST di API Gateway 69 | 70 | Dopo aver creato lo stack di CloudFormation, è necessario aggiornare l'API creata in precedenza, per utilizzare la nuova "NotesApiFunction". 71 | 72 | 1. Selezionare l'API nella [Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis). 73 | 1. Selezionare **Create Resource** per creare una nuova risorsa secondaria in /. 74 | 1. Digitare "notes" come nome della risorsa e "/notes" come percorso della risorsa. 75 | 1. Selezionare **Create Resource**. 76 | 1. Dopo aver selezionato la nuova risorsa "/notes", selezionare **Create Method**. 77 | 1. Selezionare "POST" e spuntare la casella. 78 | 1. Selezionare **Lambda Function** come tipo di integrazione, quindi selezionare la regione in cui è stato avviato lo stack di CloudFormation come regione per Lambda. 79 | 1. In **Lambda Function**, digitare **"NotesApiFunction"** e selezionare la funzione creata dallo stack di CloudFormation. 80 | 1. Selezionare **Save** e assegnare ad API Gateway le autorizzazioni per eseguire la funzione di Lambda. 81 | 1. Selezionare **Method Request** per modificare la configurazione della richiesta. 82 | 1. Per **Authorization type**, selezionare "AWS_IAM". 83 | 1. Per **API Key Required**, selezionare "true". 84 | 1. Selezionare **Deploy API**. 85 | 1. Per **Deployment stage**, selezionare "New Stage", quindi digitare un nome in **Stage name**. 86 | 1. Prendere nota del valore **Invoke URL** per la nuova fase. Questo valore verrà utilizzato durante l'esecuzione dell'app iOS esempio. 87 | 88 | #### Passaggio 6: creare una chiave API 89 | 90 | 1. Nella [Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis), selezionare **APIs**, quindi **API Keys**. 91 | 1. Selezionare **Create API Key**. 92 | 1. Digitare un nome per la chiave, quindi selezionare **Enabled**. 93 | 1. Selezionare **Save**. 94 | 1. Nella sezione **API Stage Association**, selezionare l'API e la fase creata nel passaggio precedente. 95 | 1. Selezionare **Add**. 96 | 1. Prendere nota del valore **API key**. Questo valore verrà utilizzato durante l'esecuzione dell'applicazione per dispositivi mobili. 97 | 98 | #### Passaggio 7: aggiornare il pool di identità di Amazon Cognito 99 | 100 | 1. Selezionare il pool di identità nella [Amazon Cognito Console](https://console.aws.amazon.com/cognito/home?region=us-east-1). 101 | 1. Selezionare **Edit Identity Pool**. 102 | 1. Per **Unauthenticated role** e per **Authenticated role**, selezionare **MobileClientRole** creato dallo stack di CloudFormation. L'ARN completo per il ruolo viene fornito negli output dello stack. 103 | 1. Selezionare **Save Changes**. 104 | 105 | 106 | ### Esecuzione dell'applicazione iOS esempio 107 | 108 | #### Prerequisiti 109 | 110 | Per eseguire l'applicazione iOS esempio fornita, è necessario disporre di Mac OS X 10.10 (Yosemite) o versione più recente. Inoltre, deve essere installata la versione più recente di [Xcode](https://itunes.apple.com/us/app/xcode/id497799835) e [Cocoa Pods](https://cocoapods.org/). 111 | 112 | #### Creazione ed esecuzione dell'applicazione 113 | 114 | 1. Controllare o scaricare il codice sorgente per **ios-sample** in questo repository. 115 | 1. Aggiornare "MobileBackendIOS/Constants.swift" con i valori per la distribuzione del backend. La maggior parte dei valori è disponibile negli output dello stack di CloudFormation. I valori dell'URL degli endpoint e della chiave di API Gateway sono disponibili nei dettagli per l'API personale nella console di gestione AWS. 116 | 1. Eseguire Cocoa Pods dalla directory "ios-sample" radice. 117 | 118 | ``` 119 | pod install 120 | ``` 121 | 122 | 1. Aprire il file "MobileBackendIOS.xcworkspace" creato in Xcode. 123 | 124 | ``` 125 | open -a Xcode MobileBackendIOS.xcworkspace 126 | ``` 127 | 128 | 1. Creare ed eseguire il progetto da Xcode facendo clic sul pulsante di riproduzione nella parte superiore della finestra. 129 | 130 | ## Test dell'applicazione 131 | 132 | L'applicazione esempio fornisce due caratteristiche: il caricamento di un'immagine e la pubblicazione di appunti. 133 | 134 | ### Per caricare un'immagine 135 | 136 | 1. Selezionare **Upload Image** nell'applicazione. 137 | 1. Selezionare l'icona della fotocamera, un'immagine dal rullino della fotocamera, quindi **Choose**. 138 | 1. Selezionare il pulsante **Upload**. 139 | 140 | #### Verifica del caricamento dell'immagine 141 | 142 | Nel pannello di output di Xcode, viene visualizzata una voce di registro che indica che l'immagine è stata caricata su Amazon S3. 143 | 144 | Per verificare il corretto caricamento dell'immagine, è inoltre possibile sfogliare il bucket creato dallo stack di CloudFormation utilizzando la console di gestione AWS. 145 | 146 | ### Per pubblicare appunti 147 | 148 | 1. Selezionare **Post a Note**. 149 | 1. Negli appunti, digitare l'intestazione e il testo. 150 | 1. Selezionare **Save Note**. 151 | 152 | #### Verifica della pubblicazione degli appunti 153 | 154 | Nel pannello di output di Xcode, viene visualizzata una voce di registro che indica che gli appunti sono stati salvati correttamente. 155 | 156 | Dopo il caricamento degli appunti, la "NotesApiFunction" viene richiamata dall'applicazione per dispositivi mobili. È possibile visualizzare i registri per questa funzione in Amazon CloudWatch. 157 | 158 | Dopo il richiamo della funzione, viene aggiunta uno voce alla tabella DynamoDB creata nello stack di CloudFormation. È possibile verificare che gli appunti pubblicati nell'applicazione siano stati conservati nella tabella creata. 159 | 160 | Infine, quando gli appunti vengono conservati nella tabella DynamoDB, viene aggiunto un record al flusso della tabella che viene a sua volta elaborato da "DynamoStreamHandlerFunction". È possibile visualizzare i registri di questa funzione in CloudWatch e verificare che un documento nuovo sia stato aggiunto al dominio di CloudSearch creato. 161 | 162 | 163 | ## Eliminazione delle risorse dell'applicazione 164 | 165 | Per eliminare tutte le risorse create da questo esempio, effettuare i seguenti passaggi: 166 | 167 | 1. Eliminare tutti gli oggetti dal bucket S3 creato dallo stack di CloudFormation. 168 | 1. Eliminare lo stack di CloudFormation. 169 | 1. Eliminare il pool di identità di Amazon Cognito, API Gateway e il dominio di CloudSearch. 170 | 1. Eliminare i gruppi di registri di CloudWatch associati a ciascuna funzione di Lambda creata dallo stack di CloudFormation. 171 | 172 | ## Risorse dei modelli di CloudFormation 173 | 174 | ### Funzioni di Lambda 175 | 176 | - **NotesApiFunction**: funzione per la gestione degli appunti pubblicati dall'applicazione per dispositivi mobili tramite API Gateway. 177 | 178 | - **SearchApiFunction**: funzione che utilizza il dominio di CloudSearch per trovare gli appunti indicizzati in base a termini di ricerca. 179 | 180 | - **DynamoStreamHandlerFunction**: funzione che aggiunge un documento indicizzato al dominio di CloudSearch fornito in base ai record nel flusso "PhotoNotesTable". 181 | 182 | ### Ruoli di AWS Identity and Access Management (IAM) 183 | 184 | - **NotesApiRole**: ruolo per "NotesApiFunction". Questo ruolo assegna l'autorizzazione per l'accesso agli elementi della "PhotoNotesTable" e il loro utilizzo. 185 | 186 | - **SearchApiRole**: ruolo per "SearchApiFunction". Questo ruolo assegna le autorizzazioni per l'accesso e la ricerca nel dominio CloudSearch fornito. 187 | 188 | - **DynamoStreamHandlerRole**: ruolo per "DynamoStreamHandlerFunction". Questo ruolo assegna le autorizzazioni per l'accesso e l'aggiunta di documenti al dominio CloudSearch fornito. 189 | 190 | - **MobileClientRole**: ruolo utilizzato dal pool di identità di Amazon Cognito per gli utenti autenticati e non autenticati. Questo ruolo fornisce accesso all'API REST di API Gateway fornita e assegna le autorizzazioni per il posizionamento di oggetti in "MobileUploadsBucket". 191 | 192 | ### Altre risorse 193 | 194 | - **MobileUploadsBucket**: bucket S3 per le foto caricate dagli utenti. 195 | 196 | - **CloudFrontDistribution**: distribuzione CDN con "MobileUploadsBucket" configurato come origine. 197 | 198 | - **PhotoNotesTable**: tabella di DynamoDB che archivia gli appunti caricati dagli utenti dall'applicazione per dispositivi mobili. 199 | 200 | ### Configurazione 201 | 202 | - **ConfigTable**: tabella di DynamoDB che contiene i valori della configurazione letti dalle varie funzioni di Lambda. Il nome di questa tabella, "MobileRefArchConfig", è hardcoded nel codice di ciascuna funzione e può essere modificato solo se viene aggiornato anche il codice. 203 | 204 | - **ConfigHelperStack**: sub-stack che crea una risorsa personalizzata per la scrittura delle voci in "ConfigTable". Questo stack crea una funzione di Lambda e un ruolo di esecuzione che assegna l'autorizzazione "UpdateItem" su "ConfigTable". 205 | 206 | - **NotesTableConfig**: voce di configurazione che identifica il nome di "PhotoNotesTable". 207 | 208 | - **SearchEndpointConfig**: voce di configurazione che identifica il search endpoint del dominio di CloudSearch come parametro. 209 | 210 | - **DocumentEndpointConfig**: voce di configurazione che identifica l'endpoint dei documenti del dominio di CloudSearch come parametro. 211 | 212 | ## Licenza 213 | 214 | La licenza di questo esempio di architettura di riferimento è fornita con Apache 2.0. 215 | -------------------------------------------------------------------------------- /README/README-JP.md: -------------------------------------------------------------------------------- 1 | # サーバーレスリファレンスアーキテクチャ: モバイルバックエンド 2 | 3 | ## はじめに 4 | 5 | モバイルバックエンドリファレンスアーキテクチャ ([図](https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/lambda-refarch-mobilebackend.pdf)) は、[AWS Lambda](http://aws.amazon.com/lambda/) をその他のサービスとともに使用してモバイルアプリケーション用のサーバーレスバックエンドを構築する方法を示します。このレポジトリに用意されているサンプルアプリケーションにより、Amazon Simple Storage Service (Amazon S3) および Amazon API Gateway を使用して、それぞれ写真とメモをアップロードすることができます。メモは Amazon DynamoDB に保存され、DynamoDB ストリームおよび Lambda 関数を使用して非同期に処理されて Amazon CloudSearch ドメインに追加されます。このレポジトリには、Lambda 関数のソースコードに加えて、AWS Mobile SDK for iOS を使用して、アーキテクチャで定義されたバックエンドリソースを操作する方法の例を示すプロトタイプ iOS アプリケーションも含まれています。 6 | 7 | ## 例の実行 8 | 9 | 完全なサンプルアプリケーションを実行するには、最初にバックエンドリソースをデプロイしてから、iOS サンプルアプリケーションをコンパイルおよび実行する必要があります。 10 | 11 | ### バックエンドのデプロイ 12 | 13 | 用意された AWS CloudFormation テンプレートでは、この例に必要なバックエンドリソースのほとんどが作成されますが、AWS CloudFormation 外部に Amazon CloudSearch ドメイン、API Gateway REST API、および Cognito ID プールを作成する必要があります。 14 | 15 | #### ステップ 1: CloudSearch ドメインを作成する 16 | 17 | 1.[AWS CLI](https://aws.amazon.com/cli/) を使用して、任意のドメイン名を提供する新しい CloudSearch ドメインを作成します。 18 | 19 | ``` 20 | aws cloudsearch create-domain --domain-name [ドメイン名] 21 | ``` 22 | 23 | 1.出力ドキュメントの新しいドメインの ARN をメモしておきます。CloudFormation スタックを起動するときに、これを入力として使用します。 24 | 25 | 1.`headline` および `note_text` フィールドのインデックスを定義します。 26 | 27 | ``` 28 | aws cloudsearch define-index-field --name headline --type text --domain-name [ドメイン名] 29 | aws cloudsearch define-index-field --name note_text --type text --domain-name [ドメイン名] 30 | ``` 31 | 32 | ``` 33 | aws cloudsearch index-documents --domain-name [YOUR_DOMAIN_NAME] 34 | ``` 35 | 36 | #### ステップ 2: API Gateway REST API を作成する 37 | 38 | 1.[AWS CLI](https://aws.amazon.com/cli/) を使用し、任意の名前を指定して新しい API を作成します。 39 | 40 | ``` 41 | aws apigateway create-rest-api --name [API 名] 42 | ``` 43 | 44 | 1.出力ドキュメントの `API ID` をメモしておきます。CloudFormation スタックを起動するときに、これを入力として使用します。 45 | 46 | #### ステップ 3: Amazon Cognito ID プールを作成する 47 | 48 | 1.[AWS CLI](https://aws.amazon.com/cli/) を使用し、任意の名前を指定して新しい ID プールを作成します。 49 | 50 | ``` 51 | aws cognito-identity create-identity-pool --allow-unauthenticated-identities --identity-pool-name [プール名] 52 | ``` 53 | 54 | 1.出力ドキュメントの `IdentityPoolId` をメモしておきます。CloudFormation スタックを起動するときに、これをパラメーターとして使用します。 55 | 56 | #### ステップ 4: CloudFormation テンプレートを起動する 57 | 58 | 用意された CloudFormation テンプレートおよび S3 バケットを使用して、例全体を us-east-1 リージョンにデプロイできます。テンプレートを別のリージョンにデプロイする場合は、そのリージョンで Amazon S3 バケットを作成し、テンプレートおよび Lambda 関数の定義をそこにコピーする必要があります。 59 | 60 | [**Launch Stack**] を選択して、アカウントで us-east-1 リージョンにテンプレートを起動します。 61 | 62 | [![CloudFormation を使用して Lambda モバイルバックエンドを北バージニアに起動する](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/images/cloudformation-launch-stack-button.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=lambda-mobile-backend&templateURL=https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/mobile-backend.template) 63 | 64 | プロンプトが表示されたら、前のステップで作成した CloudSearch ドメイン、API Gateway REST API、および Amazon Cognito ID プールリソースのパラメーター値を入力します。 65 | 66 | このテンプレートで作成されるリソースの詳細は、このドキュメントの「*CloudFormation テンプレートのリソース*」セクションで説明しています。 67 | 68 | #### ステップ 5: API Gateway REST API を更新する 69 | 70 | CloudFormation スタックを作成した後で、新しく作成された `NotesApiFunction` を使用するには、前に作成した API を更新する必要があります。 71 | 72 | 1.[Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis) で、API を選択します。 73 | 1.[**Create Resource**] を選択し、/ 以下に新しい子リソースを作成します。 74 | 1.リソース名として「notes」、リソースパスとして「/notes」と入力します。 75 | 1.[**Create Resource**] を選択します。 76 | 1.新しい `/notes` リソースを選択して、[**Create Method**] を選択します。 77 | 1.[`POST`] を選択し、チェックボックスをオンにします。 78 | 1.統合タイプとして [**Lambda Function**] を選択し、Lambda リージョンとして CloudFormation スタックを起動したリージョンを選択します。 79 | 1.[**Lambda Function**] に「**`NotesApiFunction`**」と入力し、CloudFormation スタックで作成した関数を選択します。 80 | 1.[**Save**] を選択し、Lambda 関数を実行するための API Gateway アクセス権限を付与します。 81 | 1.[**Method Request**] を選択してリクエスト設定を編集します。 82 | 1.[**Authorization type**] で、[`AWS_IAM`] を選択します。 83 | 1.[ **API Key Required**] で、[`true`] を選択します。 84 | 1.[**Deploy API**] を選択します。 85 | 1.[ **Deployment stage**] で、[`New Stage`] を選択し、[**Stage name**] に名前を入力します。 86 | 1.新しいステージの [**Invoke URL**] をメモしておきます。iOS サンプルアプリを実行するときに、この値を使用します。 87 | 88 | #### ステップ 6: API キーを作成する 89 | 90 | 1.[Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis) で、[**APIs**] を選択し、[**API Keys**] を選択します。 91 | 1.[**Create API Key**] を選択します。 92 | 1.キーの名前を入力し、[**Enabled**] を選択します。 93 | 1.[**Save**] を選択します。 94 | 1.[**API Stage Association**] セクションで API を選択し、前のステップで作成したステージを選択します。 95 | 1.[**Add**] を選択します。 96 | 1.[**API key**] をメモしておきます。モバイルアプリケーションを実行するときに、これを使用します。 97 | 98 | #### ステップ 7: Amazon Cognito ID プールを更新する 99 | 100 | 1.[Amazon Cognito コンソール](https://console.aws.amazon.com/cognito/home?region=us-east-1) で、ID プールを選択します。 101 | 1.[**Edit Identity Pool**] を選択します。 102 | 1.[**Unauthenticated role**] および [**Authenticated role**] の両方について、CloudFormation スタックで作成された [**MobileClientRole**] を選択します。ロールの完全な ARN は、スタックの出力に含まれます。 103 | 1.[**Save Changes**] を選択します。 104 | 105 | 106 | ### iOS サンプルアプリケーションの実行 107 | 108 | #### 前提条件 109 | 110 | 用意された iOS サンプルアプリケーションを実行するには、Mac OS X 10.10 (Yosemite) 以降のバージョンを実行している必要があります。また、最新バージョンの [Xcode](https://itunes.apple.com/us/app/xcode/id497799835) および [Cocoa Pods](https://cocoapods.org/) がインストールされている必要があります。 111 | 112 | #### アプリケーションの構築と実行 113 | 114 | 1.このレポジトリの **ios-sample** のソースコードをチェックアウトまたはダウンロードします。 115 | 1.バックエンドデプロイの値で `MobileBackendIOS/Constants.swift` を更新します。ほとんどの値は、CloudFormation スタックの出力に含まれています。API Gateway キーおよびエンドポイント URL の値は、AWS マネジメントコンソールの API の詳細で確認できます。 116 | 1.ルートの `ios-sample` ディレクトリから Cocoa Pods を実行します。 117 | 118 | ``` 119 | pod install 120 | ``` 121 | 122 | 1.Xcode で、生成された `MobileBackendIOS.xcworkspace` ファイルを開きます。 123 | 124 | ``` 125 | Xcode MobileBackendIOS.xcworkspace を開きます。 126 | ``` 127 | 128 | 1.ウィンドウの上部にある再生ボタンをクリックして、Xcode からプロジェクトを構築および実行します。 129 | 130 | ## アプリケーションのテスト 131 | 132 | サンプルアプリケーションは、画像のアップロードとメモの投稿という 2 つの機能を備えています。 133 | 134 | ### 画像をアップロードするには 135 | 136 | 1.アプリケーションの [**Upload Image**] を選択します。 137 | 1.カメラアイコンを選択し、カメラロールから画像を選択して、[**Choose**] を選択します。 138 | 1.[**Upload**] ボタンを選択します。 139 | 140 | #### 画像がアップロードされたことの確認 141 | 142 | 画像が Amazon S3 にアップロードされたというログエントリが、Xcode の出力ペインに表示されます。 143 | 144 | AWS マネジメントコンソールを使用して CloudFormation スタックで作成されたバケットを参照して、画像が正しくアップロードされたことを確認することもできます。 145 | 146 | ### メモを投稿するには 147 | 148 | 1.[**Post a Note**] を選択します。 149 | 1.メモに見出しとテキストを入力します。 150 | 1.[**Save Note**] を選択します。 151 | 152 | #### メモが投稿されたことの確認 153 | 154 | メモが正常に保存されたというログエントリが、Xcode の出力ペインに表示されます。 155 | 156 | メモがアップロードされると、モバイルアプリケーションによって `NotesApiFunction` が呼び出されます。この関数のログは、Amazon CloudWatch で表示できます。 157 | 158 | 関数を正しく呼び出すと、CloudFormation スタックで作成された DynamoDB テーブルにエントリが追加されます。作成されたテーブルで、アプリケーションで投稿したメモが保持されていることを確認できます。 159 | 160 | 最後に、メモが DynamoDB テーブルに保持されていると、レコードがテーブルのストリームに追加され、`DynamoStreamHandlerFunction` によって処理されます。この関数のログは CloudWatch で表示でき、作成した CloudSearch ドメインに新しいドキュメントが追加されたことを確認できます。 161 | 162 | 163 | ## アプリケーションリソースのクリーンアップ 164 | 165 | この例で作成されたすべてのリソースを削除するには、次の操作を行います。 166 | 167 | 1.CloudFormation スタックによって作成された S3 バケットからすべてのオブジェクトを削除します。 168 | 1.CloudFormation スタックを削除します。 169 | 1.Amazon Cognito ID プール、API Gateway、および CloudSearch ドメインを削除します。 170 | 1.CloudFormation スタックによって作成された各 Lambda 関数と関連付けられた CloudWatch ロググループを削除します。 171 | 172 | ## CloudFormation テンプレートのリソース 173 | 174 | ### Lambda 関数 175 | 176 | - **NotesApiFunction** - API Gateway を通じてモバイルアプリケーションから投稿されたメモを処理する関数。 177 | 178 | - **SearchApiFunction** - CloudSearch ドメインを使用し、インデックスが作成されたメモを検索用語に基づいて検索する関数。 179 | 180 | - **DynamoStreamHandlerFunction** - `PhotoNotesTable` ストリームのレコードに基づいて、インデックスが作成されたドキュメントを、指定された CloudSearch ドメインに追加する関数。 181 | 182 | ### AWS Identity and Access Management (IAM) ロール 183 | 184 | - **NotesApiRole** - `NotesApiFunction` のロール。このロールは、`PhotoNotesTable` の項目のログを記録し、操作するためのアクセス権限を付与します。 185 | 186 | - **SearchApiRole** - `SearchApiFunction` のロール。このロールは、指定された CloudSearch ドメインのログを記録し、検索するためのアクセス権限を付与します。 187 | 188 | - **DynamoStreamHandlerRole** - DynamoStreamHandlerFunction のロール。このロールは、指定された CloudSearch ドメインのログを記録し、ドキュメントを追加するためのアクセス権限を付与します。 189 | 190 | - **MobileClientRole** - 認証されていないユーザーおよび認証されたユーザーの両方に対して Amazon Cognito ID プールによって使用されるロール。このロールは、指定された API Gateway REST API へのアクセス権および `MobileUploadsBucket` にオブジェクトを配置する権限を提供します。 191 | 192 | ### その他のリソース 193 | 194 | - **MobileUploadsBucket** - ユーザーがアップロードした写真用の S3 バケット。 195 | 196 | - **CloudFrontDistribution** - `MobileUploadsBucket` がオリジンとして設定された CDN ディストリビューション。 197 | 198 | - **PhotoNotesTable** - モバイルアプリケーションからユーザーによってアップロードされたメモを保存する DynamoDB テーブル。 199 | 200 | ### 設定 201 | 202 | - **ConfigTable** - さまざまな Lambda 関数によって読み込まれた設定値を保持する DynamoDB テーブル。このテーブルの名前 "MobileRefArchConfig" は各関数のコードにハードコーディングされ、コードを更新しないと変更することはできません。 203 | 204 | - **ConfigHelperStack** - エントリを `ConfigTable` に書き込むためにカスタムリソースを作成するサブスタック。このスタックは、`ConfigTable` で UpdateItem アクセス権限を付与する Lambda 関数と実行ロールを作成します。 205 | 206 | - **NotesTableConfig** - `PhotoNotesTable` 名を識別する設定エントリ。 207 | 208 | - **SearchEndpointConfig** - パラメーターとして渡された CloudSearch ドメインの検索エンドポイントを識別する設定エントリ。 209 | 210 | - **DocumentEndpointConfig** - パラメーターとして渡された CloudSearch ドメインのドキュメントエンドポイントを識別する設定エントリ。 211 | 212 | ## ライセンス 213 | 214 | このリファレンスアーキテクチャサンプルは Apache 2.0 でライセンスされています。 215 | -------------------------------------------------------------------------------- /README/README-KR.md: -------------------------------------------------------------------------------- 1 | # 서버 없는 레퍼런스 아키텍처: 모바일 백엔드 2 | 3 | ## 서론 4 | 5 | 모바일 백엔드 레퍼런스 아키텍처([diagram](https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/lambda-refarch-mobilebackend.pdf))는 [AWS Lambda](http://aws.amazon.com/lambda/)와 다른 서비스를 사용하여 모바일 애플리케이션을 위한 서버 없는 백엔드를 구축하는 방법을 보여 줍니다. 본 리포지토리에 제공된 특정 예제 애플리케이션을 통해 사용자는 Amazon Simple Storage Service(Amazon S3)와 Amazon API Gateway를 각각 사용하여 사진과 노트를 업로드할 수 있습니다. 노트는 Amazon DynamoDB에 저장되며 DynamoDB 스트림과 Lambda 함수를 사용하여 비동기 방식으로 처리되어 Amazon CloudSearch 도메인에 추가됩니다. Lambda 함수의 소스 코드뿐 아니라 이 리포지토리 역시 iOS용 AWS Mobile SDK를 사용하여 아키텍처에 정의된 백엔드 리소스와 연결하는 방법에 대한 예제를 제공하는 프로토타입 iOS 애플리케이션을 포함하고 있습니다. 6 | 7 | ## 예제 실행 8 | 9 | 전체 예제 애플리케이션을 실행하려면 먼저 백엔드 리소스를 배포한 후 예제 iOS 애플리케이션을 컴파일 및 실행해야 합니다. 10 | 11 | ### 백엔드 배포 12 | 13 | 제공된 AWS CloudFormation 템플릿은 이 예제에 필요한 대부분의 백엔드 리소스를 만들지만 여전히 AWS CloudFormation 외부에 Amazon CloudSearch 도메인, API Gateway REST API 및 Cognito 자격 증명 풀을 만들어야 합니다. 14 | 15 | ### 1단계: CloudSearch 도메인 생성 16 | 17 | 1. [AWS CLI](https://aws.amazon.com/cli/)를 사용하여 CloudSearch 도메인을 만듭니다. 이때 원하는 도메인 이름을 지정합니다. 18 | 19 | ``` 20 | aws cloudsearch create-domain --domain-name [YOUR_DOMAIN_NAME] 21 | ``` 22 | 23 | 1. 출력 문서에서 새 도메인의 ARN을 확인합니다. 이 값을 CloudFormation 스택을 시작할 때 입력으로 사용합니다. 24 | 25 | 1. `headline` 및 `text` 필드에 대한 인덱스를 정의합니다. 26 | 27 | ``` 28 | aws cloudsearch define-index-field --name headline --type text --domain-name [YOUR_DOMAIN_NAME] 29 | aws cloudsearch define-index-field --name note_text --type text --domain-name [YOUR_DOMAIN_NAME] 30 | ``` 31 | 32 | ``` 33 | aws cloudsearch index-documents --domain-name [YOUR_DOMAIN_NAME] 34 | ``` 35 | 36 | #### 2단계: API Gateway REST API 생성 37 | 38 | 1. [AWS CLI](https://aws.amazon.com/cli/)를 사용하여 API를 만듭니다. 이때 원하는 이름을 지정합니다. 39 | 40 | ``` 41 | aws apigateway create-rest-api --name [YOUR_API_NAME] 42 | ``` 43 | 44 | 1. 출력 문서에 제공된 `API ID`를 확인합니다. 이 값을 CloudFormation 스택을 시작할 때 입력으로 사용합니다. 45 | 46 | #### 3단계: Amazon Cognito 자격 증명 풀 생성 47 | 48 | 1. [AWS CLI](https://aws.amazon.com/cli/)를 사용하여 새로운 자격 증명 풀을 만듭니다. 이때 원하는 이름을 지정합니다. 49 | 50 | ``` 51 | aws cognito-identity create-identity-pool --allow-unauthenticated-identities --identity-pool-name [YOUR_POOL_NAME] 52 | ``` 53 | 54 | 1. 출력 문서에서 `IdentityPoolId`를 확인합니다. 이 값을 CloudFormation 스택을 시작할 때 파라미터로 사용합니다. 55 | 56 | #### 4단계: CloudFormation 템플릿 시작 57 | 58 | 제공된 CloudFormation 템플릿 및 S3 버킷을 사용하여 us-east-1 리전에 전체 예제를 배포할 수 있습니다. 다른 리전에 템플릿을 배포하려면 해당 리전에 Amazon S3 버킷을 생성하고 템플릿 및 Lambda 함수 정의를 복사합니다. 59 | 60 | **Launch Stack**을 선택하여 계정의 us-east-1 리전에서 템플릿을 시작합니다. 61 | 62 | [![Launch Lambda Mobile Backend into North Virginia with CloudFormation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/images/cloudformation-launch-stack-button.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=lambda-mobile-backend&templateURL=https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/mobile-backend.template) 63 | 64 | 확인 메시지가 표시되면 앞 단계에서 만든 CloudSearch 도메인, API Gateway REST API 및 Amazon Cognito 자격 증명 풀 리소스의 파라미터 값을 입력합니다. 65 | 66 | 이 템플릿을 통해 생성된 리소스에 대한 세부 정보는 본 문서의 *CloudFormation 템플릿 리소스* 단원에서 제공됩니다. 67 | 68 | #### 5단계: API Gateway REST API 업데이트 69 | 70 | CloudFormation 스택을 만든 후에는 새로 생성된 `NotesApiFunction`을 사용하기 위해 앞서 만든 API를 업데이트해야 합니다. 71 | 72 | 1. [Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis)에서 API를 선택합니다. 73 | 1. **Create Resource**를 선택하여 / 아래에 하위 리소스를 만듭니다. 74 | 1. 리소스 이름으로 `notes`를 입력하고 리소스 경로로 `/notes`를 입력합니다. 75 | 1. **Create Resource**를 선택합니다. 76 | 1. 새 `/notes` 리소스를 선택한 상태에서 **Create Method**를 선택합니다. 77 | 1. `POST`를 선택하고 확인란을 선택합니다. 78 | 1. 통합 유형으로 **Lambda Function**을 선택한 후 CloudFormation 스택을 시작한 리전을 Lambda 리전으로 선택합니다. 79 | 1. **Lambda Function**에 **`NotesApiFunction`**을 입력한 후 CloudFormation 스택을 통해 생성된 함수를 선택합니다. 80 | 1. **Save**를 선택하여 API Gateway에 Lambda 함수를 실행할 권한을 부여합니다. 81 | 1. **Method Request**를 선택하여 요청 구성을 편집합니다. 82 | 1. **Authorization type**에서 `AWS_IAM`을 선택합니다. 83 | 1. **API Key Required**에서 `true`를 선택합니다. 84 | 1. **Deploy API**를 선택합니다. 85 | 1. **Deployment stage**에서 `New Stage`를 선택한 후 **Stage name**에 이름을 입력합니다. 86 | 1. 새 단계의 **Invoke URL**을 확인합니다. 샘플 iOS 애플리케이션을 실행할 때 이 값을 사용해야 합니다. 87 | 88 | #### 6단계: API 키 생성 89 | 90 | 1. [Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis)에서 **APIs**를 선택한 후 **API Keys**를 선택합니다. 91 | 1. **Create API Key**를 선택합니다. 92 | 1. 키의 이름을 입력한 후 **Enabled**를 선택합니다. 93 | 1. **Save**를 선택합니다. 94 | 1. **API Stage Association** 섹션에서 API를 선택한 후 앞 단계에서 만든 단계를 선택합니다. 95 | 1. **Add**를 선택합니다. 96 | 1. **API key**를 확인합니다. 모바일 애플리케이션을 실행할 때 이 값을 사용해야 합니다. 97 | 98 | #### 7단계: Amazon Cognito 자격 증명 풀 업데이트 99 | 100 | 1. [Amazon Cognito Console](https://console.aws.amazon.com/cognito/home?region=us-east-1)에서 자격 증명 풀을 선택합니다. 101 | 1. **Edit Identity Pool**을 선택합니다. 102 | 1. **Unauthenticated role** 및 **Authenticated role**에 대해 CloudFormation 스택을 통해 생성된 **MobileClientRole**을 선택합니다. 이 역할의 전체 ARN이 스택 출력에 제공됩니다. 103 | 1. **Save Changes**를 선택합니다. 104 | 105 | 106 | ### 샘플 iOS 애플리케이션 실행 107 | 108 | #### 사전 조건 109 | 110 | 제공된 iOS 샘플 애플리케이션을 실행하려면 Mac OS X 10.10(Yosemite) 또는 보다 최신 버전을 실행해야 합니다. 최신 버전의 [Xcode](https://itunes.apple.com/us/app/xcode/id497799835) 및 [Cocoa Pods](https://cocoapods.org/)도 설치되어 있어야 합니다. 111 | 112 | #### 애플리케이션 구축 및 실행 113 | 114 | 1. 이 리포지토리에서 **ios-sample**의 소스 코드를 확인하거나 다운로드합니다. 115 | 1. `MobileBackendIOS/Constants.swift`를 백엔드 배포 값으로 업데이트합니다. 대부분의 값은 CloudFormation 스택의 출력에서 찾을 수 있습니다. API Gateway 키 및 엔드포인트 URL 값은 AWS Management Console의 API 세부 정보에서 찾을 수 있습니다. 116 | 1. 루트 `ios-sample` 디렉터리에서 Cocoa Pods를 실행합니다. 117 | 118 | ``` 119 | pod install 120 | ``` 121 | 122 | 1. Xcode에서 생성된 `MobileBackendIOS.xcworkspace` 파일을 엽니다. 123 | 124 | ``` 125 | open -a Xcode MobileBackendIOS.xcworkspace 126 | ``` 127 | 128 | 1. 화면 상단에서 재생 버튼을 클릭하여 Xcode에서 프로젝트를 구축 및 실행합니다. 129 | 130 | ## 애플리케이션 테스트 131 | 132 | 샘플 애플리케이션은 이미지 업로드 및 노트 게시라는 두 기능을 제공합니다. 133 | 134 | ### 이미지 업로드 방법 135 | 136 | 1. 애플리케이션에서 **Upload Image**를 선택합니다. 137 | 1. 카메라 아이콘을 선택하고 카메라 역할에서 이미지를 선택한 후 **Choose**를 선택합니다. 138 | 1. **Upload** 버튼을 선택합니다. 139 | 140 | #### 이미지 업로드 여부 확인 방법 141 | 142 | Xcode의 출력 창에 이미지가 Amazon S3로 업로드되었음을 나타내는 로그 항목을 확인할 수 있습니다. 143 | 144 | AWS Management Console을 사용하여 CloudFormation 스택을 통해 생성된 버킷을 탐색하여 이미지가 올바르게 업로드되었음을 확인할 수도 있습니다. 145 | 146 | ### 노트 게시 방법 147 | 148 | 1. **Post a Note**를 선택합니다. 149 | 1. 노트란에 헤드라인과 텍스트를 입력합니다. 150 | 1. **Save Note**를 선택합니다. 151 | 152 | #### 노트 게시 여부 확인 방법 153 | 154 | Xcode의 출력 창에 노트가 저장되었음을 나타내는 로그 항목을 확인할 수 있습니다. 155 | 156 | 노트가 업로드되면 `NotesApiFunction`이 모바일 애플리케이션에 의해 호출됩니다. Amazon CloudWatch에서 이 함수에 대한 로그를 볼 수 있습니다. 157 | 158 | 함수가 호출되면 CloudFormation 스택에 생성된 DynamoDB 테이블에 항목을 추가합니다. 생성된 테이블에 애플리케이션에 게시한 노트가 유지되고 있는지 확인할 수 있습니다. 159 | 160 | 마지막으로 노트가 DynamoDB 테이블에 유지되고 있으면 테이블의 스트림에 레코드가 추가되며 이 레코드는 `DynamoStreamHandlerFunction`에 의해 처리됩니다. CloudWatch에서 이 함수에 대한 로그를 보고 생성된 CloudSearch 도메인에 새 문서가 추가되었음을 확인할 수 있습니다. 161 | 162 | 163 | ## 애플리케이션 리소스 정리 164 | 165 | 이 예제에서 생성된 모든 리소스를 제거하려면 다음을 수행합니다. 166 | 167 | 1. CloudFormation 스택에서 생성된 S3 버킷에서 모든 객체를 삭제합니다. 168 | 1. CloudFormation 스택을 삭제합니다. 169 | 1. Amazon Cognito 자격 증명 풀, API Gateway 및 CloudSearch 도메인을 삭제합니다. 170 | 1. CloudFormation 스택을 통해 생성된 각 Lambda 함수와 연결된 CloudWatch 로그 그룹을 삭제합니다. 171 | 172 | ## CloudFormation 템플릿 리소스 173 | 174 | ### Lambda 함수 175 | 176 | - **NotesApiFunction** - API Gateway를 통해 모바일 애플리케이션에서 게시된 노트를 처리하는 함수입니다. 177 | 178 | - **SearchApiFunction** - CloudSearch 도메인을 사용하여 검색어를 기반으로 인덱싱된 노트를 찾는 함수입니다. 179 | 180 | - **DynamoStreamHandlerFunction** - 제공된 CloudSearch 도메인에 `PhotoNotesTable` 스트림의 레코드를 기반으로 인덱싱된 문서를 추가하는 함수입니다. 181 | 182 | ### AWS IAM(Identity and Access Management) 역할 183 | 184 | - **NotesApiRole** - `NotesApiFunction`의 역할입니다. 이 역할은 로그인 및 `PhotoNotesTable`의 항목 작업에 대한 권한을 부여합니다. 185 | 186 | - **SearchApiRole** - `SearchApiFunction`의 역할입니다. 이 역할은 로그인 및 제공된 CloudSearch 도메인 검색에 대한 권한을 부여합니다. 187 | 188 | - **DynamoStreamHandlerRole** - DynamoStreamHandlerFunction에 대한 역할입니다. 이 역할은 로그인 및 제공된 CloudSearch 도메인에 문서를 추가하는 권한을 부여합니다. 189 | 190 | - **MobileClientRole** - 인증되지 않은 사용자 및 인증된 사용자에 대해 Amazon Cognito 자격 증명 풀에서 사용하는 역할입니다. 이 역할은 제공된 API Gateway REST API에 대한 액세스를 제공하고 객체를 `MobileUploadsBucket`에 추가하는 권한을 제공합니다. 191 | 192 | ### 기타 리소스 193 | 194 | - **MobileUploadsBucket** - 사용자가 업로드한 사진의 S3 버킷입니다. 195 | 196 | - **CloudFrontDistribution** - `MobileUploadsBucket`이 오리진으로 구성된 CDN 배포입니다. 197 | 198 | - **PhotoNotesTable** - 모바일 애플리케이션을 통해 사용자가 업로드한 노트를 저장하는 DynamoDB 테이블입니다. 199 | 200 | ### 구성 201 | 202 | - **ConfigTable** - 여러 Lambda 함수가 읽는 구성 값이 저장된 DynamoDB 테이블입니다. 이 테이블의 이름인 "MobileRefArchConfig"는 각 함수의 코드로 하드 코딩되며 코드까지 업데이트해야 수정이 가능합니다. 203 | 204 | - **ConfigHelperStack** - `ConfigTable`에 항목을 쓰기 위한 사용자 지정 리소스를 만드는 하위 스택입니다. 이 스택은 Lambda 함수를 만들고 `ConfigTable`에서 UpdateItem 권한을 부여하는 실행 역할을 만듭니다. 205 | 206 | - **NotesTableConfig** - `PhotoNotesTable` 이름을 식별하는 구성 항목입니다. 207 | 208 | - **SearchEndpointConfig** - 파라미터로 전달된 CloudSearch 도메인의 검색 엔드포인트를 식별하는 구성 항목입니다. 209 | 210 | - **DocumentEndpointConfig** - 파라미터로 전달된 CloudSearch 도메인의 문서 엔드포인트를 식별하는 구성 항목입니다. 211 | 212 | ## 라이선스 213 | 214 | 이 레퍼런스 아키텍처 샘플은 Apache 2.0에서 라이선스가 부여되었습니다. 215 | -------------------------------------------------------------------------------- /README/README-PT.md: -------------------------------------------------------------------------------- 1 | # Arquitetura de referência sem servidor: back-end móvel 2 | 3 | ## Introdução 4 | 5 | A arquitetura de referência de back-end móvel ([diagrama](https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/lambda-refarch-mobilebackend.pdf)) demonstra como usar o [AWS Lambda](http://aws.amazon.com/lambda/) juntamente com outros serviços para construir um back-end sem servidor para um aplicativo móvel. O aplicativo de exemplo específico fornecido neste repositório permite aos usuários fazer upload de fotos e notas usando o Amazon Simple Storage Service (Amazon S3) e o Amazon API Gateway, respectivamente. As notas são armazenadas no Amazon DynamoDB, e são processadas de forma assíncrona usando fluxos DynamoDB e uma função Lambda para adicioná-las a um domínio do Amazon CloudSearch. Além do código-fonte para as funções do Lambda, este repositório também contém um aplicativo iOS protótipo que fornece exemplos de como usar o AWS Mobile SDK para iOS para fazer a interface com os recursos de back-end definidos na arquitetura. 6 | 7 | ## Executando o exemplo 8 | 9 | Para executar o aplicativo de exemplo completo, você deve primeiro implantar os recursos de back-end e, em seguida, compilar e executar o exemplo de aplicativo para iOS. 10 | 11 | ### Implementando o back-end 12 | 13 | O modelo do AWS CloudFormation fornecido cria a maior parte dos recursos de back-end que você precisa para este exemplo, mas você ainda precisa criar o domínio do Amazon CloudSearch, a API REST do gateway de API e um pool de identidades do Cognito fora do AWS CloudFormation. 14 | 15 | #### Etapa 1: criar um domínio do CloudSearch 16 | 17 | 1. Usando o [AWS CLI](https://aws.amazon.com/cli/), crie um novo domínio do CloudSearch fornecendo um nome de domínio a sua escolha. 18 | 19 | ``` 20 | aws cloudsearch create-domain --domain-name [YOUR_DOMAIN_NAME] 21 | ``` 22 | 23 | 1. Anote o ARN do novo domínio no documento de saída. Você vai usá-lo como uma entrada ao iniciar a pilha do CloudFormation. 24 | 25 | 1. Defina índices para os campos "headline" e "note_text". 26 | 27 | ``` 28 | aws cloudsearch define-index-field --name headline --type text --domain-name [YOUR_DOMAIN_NAME] 29 | aws cloudsearch define-index-field --name note_text --type text --domain-name [YOUR_DOMAIN_NAME] 30 | ``` 31 | 32 | ``` 33 | aws cloudsearch index-documents --domain-name [YOUR_DOMAIN_NAME] 34 | ``` 35 | 36 | #### Etapa 2: criar uma API REST do gateway de API 37 | 38 | 1. Usando o [AWS CLI](https://aws.amazon.com/cli/), crie uma nova API fornecendo um nome de domínio a sua escolha. 39 | 40 | ``` 41 | aws apigateway create-rest-api --name [YOUR_API_NAME] 42 | ``` 43 | 44 | 1. Anote o "ID da API" fornecido no documento de saída. Você vai usá-lo como uma entrada ao iniciar a pilha do CloudFormation. 45 | 46 | #### Etapa 3: criar um pool de identidades do Amazon Cognito 47 | 48 | 1. Usando o [AWS CLI](https://aws.amazon.com/cli/), crie um novo pool de identidades fornecendo um nome de domínio a sua escolha. 49 | 50 | ``` 51 | aws cognito-identity create-identity-pool --allow-unauthenticated-identities --identity-pool-name [YOUR_POOL_NAME] 52 | ``` 53 | 54 | 1. Anote o "IdentityPoolId" no documento de saída. Você vai usá-lo como um parâmetro ao iniciar a pilha do CloudFormation. 55 | 56 | #### Etapa 4: iniciar o modelo do CloudFormation 57 | 58 | Você pode implantar o exemplo inteiro na região us-east-1 usando o modelo do CloudFormation e bucket S3 fornecido. Se você quiser implantar o modelo em uma região diferente, você deve criar um bucket do Amazon S3 naquela região, e depois copiar as definições do modelo e função do Lambda nele. 59 | 60 | Escolha **Launch Stack** para iniciar o modelo na região us-east-1 em sua conta: 61 | 62 | [![Launch Lambda Mobile Backend into North Virginia with CloudFormation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/images/cloudformation-launch-stack-button.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=lambda-mobile-backend&templateURL=https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/mobile-backend.template) 63 | 64 | Quando solicitado, digite os valores dos parâmetros para o domínio dos recursos CloudSearch, REST API do gateway de API e pool de identidades do Amazon Cognito que você criou nas etapas anteriores. 65 | 66 | Os detalhes sobre os recursos criados por este modelo são fornecidos na seção *Recursos do modelo do CloudFormation* deste documento. 67 | 68 | #### Etapa 5: atualizar sua API REST do gateway de API 69 | 70 | Depois de ter criado a pilha do CloudFormation, você precisa atualizar a API criada anteriormente para usar o recém-criado "NotesApiFunction". 71 | 72 | 1. No [Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis), escolha a API. 73 | 1. Escolha **Create Resource** para criar um novo recurso dependente em /. 74 | 1. Digite "notes" como nome do recurso e "/notes" como caminho. 75 | 1. Escolha **Create Resource**. 76 | 1. Com o novo recurso "/notes" selecionado, escolha **Create Method**. 77 | 1. Escolha "POST" e marque a caixa de seleção. 78 | 1. Escolha **Lambda Function** como o tipo de integração e, em seguida, escolha a região onde iniciar a pilha do CloudFormation como também a região da função do Lambda. 79 | 1. Em **Lambda Function**, digite **`NotesApiFunction`** e, em seguida, selecione a função criada pela pilha do CloudFormation. 80 | 1. Escolha **Save** e dê permissão ao gateway de API para executar a função do Lambda. 81 | 1. Escolha **Method Request** para editar a configuração do pedido. 82 | 1. Em **Authorization type**, selecione "AWS_IAM". 83 | 1. Em **API Key Required**, selecione "true". 84 | 1. Escolha **Deploy API**. 85 | 1. Em **Deployment stage**, escolha "New Stage" e, em seguida, digite o nome em **Stage name**. 86 | 1. Anote o **Invoke URL** para o próximo estágio. Você vai usá-lo ao executar o aplicativo para iOS de amostra. 87 | 88 | #### Etapa 6: criar uma chave de API 89 | 90 | 1. No [Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis), escolha **APIs** e, em seguida, escolha **API Keys**. 91 | 1. Escolha **Create API Key**. 92 | 1. Digite um nome para as chaves e selecione **Enabled**. 93 | 1. Escolha **Save** 94 | 1. Na seção **API Stage Association**, escolha sua API e, em seguida, escolha o estágio que você criou no passo anterior. 95 | 1. Escolha **Add**. 96 | 1. Anote a **API key**. Você vai usá-la ao executar o aplicativo móvel. 97 | 98 | #### Etapa 7: atualizar o pool de identidades do Amazon Cognito 99 | 100 | 1. No [Amazon Cognito Console](https://console.aws.amazon.com/cognito/home?region=us-east-1), selecione seu pool de identidades. 101 | 1. Escolha **Edit Identity Pool**. 102 | 1. Para **Unauthenticated role** e **Authenticated role**, selecione o **MobileClientRole** criado pela pilha do CloudFormation. O ARN completo para a função é fornecido nas saídas da pilha. 103 | 1. Escolha **Save Changes**. 104 | 105 | 106 | ### Executando a amostra do aplicativo para iOS 107 | 108 | #### Pré-requisitos 109 | 110 | Para executar a amostra do aplicativo para iOS fornecido, você deve estar executando o Mac OS X 10.10 (Yosemite) ou uma versão mais recente. Você também deve ter as versões mais recentes do [Xcode](https://itunes.apple.com/us/app/xcode/id497799835) e do [Cocoa Pods](https://cocoapods.org/) instaladas. 111 | 112 | #### Desenvolvendo e executando o aplicativo 113 | 114 | 1. Verifique ou faça o download do código-fonte do **ios-sample** neste repositório. 115 | 1. Atualize "MobileBackendIOS/Constants.swift" com os valores para a implementação do back-end. A maioria dos valores podem ser encontrados nas saídas da pilha do CloudFormation. Os valores de chave do gateway de API e URL de endpoint estão disponíveis em detalhes para sua API no AWS Management Console. 116 | 1. Execute Cocoa Pods a partir do diretório raiz "ios-sample". 117 | 118 | ``` 119 | pod install 120 | ``` 121 | 122 | 1. Abra o arquivo "MobileBackendIOS.xcworkspace" gerado no Xcode. 123 | 124 | ``` 125 | abra -a Xcode MobileBackendIOS.xcworkspace 126 | ``` 127 | 128 | 1. Desenvolva e execute o projeto do Xcode, clicando no botão de reprodução na parte superior da janela. 129 | 130 | ## Testando o aplicativo 131 | 132 | O aplicativo de amostra fornece dois recursos: fazer upload de uma imagem e postar uma nota. 133 | 134 | ### Para fazer upload de uma imagem 135 | 136 | 1. Escolha **Upload Image** no aplicativo. 137 | 1. Escolha o ícone da câmera, selecione uma imagem no rolo da câmera e, em seguida, escolha **Choose**. 138 | 1. Escolha o botão **Upload**. 139 | 140 | #### Validando a imagem que foi carregada 141 | 142 | Você deverá ver uma entrada de registro que diz que a imagem foi carregada para o Amazon S3 no painel de saída do Xcode. 143 | 144 | Você também pode procurar o bucket criado pela pilha CloudFormation usando o Console de Gerenciamento da AWS para verificar se a imagem foi corretamente carregada. 145 | 146 | ### Para postar uma nota 147 | 148 | 1. Escolha **Post a Note**. 149 | 1. Na nota, digite um título e o texto. 150 | 1. Escolha **Save Note**. 151 | 152 | #### Validando a nota que foi postada 153 | 154 | Você deverá ver uma entrada de registro de que a nota foi salva com sucesso para o Amazon S3 no painel de saída do Xcode. 155 | 156 | Quando a nota é carregada, o "NotesApiFunction" é chamado pelo aplicativo móvel. É possível visualizar os registros para esta função no Amazon CloudWatch. 157 | 158 | Quando a função é chamada com sucesso, ela adiciona uma entrada à tabela DynamoDB criada na pilha do CloudFormation. Você pode verificar se a nota que você postou no aplicativo se mantém na tabela criada. 159 | 160 | Finalmente, quando a nota é mantida na tabela DynamoDB, um registro é adicionado ao fluxo da tabela, que por sua vez é processado por "DynamoStreamHandlerFunction". É possível visualizar os registros para esta função em CloudWatch e verificar se um novo documento foi adicionado ao domínio do CloudSearch que você criou. 161 | 162 | 163 | ## Limpando os recursos do aplicativo 164 | 165 | Para remover todos os recursos criados por este exemplo, faça o seguinte: 166 | 167 | 1. Exclua todos os objetos do bucket S3 criados pela pilha do CloudFormation. 168 | 1. Exclua a pilha do CloudFormation. 169 | 1. Exclua o pool de identidades do Amazon Cognito, o gateway de API e o domínio do CloudSearch. 170 | 1. Exclua os grupos de registro do CloudWatch associados a cada função Lambda criada pela pilha CloudFormation. 171 | 172 | ## Recursos do modelo do CloudFormation 173 | 174 | ### Funções lambda 175 | 176 | - **NotesApiFunction** - Uma função para lidar com notas postadas no aplicativo móvel através do gateway da API. 177 | 178 | - **SearchApiFunction** - Uma função que usa o domínio do CloudSearch para encontrar notas indexadas com base em termos de pesquisa. 179 | 180 | - **DynamoStreamHandlerFunction** - Uma função que adiciona um documento indexado ao domínio do CloudSearch fornecido com base em registros no fluxo "PhotoNotesTable". 181 | 182 | ### Funções AWS Identity and Access Management (IAM) 183 | 184 | - **NotesApiRole** - Uma função para a "NotesApiFunction". Esta função concede a permissão para iniciar sessão e trabalhar com itens da "PhotoNotesTable". 185 | 186 | - **SearchApiRole** - Uma função para a "SearchApiFunction". Esta função concede a permissão para iniciar sessão e pesquisar no domínio do CloudSearch fornecido. 187 | 188 | - **DynamoStreamHandlerRole** - Uma função para a DynamoStreamHandlerFunction. Esta função concede a permissão para iniciar sessão e adicionar documentos ao domínio do CloudSearch fornecido. 189 | 190 | - **MobileClientRole** - Uma função usada pelo pool de identidades Amazon Cognito para usuários autenticados ou não. Esta função fornece acesso à REST API do gateway de API fornecida, bem como permissões para adicionar objetos ao "MobileUploadsBucket". 191 | 192 | ### Outros recursos 193 | 194 | - **MobileUploadsBucket** - Um bucket S3 para o upload de fotos de usuários. 195 | 196 | - **CloudFrontDistribution** - Uma distribuição CDN com o "MobileUploadsBucket" configurado como uma origem. 197 | 198 | - **PhotoNotesTable** - Uma tabela DynamoDB que armazena o upload de fotos feitas pelos usuários a partir do aplicativo móvel. 199 | 200 | ### Configuração 201 | 202 | - **ConfigTable** - Uma tabela DynamoDB para armazenar valores de configuração lidos pelas várias funções Lambda. O nome desta tabela, "MobileRefArchConfig", é codificado em cada código da função e não pode ser modificado sem também atualizar o código. 203 | 204 | - **ConfigHelperStack** - A subpilha que cria um recurso personalizado para escrever entradas à "ConfigTable". Esta pilha cria uma função e execução da função Lambda que concede a permissão ao UpdateItem na "ConfigTable". 205 | 206 | - **NotesTableConfig** - Uma entrada de configuração que identifica o nome da "PhotoNotesTable". 207 | 208 | - **SearchEndpointConfig** - Uma entrada de configuração que identifica o ponto de extremidade da busca do domínio do CloudSearch transmitido como um parâmetro. 209 | 210 | - **DocumentEndpointConfig** - Uma entrada de configuração que identifica o ponto de extremidade dos documentos do domínio do CloudSearch transmitido como um parâmetro. 211 | 212 | ## Licença 213 | 214 | Este exemplo de arquitetura de referência é licenciado sob a licença do Apache 2.0. 215 | -------------------------------------------------------------------------------- /README/README-RU.md: -------------------------------------------------------------------------------- 1 | # Эталонная бессерверная архитектура: базовая система для мобильного приложения 2 | 3 | ## Введение 4 | 5 | Эта эталонная архитектура ([схема](https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/lambda-refarch-mobilebackend.pdf)) позволяет понять, как использовать [AWS Lambda](http://aws.amazon.com/lambda/) вместе с другими сервисами для создания бессерверной базовой системы для мобильного приложения. Пример приложения, предоставленный в этом репозитории, позволяет пользователям загружать фотографии и заметки с помощью Amazon Simple Storage Service (Amazon S3) и Amazon API Gateway соответственно. Заметки хранятся в Amazon DynamoDB и обрабатываются асинхронно с помощью потоков DynamoDB, а функция Lambda добавляет их в домен Amazon CloudSearch. Помимо исходного кода для функций Lambda, этот репозиторий также содержит прототип приложения для iOS с примерами взаимодействия с базовыми ресурсами, заданными в архитектуре, с помощью пакета AWS Mobile SDK для iOS. 6 | 7 | ## Запуск примера 8 | 9 | Чтобы запустить пример приложения, сначала необходимо развернуть базовые ресурсы, а затем скомпилировать и запустить приложение для iOS. 10 | 11 | ### Развертывание базовой системы 12 | 13 | Предоставленный шаблон AWS CloudFormation создает большинство необходимых базовых ресурсов, но вам по-прежнему потребуется создать домен Amazon CloudSearch, API REST для API Gateway и пул удостоверений Cognito за пределами AWS CloudFormation. 14 | 15 | #### Шаг 1. Создание домена CloudSearch 16 | 17 | 1. С помощью [AWS CLI](https://aws.amazon.com/cli/) создайте домен CloudSearch с именем по вашему выбору. 18 | 19 | ``` 20 | aws cloudsearch create-domain --domain-name [YOUR_DOMAIN_NAME] 21 | ``` 22 | 23 | 1. Обратите внимание на ARN нового домена в полученном документе. Вы введете это значение при запуске стека CloudFormation. 24 | 25 | 1. Определите индексы для полей «headline» и «note_text». 26 | 27 | ``` 28 | aws cloudsearch define-index-field --name headline --type text --domain-name [YOUR_DOMAIN_NAME] 29 | aws cloudsearch define-index-field --name note_text --type text --domain-name [YOUR_DOMAIN_NAME] 30 | ``` 31 | 32 | ``` 33 | aws cloudsearch index-documents --domain-name [YOUR_DOMAIN_NAME] 34 | ``` 35 | 36 | #### Шаг 2. Создание API REST для API Gateway 37 | 38 | 1. С помощью [AWS CLI](https://aws.amazon.com/cli/) создайте API с именем по вашему выбору. 39 | 40 | ``` 41 | aws apigateway create-rest-api --name [YOUR_API_NAME] 42 | ``` 43 | 44 | 1. Обратите внимание на API ID в полученном документе. Вы введете это значение при запуске стека CloudFormation. 45 | 46 | #### Шаг 3. Создание пула удостоверений Amazon Cognito 47 | 48 | 1. С помощью [AWS CLI](https://aws.amazon.com/cli/) создайте пул удостоверений с именем по вашему выбору. 49 | 50 | ``` 51 | aws cognito-identity create-identity-pool --allow-unauthenticated-identities --identity-pool-name [YOUR_POOL_NAME] 52 | ``` 53 | 54 | 1. Обратите внимание на строку IdentityPoolId в полученном документе. Вы введете это значение в качестве параметра при запуске стека CloudFormation. 55 | 56 | #### Шаг 4. Запуск шаблона CloudFormation 57 | 58 | Вы можете развернуть весь пример в регионе us-east-1, используя предоставленный шаблон CloudFormation и корзину S3. Чтобы использовать другой регион, создайте корзину Amazon S3 в этом регионе и скопируйте в него шаблон и определения функций Lambda. 59 | 60 | Выберите **Launch Stack**, чтобы запустить шаблон в регионе us-east-1 в вашем аккаунте: 61 | 62 | [![Запуск базовой системы для мобильного приложения Lambda в Северной Вирджинии с помощью CloudFormation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/images/cloudformation-launch-stack-button.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=lambda-mobile-backend&templateURL=https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/mobile-backend.template) 63 | 64 | После появления запроса укажите домен CloudSearch, API REST для API Gateway и пул удостоверений Amazon Cognito, созданные ранее. 65 | 66 | Сведения о ресурсах, созданных этим шаблоном, представлены в разделе «Ресурсы шаблона CloudFormation» этого документа. 67 | 68 | #### Шаг 5. Обновление API REST для API Gateway 69 | 70 | После создания стека CloudFormation вам необходимо обновить API, созданный ранее, чтобы воспользоваться новой функцией NotesApiFunction. 71 | 72 | 1. В [консоли Amazon API Gateway](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis) выберите ваш API. 73 | 1. Выберите **Create Resource**, чтобы создать дочерний ресурс в каталоге «/». 74 | 1. Укажите имя ресурса «notes» и путь «/notes». 75 | 1. Выберите **Create Resource**. 76 | 1. Выберите новый ресурс «/notes» и нажмите кнопку **Create Method**. 77 | 1. Выберите «POST» и установите флажок. 78 | 1. Выберите тип интеграции **Lambda Function**, а затем выберите регион, где вы запустили стек CloudFormation, в качестве региона Lambda. 79 | 1. В поле **Lambda Function** введите **NotesApiFunction**, а затем выберите функцию, созданную стеком CloudFormation. 80 | 1. Нажмите кнопку **Save** и предоставьте API Gateway разрешения для выполнения функции Lambda. 81 | 1. Выберите **Method Request**, чтобы изменить конфигурацию запроса. 82 | 1. Для параметра **Authorization type** выберите значение «AWS_IAM». 83 | 1. Для параметра **API Key Required** выберите значение «true». 84 | 1. Нажмите кнопку **Deploy API**. 85 | 1. Для параметра **Deployment stage** выберите значение **New Stage** и введите имя в поле **Stage name**. 86 | 1. Обратите внимание на адрес **Invoke URL** нового этапа. Вы будете использовать это значение при запуске примера приложения для iOS. 87 | 88 | #### Шаг 6. Создание ключа API 89 | 90 | 1. В [консоли Amazon API Gateway](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis) выберите **APIs**, а затем выберите **API Keys**. 91 | 1. Нажмите кнопку **Create API Key**. 92 | 1. Введите имя ключа и выберите **Enabled**. 93 | 1. Нажмите кнопку **Save**. 94 | 1. В разделе **API Stage Association** выберите ваш API, а затем выберите этап, созданный ранее. 95 | 1. Нажмите кнопку **Add**. 96 | 1. Обратите внимание на значение **API key**. Оно будет использоваться при запуске мобильного приложения. 97 | 98 | #### Шаг 7. Обновление пула удостоверений Amazon Cognito 99 | 100 | 1. В [консоли Amazon Cognito](https://console.aws.amazon.com/cognito/home?region=us-east-1) выберите ваш пул удостоверений. 101 | 1. Нажмите кнопку **Edit Identity Pool**. 102 | 1. Для параметров **Unauthenticated role** и **Authenticated role** выберите роль **MobileClientRole**, созданную стеком CloudFormation. В выходных данных стека указано полное значение ARN роли. 103 | 1. Нажмите кнопку **Save Changes**. 104 | 105 | 106 | ### Запуск примера приложения iOS 107 | 108 | #### Необходимые условия 109 | 110 | Для запуска примера приложения iOS необходима операционная система Mac OS X версии 10.10 (Yosemite) или более поздней. Рекомендуем установить последнюю версию [Xcode](https://itunes.apple.com/us/app/xcode/id497799835) и [Cocoa Pods](https://cocoapods.org/). 111 | 112 | #### Построение и запуск приложения 113 | 114 | 1. Извлеките или загрузите исходный код приложения **ios-sample** из этого репозитория. 115 | 1. Укажите в файле MobileBackendIOS/Constants.swift значения для вашего развертывания. Большинство из них можно найти в выходных данных стека CloudFormation. Ключ API Gateway и URL доступны в описании вашего API в консоли управления AWS. 116 | 1. Запустите Cocoa Pods из корневого каталога ios-sample. 117 | 118 | ``` 119 | pod install 120 | ``` 121 | 122 | 1. Откройте созданный файл MobileBackendIOS.xcworkspace в Xcode. 123 | 124 | ``` 125 | open -a Xcode MobileBackendIOS.xcworkspace 126 | ``` 127 | 128 | 1. Выполните построение проекта и запустите его в Xcode, нажав кнопку воспроизведения в верхней части окна. 129 | 130 | ## Тестирование приложения 131 | 132 | Пример приложения предоставляет две возможности: загрузка изображения и публикация заметки. 133 | 134 | ### Загрузка изображения 135 | 136 | 1. Нажмите кнопку **Upload Image** в приложении. 137 | 1. Щелкните значок камеры, выберите изображение и нажмите кнопку **Choose**. 138 | 1. Нажмите кнопку **Upload**. 139 | 140 | #### Проверка загрузки изображения 141 | 142 | В журнале на панели выходных данных Xcode вы увидите запись о том, что изображение загружено в Amazon S3. 143 | 144 | Вы также можете просмотреть корзину, созданную стеком CloudFormation, используя консоль управления AWS, чтобы убедиться, что изображение загружено без ошибок. 145 | 146 | ### Публикация заметки 147 | 148 | 1. Нажмите кнопку **Post a Note**. 149 | 1. Введите заголовок и текст. 150 | 1. Нажмите кнопку **Save Note**. 151 | 152 | #### Проверка публикации заметки 153 | 154 | В журнале на панели выходных данных Xcode вы увидите запись о том, что заметка успешно сохранена. 155 | 156 | При загрузке заметки мобильное приложение вызывает функцию NotesApiFunction. Журналы выполнения этой функции можно просмотреть в Amazon CloudWatch. 157 | 158 | После успешного выполнения функции в таблицу DynamoDB, созданную стеком CloudFormation, добавляется запись. Вы можете проверить, сохранена ли заметка, опубликованная в приложении, в созданной таблице. 159 | 160 | Наконец, когда заметка сохраняется в таблице DynamoDB, в ее поток добавляется запись, которую обрабатывает функция DynamoStreamHandlerFunction. Вы можете просмотреть журналы ее выполнения в CloudWatch и убедиться, что в созданный домен CloudSearch добавлен новый документ. 161 | 162 | 163 | ## Очистка ресурсов приложения 164 | 165 | Чтобы удалить все ресурсы, созданные этим примером, выполните следующие действия. 166 | 167 | 1. Удалите все объекты из корзины S3, созданной стеком CloudFormation. 168 | 1. Удалите стек CloudFormation. 169 | 1. Удалите пул удостоверений Amazon Cognito, API Gateway и домен CloudSearch. 170 | 1. Удалите группы журналов CloudWatch, связанные с каждой функцией Lambda, созданной стеком CloudFormation. 171 | 172 | ## Ресурсы шаблона CloudFormation 173 | 174 | ### Функции Lambda 175 | 176 | - **NotesApiFunction** – функция для обработки заметок, публикуемых в мобильном приложении, с использованием API Gateway. 177 | 178 | - **SearchApiFunction** – функция, использующая домен CloudSearch для поиска индексированных заметок по заданным условиям. 179 | 180 | - **DynamoStreamHandlerFunction** – функция, которая добавляет индексированный документ в указанный домен CloudSearch на основе записей в потоке PhotoNotesTable. 181 | 182 | ### Роли AWS Identity and Access Management (IAM) 183 | 184 | - **NotesApiRole** – роль для функции NotesApiFunction. Она предоставляет разрешения для ведения журнала и обработки элементов в таблице PhotoNotesTable. 185 | 186 | - **SearchApiRole** – роль для функции SearchApiFunction. Она предоставляет разрешения для ведения журнала и поиска в заданном домене CloudSearch. 187 | 188 | - **DynamoStreamHandlerRole** – роль для функции DynamoStreamHandlerFunction. Она предоставляет разрешения для ведения журнала и добавления документов в заданный домен CloudSearch. 189 | 190 | - **MobileClientRole** – эту роль пул удостоверений Amazon Cognito применяет для пользователей, прошедших и не прошедших аутентификацию. Она предоставляет доступ к указанному API REST для API Gateway, а также разрешения на размещение объектов в MobileUploadsBucket. 191 | 192 | ### Другие ресурсы 193 | 194 | - **MobileUploadsBucket** – корзина S3 для загруженных фотографий. 195 | 196 | - **CloudFrontDistribution** – база раздачи CDN, где в качестве источника используется корзина MobileUploadsBucket. 197 | 198 | - **PhotoNotesTable** – таблица DynamoDB, в которой хранятся заметки, опубликованные пользователями в мобильном приложении. 199 | 200 | ### Конфигурация 201 | 202 | - **ConfigTable** – таблица DynamoDB для хранения значений конфигурации, которые используют различные функции Lambda. Имя этой таблицы, «MobileRefArchConfig», строго закодировано в каждой функции, его можно изменить, только обновив код. 203 | 204 | - **ConfigHelperStack** – вложенный стек, создающий пользовательский ресурс для записи данных в таблицу ConfigTable. Этот стек создает функцию Lambda и роль выполнения, предоставляющую разрешение UpdateItem для таблицы ConfigTable. 205 | 206 | - **NotesTableConfig** – запись конфигурации, которая определяет имя таблицы PhotoNotesTable. 207 | 208 | - **SearchEndpointConfig** – запись конфигурации, которая определяет адрес поиска для домена CloudSearch, переданного в качестве параметра. 209 | 210 | - **DocumentEndpointConfig** – запись конфигурации, которая определяет адрес документа для домена CloudSearch, переданного в качестве параметра. 211 | 212 | ## Лицензия 213 | 214 | Данная эталонная архитектура лицензирована в соответствии с лицензией Apache 2.0. 215 | -------------------------------------------------------------------------------- /README/README-TW.md: -------------------------------------------------------------------------------- 1 | # 無伺服器參考架構:行動後端 2 | 3 | ## 簡介 4 | 5 | 行動後端參考架構 ([示意圖](https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/lambda-refarch-mobilebackend.pdf)) 展示如何使用 [AWS Lambda](http://aws.amazon.com/lambda/) 及其他服務,為行動應用程式建立無需伺服器的後端。此儲存庫提供的具體範例應用程式可讓使用者分別使用 Amazon Simple Storage Service (Amazon S3) 與 Amazon API Gateway 上傳照片與筆記。這些筆記存放於 Amazon DynamoDB,並利用 DynamoDB streams 與 Lambda 功能以非同步方式處理,將它們新增至 Amazon CloudSearch 網域。除了 Lambda 功能的原始碼之外,此儲存庫還包含可提供範例,並說明如何使用 iOS 的 AWS Mobile SDK 連接架構中定義的後端資源的原型 iOS 應用程式。 6 | 7 | ## 執行範例 8 | 9 | 若要執行完整的範例應用程式,您必須先部署後端資源,然後編譯並執行範例 iOS 應用程式。 10 | 11 | ### 部署後端 12 | 13 | 系統提供的 AWS CloudFormation 範本將建立此範例所需的大多數後端資源,但您仍需建立 Amazon CloudSearch 網域、API Gateway REST API,以及 AWS CloudFormation 外部的 Cognito Identity Pool。 14 | 15 | #### 步驟 1:建立 CloudSearch 網域 16 | 17 | 1.使用 [AWS CLI](https://aws.amazon.com/cli/) 建立新的 CloudSearch 網域並提供您選擇的網域名稱。 18 | 19 | ``` 20 | aws cloudsearch create-domain --domain-name [YOUR_DOMAIN_NAME] 21 | ``` 22 | 23 | 1.請記下輸出文件中的新網域的 ARN,您將在啟動 CloudFormation 堆疊時使用它做為輸入。 24 | 25 | 1.定義「headline」與「note_text」欄位的索引。 26 | 27 | ``` 28 | aws cloudsearch define-index-field --name headline --type text --domain-name [YOUR_DOMAIN_NAME] 29 | aws cloudsearch define-index-field --name note_text --type text --domain-name [YOUR_DOMAIN_NAME] 30 | ``` 31 | 32 | ``` 33 | aws cloudsearch index-documents --domain-name [YOUR_DOMAIN_NAME] 34 | ``` 35 | 36 | #### 步驟 2:建立 API Gateway REST API 37 | 38 | 1.使用 [AWS CLI](https://aws.amazon.com/cli/) 建立新的 API 並提供您選擇的名稱。 39 | 40 | ``` 41 | aws apigateway create-rest-api --name [YOUR_API_NAME] 42 | ``` 43 | 44 | 1.請記下輸出文件中提供的「API ID」,您將在啟動 CloudFormation 堆疊時使用它做為輸入。 45 | 46 | #### 步驟 3:建立 Amazon Cognito Identity Pool 47 | 48 | 1.使用 [AWS CLI](https://aws.amazon.com/cli/) 建立新的 Identity Pool 並提供您選擇的名稱。 49 | 50 | ``` 51 | aws cognito-identity create-identity-pool --allow-unauthenticated-identities --identity-pool-name [YOUR_POOL_NAME] 52 | ``` 53 | 54 | 1.請記下輸出文件中提供的「IdentityPoolId」,您將在啟動 CloudFormation 堆疊時使用它做為參數。 55 | 56 | #### 步驟 4:啟動 CloudFormation 範本 57 | 58 | 您可以使用系統提供的 CloudFormation 範本與 S3 儲存貯體,在 us-east-1 區域部署整個範例。如果您想在不同的區域部署此範例,您必須在該區域建立 Amazon S3 儲存貯體,然後將範本與 Lambda 功能定義複製至該儲存貯體。 59 | 60 | 選擇 **Launch Stack** 以啟動您帳戶的 us-east-1 區域中的範本。 61 | 62 | [![Launch Lambda Mobile Backend into North Virginia with CloudFormation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/images/cloudformation-launch-stack-button.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=lambda-mobile-backend&templateURL=https://s3.amazonaws.com/awslambda-reference-architectures/mobile-backend/mobile-backend.template) 63 | 64 | 收到提示時,輸入您在先前步驟建立的 CloudSearch 網域、API Gateway REST API 及 Amazon Cognito Identity Pool 等資源的參數值。 65 | 66 | 本文件的 *CloudFormation 範本資源* 章節中有提供關於此範例所建立資源的詳細資訊。 67 | 68 | #### 步驟 5:更新您的 API Gateway REST API 69 | 70 | 在您建立 CloudFormation 堆疊之後,您必須更新先前建立的 API,才能使用新建立的「NotesApiFunction」。 71 | 72 | 1.在 [Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis) 中選擇您的 API。 73 | 1.選擇 **Create Resource** 在 / 之下建立新的子資源。 74 | 1.輸入「notes」做為此資源的名稱,輸入「/notes」做為此資源的路徑。 75 | 1.選擇 **Create Resource**。 76 | 1.在已選取新的「/notes」資源的情況下,選擇 **建立方法**。 77 | 1.選擇「POST」,然後選擇該核取方塊。 78 | 1.選擇 **Lambda 功能** 做為整合類型,然後選擇您啟動 CloudFormation 堆疊的區域做為 Lambda 區域。 79 | 1.在 **Lambda 功能** 中輸入 **`NotesApiFunction`**,然後選擇 CloudFormation 堆疊建立的功能。 80 | 1.選擇 **儲存** 並授與 API Gateway 執行 Lambda 功能的許可。 81 | 1.選擇 **方法請求** 以編輯請求組態。 82 | 1.在 **授權類型** 項目中選擇「AWS_IAM」。 83 | 1.在 **需要 API 金鑰** 項目中選擇「true」。 84 | 1.選擇 **部署 API**。 85 | 1.在 **部署階段** 項目中選擇「新階段」,然後在 **階段名稱** 中輸入一個名稱。 86 | 1.請記下新階段的 **呼叫 URL**,您將在執行範例 iOS 應用程式時使用此數值。 87 | 88 | #### 步驟 6:建立 API 金鑰 89 | 90 | 1.在 [Amazon API Gateway Console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/apis) 中選擇 **APIs**,然後選擇 **API 金鑰**。 91 | 1.選擇 **建立 API 金鑰**。 92 | 1.輸入金鑰的名稱,然後選擇 **已啟用**。 93 | 1.選擇 **儲存** 94 | 1.在 **API 階段關聯** 區段中,選擇您的 API,然後選擇您在先前的步驟中建立的階段。 95 | 1.選擇 **新增**。 96 | 1.請記下 **API 金鑰**,您將在執行行動應用程式時使用它。 97 | 98 | #### 步驟 7:更新您的 Amazon Cognito Identity Pool 99 | 100 | 1.在 [Amazon Cognito Console](https://console.aws.amazon.com/cognito/home?region=us-east-1) 中選擇您的 Identity Pool。 101 | 1.選擇 **編輯 Identity Pool**。 102 | 1.在 **未授權的角色** 與 **已授權的角色** 項目中選擇由 CloudFormation 堆疊建立的 **MobileClientRole**。堆疊的輸出中有提供該角色的完整 ARN。 103 | 1.選擇 **儲存變更**。 104 | 105 | 106 | ### 執行範例 iOS 應用程式 107 | 108 | #### 必要條件 109 | 110 | 若要執行系統提供的 iOS 範例應用程式,您必須執行 Mac OS X 10.10 (Yosemite) 或更新的版本。您還必須安裝最新版的 [Xcode](https://itunes.apple.com/us/app/xcode/id497799835) 與 [Cocoa Pods](https://cocoapods.org/)。 111 | 112 | #### 建立與執行應用程式 113 | 114 | 1.查看或下載儲存庫中的 **ios-sample** 的原始碼。 115 | 1.使用您的後端部署的數值更新「MobileBackendIOS/Constants.swift」。大多數的數值皆可在 CloudFormation 堆疊的輸出中找到。在 AWS 管理主控台中的您的 API 詳細資訊中,可找到 API Gateway 金鑰與終端節點 URL 數值。 116 | 1.從「ios-sample」根目錄執行 Cocoa Pods。 117 | 118 | ``` 119 | pod install 120 | ``` 121 | 122 | 1.在 Xcode 中開啟所產生的「MobileBackendIOS.xcworkspace」檔案。 123 | 124 | ``` 125 | open -a Xcode MobileBackendIOS.xcworkspace 126 | ``` 127 | 128 | 1.按一下 Xcode 視窗最上方的播放按鈕以建立並執行專案。 129 | 130 | ## 測試應用程式 131 | 132 | 此範例應用程式提供兩個功能:上傳圖片與張貼筆記。 133 | 134 | ### 上傳圖片 135 | 136 | 1.在應用程式中選擇 **上傳圖片**。 137 | 1.選擇相機圖示,從相簿中選擇一張圖片後選擇 **選擇**。 138 | 1.選擇 **上傳** 按鈕。 139 | 140 | #### 驗證圖片是否已經上傳 141 | 142 | 您應該會在 Xcode 的輸出窗格中看到一筆有關圖片已上傳至 Amazon S3 的記錄項目。 143 | 144 | 您也可以使用 AWS 管理主控台瀏覽 CloudFormation 堆疊建立的儲存貯體,以確認圖片已正確上傳。 145 | 146 | ### 張貼筆記 147 | 148 | 1.選擇 **張貼筆記**。 149 | 1.在筆記中輸入標題與文字。 150 | 1.選擇 **儲存筆記**。 151 | 152 | #### 驗證筆記是否已經張貼 153 | 154 | 您應該會在 Xcode 的輸出窗格中看到一筆有關筆記已成功儲存的記錄項目。 155 | 156 | 當筆記上傳時,行動應用程式將會呼叫「NotesApiFunction」。您可以在 Amazon CloudWatch 中檢視此功能的記錄。 157 | 158 | 當成功呼叫此功能時,它會在 CloudFormation 堆疊建立的 DynamoDB 資料表中新增一筆資料項目。您可以確認您在應用程式中張貼的筆記已持續存在所建立的資料表中。 159 | 160 | 最後,當筆記持續存在於 DynamoDB 資料表時,將有一筆記錄新增至該資料表的 Stream 中,然後由「DynamoStreamHandlerFunction」進行處理。您可以在 CloudWatch 中檢視此功能的這筆記錄,然後確認新的文件是否已新增至您建立的 CloudSearch 網域。 161 | 162 | 163 | ## 清除應用程式資源 164 | 165 | 若要移除此範例建立的所有資源,請執行以下動作: 166 | 167 | 1.刪除 CloudFormation 堆疊建立的 S3 儲存貯體中的所有物件。 168 | 1.刪除 CloudFormation 堆疊。 169 | 1.刪除 Amazon Cognito Identity Pool、API Gateway 及 CloudSearch 網域。 170 | 1.刪除與 CloudFormation 堆疊所建立的 Lambda 功能相關聯的 CloudWatch 記錄群組。 171 | 172 | ## CloudFormation 範本資源 173 | 174 | ### Lambda 功能 175 | 176 | - **NotesApiFunction** - 處理從行動應用程式透過 API Gateway 所張貼筆記的功能。 177 | 178 | - **SearchApiFunction** - 利用 CloudSearch 網域依據搜尋詞彙搜尋已索引的筆記的功能。 179 | 180 | - **DynamoStreamHandlerFunction** - 依據「PhotoNotesTable」Stream 中的記錄,將已索引的文件新增至系統提供的 CloudSearch 網域的功能。 181 | 182 | ### AWS Identity and Access Management (IAM) 角色 183 | 184 | - **NotesApiRole** - 用於「NotesApiFunction」的角色。此角色授與許可以記錄與使用「PhotoNotesTable」中的項目。 185 | 186 | - **SearchApiRole** - 用於「SearchApiFunction」的角色。此角色授與許可以記錄及搜尋所提供的 CloudSearch 網域。 187 | 188 | - **DynamoStreamHandlerRole** - 用於 DynamoStreamHandlerFunction 的角色。此角色授與許可以記錄及新增文件至所提供的 CloudSearch 網域。 189 | 190 | - **MobileClientRole** - 由 Amazon Cognito Identity Pool 用於未授權與已授權使用者的角色。此角色提供存取所提供 API Gateway REST API,以及授與許可將物件放入「MobileUploadsBucket」。 191 | 192 | ### 其他資源 193 | 194 | - **MobileUploadsBucket** - 用於存放使用者上傳的照片的 S3 儲存貯體。 195 | 196 | - **CloudFrontDistribution** -「MobileUploadsBucket」設定為原始的 CDN 分發。 197 | 198 | - **PhotoNotesTable** - 存放使用者從行動應用程式上傳的筆記的 DynamoDB 資料表。 199 | 200 | ### 組態 201 | 202 | - **ConfigTable** - 存放可供各種 Lambda 功能讀取的組態值的 DynamoDB 資料表。此資料表的名稱「MobileRefArchConfig」已硬編碼至各個功能的程式碼,若未更新該程式碼,將無法修改。 203 | 204 | - **ConfigHelperStack** - 建立自訂資源以將資料項目寫入至「ConfigTable」的子堆疊。此堆疊會建立 Lambda 功能與執行角色以授與「ConfigTable」的 UpdateItem 許可。 205 | 206 | - **NotesTableConfig** - 識別「PhotoNotesTable」名稱的組態項目。 207 | 208 | - **SearchEndpointConfig** - 識別 CloudSearch 網域搜尋端點的組態項目,以參數傳遞。 209 | 210 | - **DocumentEndpointConfig** - 識別 CloudSearch 網域的文件終端節點的組態項目,以參數傳遞。 211 | 212 | ## 授權 213 | 214 | 此參考架構範例依據 Apache 2.0 授權。 215 | -------------------------------------------------------------------------------- /apigateway-models/CreateNoteRequest.txt: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "CreateNoteRequest", 4 | "type": "object", 5 | "properties": { 6 | "noteId": { "type": "string" }, 7 | "headline": { "type": "string" }, 8 | "text": { "type": "string" } 9 | } 10 | } -------------------------------------------------------------------------------- /apigateway-models/CreateNoteResponse.txt: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "CreateNoteResponse", 4 | "type": "object", 5 | "properties": { 6 | "success": { "type": "boolean" } 7 | } 8 | } -------------------------------------------------------------------------------- /assets/cloudsearch-attributes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/lambda-refarch-mobilebackend/378aa2f66bd668230c97ac0b233e0b6d394f2fef/assets/cloudsearch-attributes.png -------------------------------------------------------------------------------- /cloudformation/config-helper.template: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion" : "2010-09-09", 3 | 4 | "Description" : "Template that defines a configuration resource for adding items to a DynamoDB config table", 5 | 6 | "Parameters": { 7 | "ConfigTable": { 8 | "Description": "Name of the DynamoDB table where config values should be stored", 9 | "Type": "String" 10 | } 11 | }, 12 | 13 | "Resources" : { 14 | "AddConfigSetting": { 15 | "Type": "AWS::Lambda::Function", 16 | "Properties": { 17 | "Handler": "index.handler", 18 | "Role": { "Fn::GetAtt" : ["AddConfigExecutionRole", "Arn"] }, 19 | "Code": { 20 | "ZipFile": { "Fn::Join": ["", [ 21 | "var response = require('cfn-response');", 22 | "var AWS = require('aws-sdk');", 23 | "var dynamodb = new AWS.DynamoDB();", 24 | 25 | "exports.handler = function(event, context) {", 26 | " console.log('REQUEST RECEIVED:\\n', JSON.stringify(event));", 27 | " var env = event.ResourceProperties.Environment;", 28 | " var configKey = event.ResourceProperties.Key;", 29 | " var configValue = event.ResourceProperties.Value;", 30 | 31 | " if (event.RequestType == 'Delete') {", 32 | " dynamodb.updateItem({", 33 | " TableName: \"", {"Ref": "ConfigTable"} , "\",", 34 | " Key: { Environment: { S: env } },", 35 | " UpdateExpression: \"REMOVE \" + configKey", 36 | " },", 37 | " function(err, data){", 38 | " if(err) { console.log(JSON.stringify(err)); }", 39 | " response.send(event, context, response.SUCCESS);", 40 | " });", 41 | " return;", 42 | " } else {", 43 | " var param = {", 44 | " TableName: '", {"Ref": "ConfigTable"} , "',", 45 | " Key: { Environment: { S: env } },", 46 | " UpdateExpression: 'SET ' + configKey + ' = :newVal',", 47 | " ExpressionAttributeValues: { ':newVal': { 'S': configValue } }", 48 | " };", 49 | " console.log(JSON.stringify(param));", 50 | " dynamodb.updateItem(param,", 51 | " function(err, data){", 52 | " if(err) { console.log(JSON.stringify(err)); response.send(event, context, response.FAILED, err); }", 53 | " else { response.send(event, context, response.SUCCESS); }", 54 | " });", 55 | " }", 56 | "};" 57 | ]]} 58 | }, 59 | "Runtime": "nodejs6.10", 60 | "Timeout": "30" 61 | } 62 | }, 63 | 64 | "AddConfigExecutionRole": { 65 | "Type": "AWS::IAM::Role", 66 | "Properties": { 67 | "AssumeRolePolicyDocument": { 68 | "Version": "2012-10-17", 69 | "Statement": [{ 70 | "Effect": "Allow", 71 | "Principal": {"Service": ["lambda.amazonaws.com"]}, 72 | "Action": ["sts:AssumeRole"] 73 | }] 74 | }, 75 | "Path": "/", 76 | "Policies": [{ 77 | "PolicyName": "root", 78 | "PolicyDocument": { 79 | "Version": "2012-10-17", 80 | "Statement": [{ 81 | "Effect": "Allow", 82 | "Action": ["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"], 83 | "Resource": "arn:aws:logs:*:*:*" 84 | }, 85 | { 86 | "Sid": "Stmt1453132818000", 87 | "Effect": "Allow", 88 | "Action": [ 89 | "dynamodb:UpdateItem" 90 | ], 91 | "Resource": { 92 | "Fn::Join": ["", ["arn:aws:dynamodb:", { 93 | "Ref": "AWS::Region" 94 | }, 95 | ":", { 96 | "Ref": "AWS::AccountId" 97 | }, 98 | ":table/", { 99 | "Ref": "ConfigTable" 100 | } 101 | ]] 102 | } 103 | }] 104 | } 105 | }] 106 | } 107 | } 108 | }, 109 | 110 | "Outputs" : { 111 | "ServiceToken": { 112 | "Value": { "Fn::GetAtt": ["AddConfigSetting", "Arn"]} 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /cloudformation/mobile-backend.template: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Metadata": { 4 | "AWS::CloudFormation::Interface": { 5 | "ParameterGroups": [{ 6 | "Label": { 7 | "default": "Code Location" 8 | }, 9 | "Parameters": ["CodeBucket", "CodeKeyPrefix"] 10 | }, { 11 | "Label": { 12 | "default": "CloudSearch Endpoints" 13 | }, 14 | "Parameters": ["SearchDomainArn", "SearchEndpoint", "DocumentEndpoint"] 15 | }, { 16 | "Label": { 17 | "default": "API Gateway and Cognito Identity" 18 | }, 19 | "Parameters": ["ApiGatewayId", "CognitoPoolId"] 20 | }], 21 | "ParameterLabels": { 22 | "CodeBucket": { 23 | "default": "Code Bucket" 24 | }, 25 | "CodeKeyPrefix": { 26 | "default": "Key Prefix" 27 | }, 28 | "SearchDomainArn": { 29 | "default": "CloudSearch Domain ARN" 30 | }, 31 | "DocumentEndpoint": { 32 | "default": "Document Endpoint" 33 | }, 34 | "SearchEndpoint": { 35 | "default": "Search Endpoint" 36 | }, 37 | "ApiGatewayId": { 38 | "default": "API Gateway REST API ID" 39 | }, 40 | "CognitoPoolId": { 41 | "default": "Cognito Identity Pool ID" 42 | } 43 | } 44 | } 45 | }, 46 | "Parameters": { 47 | "CodeBucket": { 48 | "Description": "S3 Bucket containing Lambda deployment packages and sub-stack templates", 49 | "Type": "String", 50 | "Default": "awslambda-reference-architectures" 51 | }, 52 | "CodeKeyPrefix": { 53 | "Description": "The key prefix for all deployment packages and sub-stack templates within CodeBucket", 54 | "Type": "String", 55 | "Default": "mobile-backend" 56 | }, 57 | "SearchDomainArn": { 58 | "Description": "The ARN of your CloudSearch domain", 59 | "Type": "String" 60 | }, 61 | "DocumentEndpoint": { 62 | "Description": "The document endpoint of your CloudSearch domain", 63 | "Type": "String" 64 | }, 65 | "SearchEndpoint": { 66 | "Description": "The search endpoint of your CloudSearch domain", 67 | "Type": "String" 68 | }, 69 | "CognitoPoolId": { 70 | "Description": "The ID of your Cognito Identity Pool in the format [region]:[GUID] (e.g. us-east-1:92abc7a7-4bcd-470ac-a003-a93b99ca9338)", 71 | "Type": "String" 72 | }, 73 | "ApiGatewayId": { 74 | "Description": "The ID of your API Gateway REST API. This is the most specific subdomain of your API's invoke URL.", 75 | "Type": "String" 76 | } 77 | }, 78 | "Resources": { 79 | "NotesApiFunction": { 80 | "Type": "AWS::Lambda::Function", 81 | "Properties": { 82 | "Code": { 83 | "S3Bucket": { 84 | "Ref": "CodeBucket" 85 | }, 86 | "S3Key": { 87 | "Fn::Join": ["/", [{ 88 | "Ref": "CodeKeyPrefix" 89 | }, "upload-note.zip"]] 90 | } 91 | }, 92 | "Runtime": "nodejs6.10", 93 | "Description": "Handles posting notes via API Gateway", 94 | "Handler": "index.handler", 95 | "Role": { 96 | "Fn::GetAtt": ["NotesApiRole", "Arn"] 97 | } 98 | } 99 | }, 100 | "SearchApiFunction": { 101 | "Type": "AWS::Lambda::Function", 102 | "Properties": { 103 | "Code": { 104 | "S3Bucket": { 105 | "Ref": "CodeBucket" 106 | }, 107 | "S3Key": { 108 | "Fn::Join": ["/", [{ 109 | "Ref": "CodeKeyPrefix" 110 | }, "search.zip"]] 111 | } 112 | }, 113 | "Runtime": "nodejs6.10", 114 | "Description": "Enables searching notes using CloudSearch domain.", 115 | "Handler": "index.handler", 116 | "Role": { 117 | "Fn::GetAtt": ["SearchApiRole", "Arn"] 118 | } 119 | } 120 | }, 121 | "DynamoStreamHandlerFunction": { 122 | "Type": "AWS::Lambda::Function", 123 | "Properties": { 124 | "Timeout": 10, 125 | "Code": { 126 | "S3Bucket": { 127 | "Ref": "CodeBucket" 128 | }, 129 | "S3Key": { 130 | "Fn::Join": ["/", [{ 131 | "Ref": "CodeKeyPrefix" 132 | }, "stream-handler.zip"]] 133 | } 134 | }, 135 | "Runtime": "nodejs6.10", 136 | "Description": "Handles posted notes and indexes them in CloudSearch domain.", 137 | "Handler": "index.handler", 138 | "Role": { 139 | "Fn::GetAtt": ["DynamoStreamHandlerRole", "Arn"] 140 | } 141 | } 142 | }, 143 | "NotesApiRole": { 144 | "Type": "AWS::IAM::Role", 145 | "Properties": { 146 | "AssumeRolePolicyDocument": { 147 | "Version": "2012-10-17", 148 | "Statement": [{ 149 | "Effect": "Allow", 150 | "Principal": { 151 | "Service": "lambda.amazonaws.com" 152 | }, 153 | "Action": "sts:AssumeRole" 154 | }] 155 | }, 156 | "Policies": [{ 157 | "PolicyName": "lambda_dynamo_exec_role_photoapp", 158 | "PolicyDocument": { 159 | "Version": "2012-10-17", 160 | "Statement": [ 161 | { 162 | "Effect": "Allow", 163 | "Action": [ 164 | "logs:CreateLogGroup", 165 | "logs:CreateLogStream", 166 | "logs:PutLogEvents" 167 | ], 168 | "Resource": [ 169 | "arn:aws:logs:*:*:*" 170 | ] 171 | }, { 172 | "Effect": "Allow", 173 | "Action": [ 174 | "dynamodb:GetItem", 175 | "dynamodb:PutItem", 176 | "dynamodb:Query", 177 | "dynamodb:UpdateItem" 178 | ], 179 | "Resource": { 180 | "Fn::Join": ["", ["arn:aws:dynamodb:", { 181 | "Ref": "AWS::Region" 182 | }, 183 | ":", { 184 | "Ref": "AWS::AccountId" 185 | }, 186 | ":table/", { 187 | "Ref": "PhotoNotesTable" 188 | } 189 | ]] 190 | } 191 | }, 192 | { 193 | "Effect": "Allow", 194 | "Action": [ 195 | "dynamodb:GetItem" 196 | ], 197 | "Resource": { 198 | "Fn::Join": ["", ["arn:aws:dynamodb:", { 199 | "Ref": "AWS::Region" 200 | }, 201 | ":", { 202 | "Ref": "AWS::AccountId" 203 | }, 204 | ":table/", { 205 | "Ref": "ConfigTable" 206 | } 207 | ]] 208 | } 209 | } 210 | ] 211 | } 212 | }] 213 | } 214 | }, 215 | "SearchApiRole": { 216 | "Type": "AWS::IAM::Role", 217 | "Properties": { 218 | "AssumeRolePolicyDocument": { 219 | "Version": "2012-10-17", 220 | "Statement": [{ 221 | "Effect": "Allow", 222 | "Principal": { 223 | "Service": "lambda.amazonaws.com" 224 | }, 225 | "Action": "sts:AssumeRole" 226 | }] 227 | }, 228 | "Policies": [{ 229 | "PolicyName": "lambda_cloudsearch_exec_role_photoapp", 230 | "PolicyDocument": { 231 | "Version": "2012-10-17", 232 | "Statement": [ 233 | { 234 | "Effect": "Allow", 235 | "Action": [ 236 | "logs:CreateLogGroup", 237 | "logs:CreateLogStream", 238 | "logs:PutLogEvents" 239 | ], 240 | "Resource": [ 241 | "arn:aws:logs:*:*:*" 242 | ] 243 | }, { 244 | "Effect": "Allow", 245 | "Action": [ 246 | "cloudsearch:search", 247 | "cloudsearch:suggest" 248 | ], 249 | "Resource": { 250 | "Ref": "SearchDomainArn" 251 | } 252 | }, 253 | { 254 | "Effect": "Allow", 255 | "Action": [ 256 | "dynamodb:GetItem" 257 | ], 258 | "Resource": { 259 | "Fn::Join": ["", ["arn:aws:dynamodb:", { 260 | "Ref": "AWS::Region" 261 | }, 262 | ":", { 263 | "Ref": "AWS::AccountId" 264 | }, 265 | ":table/", { 266 | "Ref": "ConfigTable" 267 | } 268 | ]] 269 | } 270 | }] 271 | } 272 | }] 273 | } 274 | }, 275 | "DynamoStreamHandlerRole": { 276 | "Type": "AWS::IAM::Role", 277 | "Properties": { 278 | "AssumeRolePolicyDocument": { 279 | "Version": "2012-10-17", 280 | "Statement": [{ 281 | "Effect": "Allow", 282 | "Principal": { 283 | "Service": "lambda.amazonaws.com" 284 | }, 285 | "Action": "sts:AssumeRole" 286 | }] 287 | }, 288 | "Policies": [{ 289 | "PolicyName": "lambda_dynamo_exec_role_photoapp", 290 | "PolicyDocument": { 291 | "Version": "2012-10-17", 292 | "Statement": [{ 293 | "Effect": "Allow", 294 | "Action": [ 295 | "logs:CreateLogGroup", 296 | "logs:CreateLogStream", 297 | "logs:PutLogEvents" 298 | ], 299 | "Resource": [ 300 | "arn:aws:logs:*:*:*" 301 | ] 302 | }, { 303 | "Effect": "Allow", 304 | "Action": [ 305 | "cloudsearch:document" 306 | ], 307 | "Resource": { 308 | "Ref": "SearchDomainArn" 309 | } 310 | }, 311 | { 312 | "Effect": "Allow", 313 | "Action": [ 314 | "dynamodb:GetItem" 315 | ], 316 | "Resource": { 317 | "Fn::Join": ["", ["arn:aws:dynamodb:", { 318 | "Ref": "AWS::Region" 319 | }, 320 | ":", { 321 | "Ref": "AWS::AccountId" 322 | }, 323 | ":table/", { 324 | "Ref": "ConfigTable" 325 | } 326 | ]] 327 | } 328 | }, 329 | { 330 | "Effect": "Allow", 331 | "Action": [ 332 | "dynamodb:GetRecords", 333 | "dynamodb:GetShardIterator", 334 | "dynamodb:DescribeStream", 335 | "dynamodb:ListStreams" 336 | ], 337 | "Resource": { "Fn::GetAtt" : [ "PhotoNotesTable", "StreamArn"] } 338 | }] 339 | } 340 | }] 341 | } 342 | }, 343 | "MobileClientRole": { 344 | "Type": "AWS::IAM::Role", 345 | "Properties": { 346 | "AssumeRolePolicyDocument": { 347 | "Version": "2012-10-17", 348 | "Statement": [{ 349 | "Effect": "Allow", 350 | "Principal": { 351 | "Federated": "cognito-identity.amazonaws.com" 352 | }, 353 | "Action": "sts:AssumeRoleWithWebIdentity", 354 | "Condition": { 355 | "StringEquals": { 356 | "cognito-identity.amazonaws.com:aud": { 357 | "Ref": "CognitoPoolId" 358 | } 359 | }, 360 | "ForAnyValue:StringLike": { 361 | "cognito-identity.amazonaws.com:amr": "unauthenticated" 362 | } 363 | } 364 | }] 365 | }, 366 | "Policies": [{ 367 | "PolicyName": "client_api_s3_access", 368 | "PolicyDocument": { 369 | "Version": "2012-10-17", 370 | "Statement": [{ 371 | "Effect": "Allow", 372 | "Action": [ 373 | "s3:GetObject", 374 | "s3:ListBucket", 375 | "s3:PutObject", 376 | "s3:PutObjectAcl" 377 | ], 378 | "Resource": [{ 379 | "Fn::Join": ["", ["arn:aws:s3:::", { 380 | "Ref": "MobileUploadsBucket" 381 | }, "/*"]] 382 | }] 383 | }, { 384 | "Effect": "Allow", 385 | "Action": [ 386 | "execute-api:Invoke" 387 | ], 388 | "Resource": [{ 389 | "Fn::Join": ["", [ 390 | "arn:aws:execute-api:", { 391 | "Ref": "AWS::Region" 392 | }, 393 | ":", { 394 | "Ref": "AWS::AccountId" 395 | }, 396 | ":", { 397 | "Ref": "ApiGatewayId" 398 | }, 399 | "/*" 400 | ]] 401 | }] 402 | } ] 403 | } 404 | }] 405 | } 406 | }, 407 | "MobileUploadsBucket": { 408 | "Type": "AWS::S3::Bucket", 409 | "Properties": { 410 | "AccessControl": "PublicRead" 411 | } 412 | }, 413 | 414 | "CloudFrontDistribution": { 415 | "Type": "AWS::CloudFront::Distribution", 416 | "Properties": { 417 | "DistributionConfig": { 418 | "Enabled": true, 419 | "PriceClass": "PriceClass_All", 420 | "DefaultCacheBehavior": { 421 | "TargetOriginId": "mobile-uploads", 422 | "ViewerProtocolPolicy": "allow-all", 423 | "MinTTL": 0, 424 | "AllowedMethods": [ 425 | "HEAD", 426 | "GET" 427 | ], 428 | "CachedMethods": [ 429 | "HEAD", 430 | "GET" 431 | ], 432 | "ForwardedValues": { 433 | "Cookies": { 434 | "Forward": "none" 435 | }, 436 | "QueryString": "false" 437 | } 438 | }, 439 | "Origins": [{ 440 | "DomainName": { 441 | "Fn::Join": ["", [{ 442 | "Ref": "MobileUploadsBucket" 443 | }, ".s3.amazonaws.com"]] 444 | }, 445 | "Id": "mobile-uploads", 446 | "S3OriginConfig": {} 447 | }], 448 | "Restrictions": { 449 | "GeoRestriction": { 450 | "RestrictionType": "none", 451 | "Locations": [] 452 | } 453 | }, 454 | "ViewerCertificate": { 455 | "CloudFrontDefaultCertificate": "true", 456 | "MinimumProtocolVersion": "SSLv3" 457 | } 458 | } 459 | } 460 | }, 461 | 462 | "PhotoNotesTable": { 463 | "Type": "AWS::DynamoDB::Table", 464 | "Properties": { 465 | "AttributeDefinitions": [{ 466 | "AttributeName": "noteId", 467 | "AttributeType": "S" 468 | }], 469 | "KeySchema": [{ 470 | "AttributeName": "noteId", 471 | "KeyType": "HASH" 472 | }], 473 | "ProvisionedThroughput": { 474 | "ReadCapacityUnits": "10", 475 | "WriteCapacityUnits": "10" 476 | }, 477 | "StreamSpecification": { 478 | "StreamViewType": "NEW_IMAGE" 479 | } 480 | } 481 | }, 482 | 483 | "DdbStreamHandlerSourceMapping": { 484 | "Type": "AWS::Lambda::EventSourceMapping", 485 | "Properties": { 486 | "FunctionName": {"Ref": "DynamoStreamHandlerFunction"}, 487 | "StartingPosition": "TRIM_HORIZON", 488 | "BatchSize": 25, 489 | "EventSourceArn": { 490 | "Fn::GetAtt" : [ "PhotoNotesTable", "StreamArn"] 491 | } 492 | } 493 | }, 494 | 495 | "ConfigTable": { 496 | "Type" : "AWS::DynamoDB::Table", 497 | "Properties" : { 498 | "TableName" : "MobileRefArchConfig", 499 | "AttributeDefinitions" : [ 500 | { 501 | "AttributeName" : "Environment", 502 | "AttributeType" : "S" 503 | } 504 | ], 505 | "KeySchema" : [ 506 | { 507 | "AttributeName" : "Environment", 508 | "KeyType" : "HASH" 509 | } 510 | ], 511 | "ProvisionedThroughput" : { 512 | "ReadCapacityUnits" : 1, 513 | "WriteCapacityUnits" : 1 514 | } 515 | } 516 | }, 517 | 518 | "ConfigHelperStack": { 519 | "Type" : "AWS::CloudFormation::Stack", 520 | "Properties" : { 521 | "TemplateURL" : {"Fn::Join": ["/", ["https://s3.amazonaws.com", {"Ref": "CodeBucket"}, {"Ref": "CodeKeyPrefix"}, "config-helper.template"]]}, 522 | "Parameters" : { 523 | "ConfigTable": { "Ref": "ConfigTable" } 524 | }, 525 | "TimeoutInMinutes" : 2 526 | } 527 | }, 528 | 529 | "NotesTableConfig": { 530 | "Type": "Custom::ConfigSetting", 531 | "Properties": { 532 | "ServiceToken": { "Fn::GetAtt" : ["ConfigHelperStack", "Outputs.ServiceToken"] }, 533 | "Environment": "demo", 534 | "Key": "NotesTable", 535 | "Value": { "Ref": "PhotoNotesTable" } 536 | } 537 | }, 538 | 539 | "SearchEndpointConfig": { 540 | "Type": "Custom::ConfigSetting", 541 | "Properties": { 542 | "ServiceToken": { "Fn::GetAtt" : ["ConfigHelperStack", "Outputs.ServiceToken"] }, 543 | "Environment": "demo", 544 | "Key": "SearchEndpoint", 545 | "Value": { "Ref": "SearchEndpoint" } 546 | } 547 | }, 548 | 549 | "DocumentEndpointConfig": { 550 | "Type": "Custom::ConfigSetting", 551 | "Properties": { 552 | "ServiceToken": { "Fn::GetAtt" : ["ConfigHelperStack", "Outputs.ServiceToken"] }, 553 | "Environment": "demo", 554 | "Key": "DocumentEndpoint", 555 | "Value": { "Ref": "DocumentEndpoint" } 556 | } 557 | } 558 | }, 559 | "Outputs": { 560 | "S3BucketName": { 561 | "Description": "Set S3BucketName in Constants.swift to this value.", 562 | "Value": { 563 | "Ref": "MobileUploadsBucket" 564 | } 565 | }, 566 | "NotesApiFunctionArn": { 567 | "Description": "Use this function to back the POST method on the /notes resource of your API.", 568 | "Value": { 569 | "Fn::GetAtt": ["NotesApiFunction", "Arn"] 570 | } 571 | }, 572 | "CognitoIdentityPoolId": { 573 | "Description": "Set CongnitoIdentityPoolId in Constants.swift to this value.", 574 | "Value": { 575 | "Ref": "CognitoPoolId" 576 | } 577 | }, 578 | "AWSAccountId": { 579 | "Description": "Set AWSAccountId in Constants.swift to this value.", 580 | "Value": { 581 | "Ref": "AWS::AccountId" 582 | } 583 | }, 584 | "CognitoRoleArn": { 585 | "Description": "Use this for both the authenticated and unauthenticated roles in your Cognito Identity Pool.", 586 | "Value": { 587 | "Fn::GetAtt": ["MobileClientRole", "Arn"] 588 | } 589 | }, 590 | "CloudFrontUrl": { 591 | "Description": "CloudFront URL for the distribution fronting the S3 bucket", 592 | "Value": { 593 | "Ref": "CloudFrontDistribution" 594 | } 595 | } 596 | } 597 | } 598 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | // 6 | // Use this file to import your target's public headers that you would like to expose to Swift. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | // Import of API Gateway 14 | #import "APINotesApiClient.h" 15 | #import "APICreateNoteRequest.h" -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS.xcodeproj/project.xcworkspace/xcuserdata/oladehin.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/lambda-refarch-mobilebackend/378aa2f66bd668230c97ac0b233e0b6d394f2fef/ios-sample/MobileBackendIOS.xcodeproj/project.xcworkspace/xcuserdata/oladehin.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/APICreateNoteRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/apache2.0 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | 17 | #import 18 | #import 19 | 20 | 21 | @interface APICreateNoteRequest : AWSModel 22 | 23 | @property (nonatomic, strong) NSString *noteId; 24 | 25 | 26 | @property (nonatomic, strong) NSString *headline; 27 | 28 | 29 | @property (nonatomic, strong) NSString *text; 30 | 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/APICreateNoteRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/apache2.0 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | 17 | #import "APICreateNoteRequest.h" 18 | 19 | @implementation APICreateNoteRequest 20 | 21 | + (NSDictionary *)JSONKeyPathsByPropertyKey { 22 | return @{ 23 | @"noteId": @"noteId", 24 | @"headline": @"headline", 25 | @"text": @"text" 26 | }; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/APICreateNoteResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/apache2.0 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | 17 | #import 18 | #import 19 | 20 | 21 | @interface APICreateNoteResponse : AWSModel 22 | 23 | @property (nonatomic, strong) NSNumber *success; 24 | 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/APICreateNoteResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/apache2.0 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | 17 | #import "APICreateNoteResponse.h" 18 | 19 | @implementation APICreateNoteResponse 20 | 21 | + (NSDictionary *)JSONKeyPathsByPropertyKey { 22 | return @{ 23 | @"success": @"success" 24 | }; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/APINotesApiClient.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/apache2.0 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | 17 | #import 18 | #import 19 | 20 | #import "APICreateNoteRequest.h" 21 | #import "APICreateNoteResponse.h" 22 | 23 | /** 24 | The service client object. 25 | */ 26 | @interface APINotesApiClient: AWSAPIGatewayClient 27 | 28 | /** 29 | Returns the singleton service client. If the singleton object does not exist, the SDK instantiates the default service client with `defaultServiceConfiguration` from `[AWSServiceManager defaultServiceManager]`. The reference to this object is maintained by the SDK, and you do not need to retain it manually. 30 | 31 | If you want to enable AWS Signature, set the default service configuration in `- application:didFinishLaunchingWithOptions:` 32 | 33 | *Swift* 34 | 35 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 36 | let credentialProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "YourIdentityPoolId") 37 | let configuration = AWSServiceConfiguration(region: .USEast1, credentialsProvider: credentialProvider) 38 | AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = configuration 39 | 40 | return true 41 | } 42 | 43 | *Objective-C* 44 | 45 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 46 | AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 47 | identityPoolId:@"YourIdentityPoolId"]; 48 | AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 49 | credentialsProvider:credentialsProvider]; 50 | [AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration; 51 | 52 | return YES; 53 | } 54 | 55 | Then call the following to get the default service client: 56 | 57 | *Swift* 58 | 59 | let serviceClient = APINotesApiClient.defaultClient() 60 | 61 | *Objective-C* 62 | 63 | APINotesApiClient *serviceClient = [APINotesApiClient defaultClient]; 64 | 65 | @return The default service client. 66 | */ 67 | + (instancetype)defaultClient; 68 | 69 | /** 70 | Creates a service client with the given service configuration and registers it for the key. 71 | 72 | If you want to enable AWS Signature, set the default service configuration in `- application:didFinishLaunchingWithOptions:` 73 | 74 | *Swift* 75 | 76 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 77 | let credentialProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "YourIdentityPoolId") 78 | let configuration = AWSServiceConfiguration(region: .USWest2, credentialsProvider: credentialProvider) 79 | APINotesApiClient.registerClientWithConfiguration(configuration, forKey: "USWest2APINotesApiClient") 80 | 81 | return true 82 | } 83 | 84 | *Objective-C* 85 | 86 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 87 | AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 88 | identityPoolId:@"YourIdentityPoolId"]; 89 | AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSWest2 90 | credentialsProvider:credentialsProvider]; 91 | 92 | [APINotesApiClient registerClientWithConfiguration:configuration forKey:@"USWest2APINotesApiClient"]; 93 | 94 | return YES; 95 | } 96 | 97 | Then call the following to get the service client: 98 | 99 | *Swift* 100 | 101 | let serviceClient = APINotesApiClient(forKey: "USWest2APINotesApiClient") 102 | 103 | *Objective-C* 104 | 105 | APINotesApiClient *serviceClient = [APINotesApiClient clientForKey:@"USWest2APINotesApiClient"]; 106 | 107 | @warning After calling this method, do not modify the configuration object. It may cause unspecified behaviors. 108 | 109 | @param configuration A service configuration object. 110 | @param key A string to identify the service client. 111 | */ 112 | + (void)registerClientWithConfiguration:(AWSServiceConfiguration *)configuration forKey:(NSString *)key withUrl:(NSString*) url; 113 | 114 | /** 115 | Retrieves the service client associated with the key. You need to call `+ registerClientWithConfiguration:forKey:` before invoking this method. If `+ registerClientWithConfiguration:forKey:` has not been called in advance or the key does not exist, this method returns `nil`. 116 | 117 | For example, set the default service configuration in `- application:didFinishLaunchingWithOptions:` 118 | 119 | *Swift* 120 | 121 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 122 | let credentialProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "YourIdentityPoolId") 123 | let configuration = AWSServiceConfiguration(region: .USWest2, credentialsProvider: credentialProvider) 124 | APINotesApiClient.registerClientWithConfiguration(configuration, forKey: "USWest2APINotesApiClient") 125 | 126 | return true 127 | } 128 | 129 | *Objective-C* 130 | 131 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 132 | AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 133 | identityPoolId:@"YourIdentityPoolId"]; 134 | AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSWest2 135 | credentialsProvider:credentialsProvider]; 136 | 137 | [APINotesApiClient registerClientWithConfiguration:configuration forKey:@"USWest2APINotesApiClient"]; 138 | 139 | return YES; 140 | } 141 | 142 | Then call the following to get the service client: 143 | 144 | *Swift* 145 | 146 | let serviceClient = APINotesApiClient(forKey: "USWest2APINotesApiClient") 147 | 148 | *Objective-C* 149 | 150 | APINotesApiClient *serviceClient = [APINotesApiClient clientForKey:@"USWest2APINotesApiClient"]; 151 | 152 | @param key A string to identify the service client. 153 | 154 | @return An instance of the service client. 155 | */ 156 | + (instancetype)clientForKey:(NSString *)key; 157 | 158 | /** 159 | Removes the service client associated with the key and release it. 160 | 161 | @warning Before calling this method, make sure no method is running on this client. 162 | 163 | @param key A string to identify the service client. 164 | */ 165 | + (void)removeClientForKey:(NSString *)key; 166 | 167 | /** 168 | 169 | 170 | @param body 171 | 172 | return type: APICreateNoteResponse * 173 | */ 174 | - (AWSTask *)notesPost:(APICreateNoteRequest *)body; 175 | 176 | @end 177 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/APINotesApiClient.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/apache2.0 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | 17 | #import "APINotesApiClient.h" 18 | #import 19 | #import 20 | #import 21 | 22 | #import "APICreateNoteRequest.h" 23 | #import "APICreateNoteResponse.h" 24 | 25 | 26 | @interface AWSAPIGatewayClient() 27 | 28 | // Networking 29 | @property (nonatomic, strong) NSURLSession *session; 30 | 31 | // For requests 32 | @property (nonatomic, strong) NSURL *baseURL; 33 | 34 | // For responses 35 | @property (nonatomic, strong) NSDictionary *HTTPHeaderFields; 36 | @property (nonatomic, assign) NSInteger HTTPStatusCode; 37 | 38 | - (AWSTask *)invokeHTTPRequest:(NSString *)HTTPMethod 39 | URLString:(NSString *)URLString 40 | pathParameters:(NSDictionary *)pathParameters 41 | queryParameters:(NSDictionary *)queryParameters 42 | headerParameters:(NSDictionary *)headerParameters 43 | body:(id)body 44 | responseClass:(Class)responseClass; 45 | 46 | @end 47 | 48 | @interface APINotesApiClient() 49 | 50 | @property (nonatomic, strong) AWSServiceConfiguration *configuration; 51 | 52 | @end 53 | 54 | @interface AWSServiceConfiguration() 55 | 56 | @property (nonatomic, strong) AWSEndpoint *endpoint; 57 | 58 | @end 59 | 60 | @implementation APINotesApiClient 61 | 62 | @synthesize configuration = _configuration; 63 | 64 | static AWSSynchronizedMutableDictionary *_serviceClients = nil; 65 | 66 | 67 | + (void)registerClientWithConfiguration:(AWSServiceConfiguration *)configuration forKey:(NSString *)key 68 | withUrl:(NSString*) url { 69 | static dispatch_once_t onceToken; 70 | dispatch_once(&onceToken, ^{ 71 | _serviceClients = [AWSSynchronizedMutableDictionary new]; 72 | }); 73 | [_serviceClients setObject:[[APINotesApiClient alloc] initWithConfiguration:configuration withUrl:url] 74 | forKey:key]; 75 | } 76 | 77 | + (instancetype)clientForKey:(NSString *)key { 78 | return [_serviceClients objectForKey:key]; 79 | } 80 | 81 | + (void)removeClientForKey:(NSString *)key { 82 | [_serviceClients removeObjectForKey:key]; 83 | } 84 | 85 | - (instancetype)init { 86 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 87 | reason:@"`- init` is not a valid initializer. Use `+ defaultClient` or `+ clientForKey:` instead." 88 | userInfo:nil]; 89 | return nil; 90 | } 91 | 92 | - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)configuration 93 | withUrl:(NSString *)URLString { 94 | if (self = [super init]) { 95 | _configuration = [configuration copy]; 96 | 97 | if ([URLString hasSuffix:@"/"]) { 98 | URLString = [URLString substringToIndex:[URLString length] - 1]; 99 | } 100 | _configuration.endpoint = [[AWSEndpoint alloc] initWithRegion:_configuration.regionType 101 | service:AWSServiceAPIGateway 102 | URL:[NSURL URLWithString:URLString]]; 103 | 104 | AWSSignatureV4Signer *signer = [AWSSignatureV4Signer signerWithCredentialsProvider:_configuration.credentialsProvider 105 | endpoint:_configuration.endpoint]; 106 | 107 | _configuration.baseURL = _configuration.endpoint.URL; 108 | _configuration.requestInterceptors = @[[AWSNetworkingRequestInterceptor new], signer]; 109 | } 110 | 111 | return self; 112 | } 113 | 114 | - (AWSTask *)notesPost:(APICreateNoteRequest *)body { 115 | NSDictionary *headerParameters = @{ 116 | @"Content-Type": @"application/json", 117 | @"Accept": @"application/json", 118 | 119 | }; 120 | NSDictionary *queryParameters = @{ 121 | 122 | }; 123 | NSDictionary *pathParameters = @{ 124 | 125 | }; 126 | 127 | return [self invokeHTTPRequest:@"POST" 128 | URLString:@"/notes" 129 | pathParameters:pathParameters 130 | queryParameters:queryParameters 131 | headerParameters:headerParameters 132 | body:body 133 | responseClass:[APICreateNoteResponse class]]; 134 | } 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/AddNoteViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // MobileBackendIOS 4 | // 5 | 6 | import Foundation 7 | import UIKit 8 | import MobileCoreServices 9 | 10 | class AddNoteViewController: UIViewController { 11 | 12 | @IBOutlet weak var headlineTextField: UITextField! 13 | @IBOutlet weak var noteTextField: UITextField! 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | MobileBackendApi.sharedInstance.configureNoteApi() 18 | } 19 | 20 | @IBAction func saveNoteButtonPressed(sender: UIButton) { 21 | if(headlineTextField.text != nil && noteTextField.text != nil) { 22 | MobileBackendApi.sharedInstance.postNote(headlineTextField.text!, text: noteTextField.text!) 23 | headlineTextField.text = nil 24 | noteTextField.text = nil 25 | } else { 26 | print("Error text fields are nil") 27 | } 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MobileBackendIOS 4 | // 5 | 6 | import UIKit 7 | 8 | @UIApplicationMain 9 | class AppDelegate: UIResponder, UIApplicationDelegate { 10 | 11 | var window: UIWindow? 12 | 13 | 14 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 15 | var mobileBackendService = MobileBackendApi.sharedInstance 16 | mobileBackendService.requestCognitoIdentity() 17 | 18 | return true 19 | } 20 | 21 | func applicationWillResignActive(application: UIApplication) { 22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 23 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 24 | } 25 | 26 | func applicationDidEnterBackground(application: UIApplication) { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(application: UIApplication) { 32 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 33 | } 34 | 35 | func applicationDidBecomeActive(application: UIApplication) { 36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 37 | } 38 | 39 | func applicationWillTerminate(application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 31 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // MobileBackendIOS 4 | // 5 | 6 | import Foundation 7 | 8 | //WARNING: To run this sample correctly, you must set the following constants. 9 | let CognitoRegionType = AWSRegionType.USEast1 10 | let DefaultServiceRegionType = AWSRegionType.USEast1 11 | let CognitoIdentityPoolId = "" 12 | let CognitoUnauthenticatedRoleArn = "" 13 | let CognitoAuthenticatedRoleArn = "" 14 | let AWSAccountId = "" 15 | let APIGatewayKey = "" 16 | let APIEndpointUrl = "" 17 | let S3BucketName: String = "" 18 | 19 | 20 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | NSAppTransportSecurity 40 | 41 | NSExceptionDomains 42 | 43 | amazonaws.com 44 | 45 | NSThirdPartyExceptionMinimumTLSVersion 46 | TLSv1.0 47 | NSThirdPartyExceptionRequiresForwardSecrecy 48 | 49 | NSIncludesSubdomains 50 | 51 | 52 | amazonaws.com.cn 53 | 54 | NSThirdPartyExceptionMinimumTLSVersion 55 | TLSv1.0 56 | NSThirdPartyExceptionRequiresForwardSecrecy 57 | 58 | NSIncludesSubdomains 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/MobileBackendApi.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MobileBackendApi.swift 3 | // MobileBackendIOS 4 | // 5 | 6 | import Foundation 7 | 8 | 9 | class MobileBackendApi { 10 | 11 | static let sharedInstance = MobileBackendApi() 12 | let awsCognitoCredentialsProvider: AWSCognitoCredentialsProvider 13 | var cognitoId:String? 14 | 15 | init() { 16 | //Initialize the identity provider 17 | self.awsCognitoCredentialsProvider = AWSCognitoCredentialsProvider.credentialsWithRegionType(CognitoRegionType, accountId: AWSAccountId, identityPoolId: CognitoIdentityPoolId, unauthRoleArn: CognitoUnauthenticatedRoleArn, authRoleArn: CognitoAuthenticatedRoleArn) 18 | } 19 | 20 | func requestCognitoIdentity() { 21 | awsCognitoCredentialsProvider.getIdentityId().continueWithBlock() { (task) -> AnyObject! in 22 | if let error = task.error { 23 | print("Error Requesting Unauthenticated user identity: \(error.userInfo)") 24 | self.cognitoId = nil 25 | } else { 26 | self.cognitoId = self.awsCognitoCredentialsProvider.identityId 27 | } 28 | return nil 29 | } 30 | } 31 | 32 | func configureS3TransferManager() { 33 | let configuration = AWSServiceConfiguration(region: DefaultServiceRegionType, credentialsProvider: self.awsCognitoCredentialsProvider) 34 | AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = configuration 35 | 36 | AWSS3TransferManager.registerS3TransferManagerWithConfiguration(configuration, forKey: "USEast1AWSTransferManagerClient") 37 | } 38 | 39 | func configureNoteApi() { 40 | let configuration = AWSServiceConfiguration(region: DefaultServiceRegionType, credentialsProvider: self.awsCognitoCredentialsProvider) 41 | AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = configuration 42 | 43 | APINotesApiClient.registerClientWithConfiguration(configuration, forKey: "USEast1NoteAPIManagerClient", withUrl: APIEndpointUrl) 44 | APINotesApiClient(forKey: "USEast1NoteAPIManagerClient").APIKey = APIGatewayKey 45 | } 46 | 47 | func postNote(headline: String, text: String) { 48 | let noteRequest = APICreateNoteRequest() 49 | noteRequest.headline = headline 50 | noteRequest.text = text 51 | noteRequest.noteId = NSUUID().UUIDString 52 | 53 | let noteApiClient = APINotesApiClient(forKey: "USEast1NoteAPIManagerClient") 54 | noteApiClient.notesPost(noteRequest).continueWithBlock { (task) -> AnyObject! in 55 | if let error = task.error { 56 | print("Failed creating note: [\(error)]") 57 | } 58 | if let exception = task.exception { 59 | print("Failed creating note: [\(exception)]") 60 | } 61 | if let noteResponse = task.result as? APICreateNoteResponse { 62 | if((noteResponse.success) != nil) { 63 | print("Saved note successfully") 64 | }else { 65 | print("Unable to save note due to unknown error") 66 | } 67 | } 68 | return task 69 | } 70 | } 71 | 72 | func uploadImageToS3(localFilePath: String, localFileName: String) { 73 | let uploadRequest:AWSS3TransferManagerUploadRequest = AWSS3TransferManagerUploadRequest() 74 | uploadRequest.bucket = S3BucketName 75 | uploadRequest.ACL = AWSS3ObjectCannedACL.PublicRead 76 | uploadRequest.contentType = "image/png" 77 | uploadRequest.body = NSURL(fileURLWithPath: localFilePath) 78 | uploadRequest.key = localFileName 79 | 80 | let s3TransferManager = AWSS3TransferManager.S3TransferManagerForKey("USEast1AWSTransferManagerClient") 81 | 82 | s3TransferManager.upload(uploadRequest).continueWithBlock { (task) -> AnyObject! in 83 | if let error = task.error { 84 | if error.domain == AWSS3TransferManagerErrorDomain as String { 85 | print("upload() failed: [\(error)]") 86 | } else { 87 | print("upload() failed: [\(error)]") 88 | } 89 | } 90 | 91 | if let exception = task.exception { 92 | print("upload() failed: [\(exception)]") 93 | } 94 | 95 | if task.result != nil { 96 | print("Uploaded local file to S3: [\(localFileName)]") 97 | } 98 | return nil 99 | } 100 | } 101 | 102 | } 103 | 104 | -------------------------------------------------------------------------------- /ios-sample/MobileBackendIOS/UploadPhotoViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UploadPhotoViewController.swift 3 | // MobileBackendIOS 4 | // 5 | 6 | 7 | import Foundation 8 | import UIKit 9 | import MobileCoreServices 10 | 11 | class UploadPhotoViewController: UIViewController,UIImagePickerControllerDelegate, UINavigationControllerDelegate { 12 | 13 | @IBOutlet weak var photoImageView: UIImageView! 14 | @IBOutlet weak var uploadButton: UIButton! 15 | @IBOutlet weak var cameraButton: UIBarButtonItem! 16 | 17 | private let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString 18 | private let fileManager = NSFileManager.defaultManager() 19 | var imagePickerController:UIImagePickerController? 20 | 21 | override func viewDidLoad() { 22 | MobileBackendApi.sharedInstance.configureS3TransferManager() 23 | uploadButton.enabled = false 24 | } 25 | 26 | @IBAction func uploadImageButtonPressed(sender: UIButton) { 27 | let imgDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] 28 | let fileName = NSProcessInfo.processInfo().globallyUniqueString.stringByAppendingString(".png") 29 | let fullyQualifiedPath = "\(imgDirectoryPath)/\(fileName)" 30 | 31 | self.saveFileAndUpload(fullyQualifiedPath, imageName: fileName) 32 | 33 | } 34 | 35 | @IBAction func cameraButtonPressed(sender: UIBarButtonItem) { 36 | imagePickerController = UIImagePickerController() 37 | if let imageController = imagePickerController { 38 | imageController.mediaTypes = [kUTTypeImage as String] 39 | imageController.allowsEditing = true 40 | imageController.delegate = self 41 | 42 | if isPhotoCameraAvailable() { 43 | imageController.sourceType = .Camera 44 | } else { 45 | imageController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary 46 | } 47 | presentViewController( imageController, animated: true, completion: nil) 48 | } 49 | } 50 | 51 | 52 | func imagePickerController(picker: UIImagePickerController, 53 | didFinishPickingMediaWithInfo info: [String : AnyObject]){ 54 | 55 | let mediaType:AnyObject? = info[UIImagePickerControllerMediaType] 56 | 57 | if let currentMediaType:AnyObject = mediaType { 58 | if currentMediaType is String { 59 | let imageType = currentMediaType as! String 60 | if imageType == kUTTypeImage as NSString { 61 | let image = info[ UIImagePickerControllerOriginalImage] as? UIImage 62 | if let currentImage = image{ 63 | //Process Image 64 | let size = CGSizeApplyAffineTransform(currentImage.size, CGAffineTransformMakeScale(0.25, 0.25)) 65 | let hasAlpha = false 66 | let scale: CGFloat = 0.0 // Automatically use scale factor of main screen 67 | 68 | UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale) 69 | currentImage.drawInRect(CGRect(origin: CGPointZero, size: size)) 70 | 71 | let scaledImage = UIGraphicsGetImageFromCurrentImageContext() 72 | UIGraphicsEndImageContext() 73 | 74 | //Save Image 75 | self.photoImageView.image = scaledImage 76 | self.uploadButton.enabled = true 77 | 78 | } 79 | } 80 | } 81 | } 82 | 83 | picker.dismissViewControllerAnimated( true, completion: nil) 84 | } 85 | 86 | func imagePickerControllerDidCancel(picker: UIImagePickerController) { 87 | print(" Picker was cancelled") 88 | picker.dismissViewControllerAnimated( true, completion: nil) 89 | } 90 | 91 | func saveFileAndUpload(imagePath:String, imageName:String) { 92 | guard let data = UIImageJPEGRepresentation(self.photoImageView.image!, 1.0) else { 93 | print("Error: Converting Image To Data") 94 | return 95 | } 96 | if fileManager.createFileAtPath(imagePath, contents: data, attributes: nil){ 97 | MobileBackendApi.sharedInstance.uploadImageToS3(imagePath,localFileName: imageName) 98 | } 99 | } 100 | 101 | private func isPhotoCameraAvailable() -> Bool{ 102 | return UIImagePickerController.isSourceTypeAvailable(.Camera) 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /ios-sample/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | pod 'AWSCore' 4 | pod 'AWSCognito' 5 | pod 'AWSS3' 6 | pod 'AWSAPIGateway', '~> 2.2.1' 7 | -------------------------------------------------------------------------------- /lambda-functions/package: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pushd upload-note 4 | zip ../upload-note.zip ./* 5 | popd 6 | 7 | pushd stream-handler 8 | zip ../stream-handler.zip ./* 9 | popd 10 | 11 | pushd search 12 | zip ../search.zip ./* 13 | popd 14 | -------------------------------------------------------------------------------- /lambda-functions/search/index.js: -------------------------------------------------------------------------------- 1 | var AWS = require('aws-sdk'); 2 | var doc = new AWS.DynamoDB.DocumentClient(); 3 | 4 | var config; 5 | 6 | exports.handler = function(event, context) { 7 | if (config) { 8 | handleEvent(event, context); 9 | } else { 10 | var params = { 11 | TableName: 'MobileRefArchConfig', 12 | Key: { Environment: 'demo' } 13 | }; 14 | doc.get(params, function(err, data) { 15 | if (err) { 16 | console.log(err, err.stack); 17 | context.fail(err); 18 | } else { 19 | config = data.Item; 20 | handleEvent(event, context); 21 | } 22 | }); 23 | } 24 | }; 25 | 26 | function handleEvent(event, context) { 27 | var cloudSearchDomain = new AWS.CloudSearchDomain({ 28 | endpoint: config.SearchEndpoint 29 | }); 30 | 31 | var params = { 32 | query: event.searchTerm, 33 | size: 10, 34 | start: 0 35 | }; 36 | 37 | cloudSearchDomain.search(params, function(err, data) { 38 | if (err) { 39 | context.fail(new Error('Error searching for documents with term: "' + event.searchTerm + '"')); // an error occurred 40 | } else { 41 | context.succeed(processSearchResults(data)); 42 | } 43 | }); 44 | } 45 | 46 | function processSearchResults(data) { 47 | //Set base response type 48 | var response = { 49 | success: true 50 | }; 51 | 52 | var searchResults = []; 53 | var hits = data.hits.hit; 54 | 55 | for (var i = 0; i < hits.length; i++) { 56 | //retrieve the next notes 57 | var currentMatch = hits[i]; 58 | var searchResult = {}; 59 | 60 | //Configure each note and push onto the search results 61 | searchResult.noteId = currentMatch.id; 62 | searchResult.headline = currentMatch.fields.headline[0]; 63 | searchResult.text = currentMatch.fields.note_text[0]; 64 | searchResults.push(searchResult); 65 | } 66 | 67 | response.notes = searchResults; 68 | return response; 69 | } 70 | -------------------------------------------------------------------------------- /lambda-functions/stream-handler/index.js: -------------------------------------------------------------------------------- 1 | var AWS = require('aws-sdk'); 2 | var doc = new AWS.DynamoDB.DocumentClient(); 3 | 4 | var config; 5 | 6 | exports.handler = function(event, context) { 7 | if (config) { 8 | handleEvent(event, context); 9 | } else { 10 | var params = { 11 | TableName: 'MobileRefArchConfig', 12 | Key: { Environment: 'demo' } 13 | }; 14 | doc.get(params, function(err, data) { 15 | if (err) { 16 | console.log(err, err.stack); 17 | context.fail(err); 18 | } else { 19 | config = data.Item; 20 | handleEvent(event, context); 21 | } 22 | }); 23 | } 24 | }; 25 | 26 | function handleEvent(event, context) { 27 | var records = event.Records; 28 | var searchDocuments = createSearchDocuments(records); 29 | 30 | uploadSearchDocuments(context, searchDocuments); 31 | } 32 | 33 | function createSearchDocuments(records) { 34 | var searchDocuments = []; 35 | 36 | for(var i = 0; i 0) { 56 | var cloudSearchDomain = new AWS.CloudSearchDomain({ 57 | endpoint: config.DocumentEndpoint 58 | }); 59 | 60 | var params = { 61 | contentType : 'application/json', 62 | documents : JSON.stringify(searchDocuments) 63 | }; 64 | 65 | cloudSearchDomain.uploadDocuments(params, function(error, result) { 66 | if(!error) { 67 | context.succeed("Processed " + searchDocuments.length + " search records."); 68 | } else { 69 | context.fail(new Error('Unable to upload search documents: ' + error)); 70 | } 71 | }); 72 | } else { 73 | context.succeed("No new documents were added to the DynamoDB Table."); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lambda-functions/upload: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # If running in region other than US EAST 1 and the provided Template 4 | # Then, change BUCKET to your custom bucket name 5 | BUCKET=awslambda-reference-architectures 6 | aws s3 cp --acl public-read upload-note.zip s3://$BUCKET/mobile-backend/upload-note.zip 7 | aws s3 cp --acl public-read search.zip s3://$BUCKET/mobile-backend/search.zip 8 | aws s3 cp --acl public-read stream-handler.zip s3://$BUCKET/mobile-backend/stream-handler.zip 9 | -------------------------------------------------------------------------------- /lambda-functions/upload-note/index.js: -------------------------------------------------------------------------------- 1 | var AWS = require('aws-sdk'); 2 | 3 | var DOC = require('dynamodb-doc'); 4 | 5 | var doc = new AWS.DynamoDB.DocumentClient(); 6 | 7 | var config; 8 | 9 | exports.handler = function(event, context) { 10 | if (config) { 11 | handleEvent(event, context); 12 | } else { 13 | var params = { 14 | TableName: 'MobileRefArchConfig', 15 | Key: { Environment: 'demo' } 16 | }; 17 | doc.get(params, function(err, data) { 18 | if (err) { 19 | console.log(err, err.stack); 20 | context.fail(err); 21 | } else { 22 | config = data.Item; 23 | handleEvent(event, context); 24 | } 25 | }); 26 | } 27 | }; 28 | 29 | function handleEvent(createNoteEvent, context) { 30 | var note = { 31 | TableName : config.NotesTable, 32 | Item : { 33 | noteId : createNoteEvent.noteId, 34 | headline : createNoteEvent.headline, 35 | text: createNoteEvent.text 36 | } 37 | }; 38 | 39 | doc.put(note, function(err,savedNote) { 40 | if(err) { 41 | context.fail(new Error('Unable to save note with key: "' + createNoteEvent.noteId + '"')); 42 | } else { 43 | context.succeed({success: true}); 44 | } 45 | }); 46 | } 47 | --------------------------------------------------------------------------------