├── .devcontainer ├── Dockerfile └── devcontainer.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LISENCE ├── README.md ├── config.toml ├── content ├── 00_prerequisites │ ├── 00_aws_account │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── 20_install_and_configs │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── _index.en.md │ └── _index.ja.md ├── 100_supplemental_resource │ ├── 10_cloud9 │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── 30_IAM_policy │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── _index.en.md │ └── _index.ja.md ├── 10_getting_started │ ├── 00_what_you_build │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── _index.en.md │ └── _index.ja.md ├── 30_mock │ ├── 00_bootstrap │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── 10_auth │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── 20_post_back_end │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── 25_post_back_end_key │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── 30_post_front_end │ │ ├── _index.en.files │ │ │ ├── AllPosts.js │ │ │ ├── App.js │ │ │ ├── PostList.js │ │ │ ├── PostsBySpecifiedUser.js │ │ │ └── Sidebar.js │ │ ├── _index.en.md │ │ ├── _index.ja.files │ │ │ ├── AllPosts.js │ │ │ ├── App.js │ │ │ ├── PostList.js │ │ │ ├── PostsBySpecifiedUser.js │ │ │ └── Sidebar.js │ │ └── _index.ja.md │ ├── _index.en.md │ └── _index.ja.md ├── 40_hosting │ ├── 00_manual_deployment │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── _index.en.md │ └── _index.ja.md ├── 50_follow_timeline │ ├── 00_follow_back_end │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── 10_follow_front_end │ │ ├── _index.en.files │ │ │ └── PostsBySpecifiedUser.js │ │ ├── _index.en.md │ │ ├── _index.ja.files │ │ │ └── PostsBySpecifiedUser.js │ │ └── _index.ja.md │ ├── 20_timeline_back_end │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── 30_function_directive │ │ ├── _index.en.files │ │ │ ├── index.js │ │ │ └── schema.graphql │ │ ├── _index.en.md │ │ ├── _index.ja.files │ │ │ ├── index.js │ │ │ └── schema.graphql │ │ └── _index.ja.md │ ├── 50_timeline_front_end │ │ ├── _index.en.files │ │ │ ├── App.js │ │ │ ├── Sidebar.js │ │ │ └── Timeline.js │ │ ├── _index.en.md │ │ ├── _index.ja.files │ │ │ ├── App.js │ │ │ ├── Sidebar.js │ │ │ └── Timeline.js │ │ └── _index.ja.md │ ├── _index.en.md │ └── _index.ja.md ├── 60_full_text_search │ ├── 00_searchable_back_end │ │ ├── _index.en.files │ │ │ └── schema.graphql │ │ ├── _index.en.md │ │ ├── _index.ja.files │ │ │ └── schema.graphql │ │ └── _index.ja.md │ ├── 10_searchable_front_end │ │ ├── _index.en.files │ │ │ ├── App.js │ │ │ ├── Search.js │ │ │ └── Sidebar.js │ │ ├── _index.en.md │ │ ├── _index.ja.files │ │ │ ├── App.js │ │ │ ├── Search.js │ │ │ └── Sidebar.js │ │ └── _index.ja.md │ ├── _index.en.md │ └── _index.ja.md ├── 70_multi_env │ ├── 00_add_env │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── 10_amplify_console │ │ ├── connect_github.en.md │ │ ├── connect_github.ja.md │ │ ├── create_cicd.en.md │ │ └── create_cicd.ja.md │ ├── 20_branch_autodetection │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── 30_preview_autodetection │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── _index.en.md │ └── _index.ja.md ├── 80_e2e_test │ ├── 00_ cypress │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── _index.en.md │ └── _index.ja.md ├── 90_summary │ ├── 10_clean_up │ │ ├── _index.en.md │ │ └── _index.ja.md │ ├── _index.en.md │ └── _index.ja.md ├── _index.en.md ├── _index.ja.md └── final_app_image.png ├── layouts ├── index.json └── partials │ └── custom-footer.html ├── static ├── images │ ├── 00_prequisites │ │ ├── add_authentication.png │ │ ├── add_hosting.png │ │ ├── addingtags.jpg │ │ ├── amplify-configure-new-iam.png │ │ ├── amplify_publish.png │ │ ├── attachingpolicy.jpg │ │ ├── cloud9-tmp-credential-off.png │ │ ├── confirmationcode.jpg │ │ ├── createnewaccount.jpg │ │ ├── creatingiamuser.jpg │ │ ├── creatinguserconfirmation.jpg │ │ ├── iam-1-create-user.png │ │ ├── iam-2-attach-policy.png │ │ ├── iam-3-create-user.png │ │ ├── iam-4-save-url.png │ │ ├── inputcredentials.jpg │ │ ├── management-console-cloud9-1.png │ │ ├── management-console-cloud9-2.png │ │ ├── management-console-cloud9-3.png │ │ ├── management-console-cloud9-4.png │ │ ├── management-console-cloud9-5.png │ │ ├── management-console-cloud9-6.png │ │ ├── open_new_window.png │ │ ├── preview_runnning_application.png │ │ ├── signin.jpg │ │ ├── signinsignout.mov.gif │ │ ├── signupform.jpg │ │ ├── terminal.png │ │ ├── usercreated.jpg │ │ └── vue-router.gif │ ├── 07_multi_env │ │ ├── .png │ │ ├── Screen Shot 2020-05-15 at 16.32.06.png │ │ ├── access_control.png │ │ ├── add_ssh_key.png │ │ ├── amplify_console.png │ │ ├── branch_name_prefix_settings.png │ │ ├── build_settings.png │ │ ├── compare.png │ │ ├── complete_deploy.png │ │ ├── confirm.png │ │ ├── connect_develop-branch.png │ │ ├── connect_github.png │ │ ├── copy_ssh_git_url.png │ │ ├── create_build_settings.png │ │ ├── create_new_repository.png │ │ ├── design_env.png │ │ ├── e2e.png │ │ ├── edit_app.png │ │ ├── enable_basic_auth.png │ │ ├── enable_preview.png │ │ ├── general_settings.png │ │ ├── install_preview.png │ │ ├── install_preview_dialog.png │ │ ├── master_env.png │ │ ├── multi_env.png │ │ ├── new_repository.png │ │ ├── open_pull_request.png │ │ ├── preview_settings.png │ │ ├── previews_detail.png │ │ ├── previews_pr_list.png │ │ ├── production_user_not_exist.png │ │ ├── pull_request_amplify_link.png │ │ ├── re_deploy.png │ │ ├── rebuild.png │ │ ├── select_branch_env.png │ │ ├── select_branch_env.png_design.png │ │ ├── select_branch_env_design.png │ │ ├── select_preview_branch.png │ │ ├── setting_basic_auth_pass.png │ │ ├── share_multi_backend.png │ │ ├── staging_confirm.png │ │ ├── staging_confirm_design.png │ │ ├── staging_env.png │ │ ├── start-autodetect-branch.png │ │ ├── start_build.png │ │ ├── success_to_create_design.png │ │ ├── success_to_create_staging.png │ │ ├── success_to_deploy_preview.png │ │ ├── success_to_push.png │ │ └── sync_github_account.png │ ├── 10_getting_started │ │ ├── c9_new_terminal.png │ │ ├── final_architecture.png │ │ ├── pop_browser_new_window.png │ │ └── preview_running_application.png │ ├── 30_mock │ │ ├── .png │ │ ├── architecture.png │ │ ├── architecture_api.png │ │ ├── architecture_auth.png │ │ ├── auth.png │ │ ├── confirm.png │ │ ├── getPost.png │ │ ├── graphql_1.png │ │ ├── graphql_2.png │ │ ├── graphql_change_auth.png │ │ ├── key.png │ │ ├── listPosts.png │ │ └── npm_start.png │ ├── 40_hosting │ │ └── architecture_hosting.png │ ├── 50_follow_timeline │ │ ├── architecture.png │ │ ├── architecture_follow.png │ │ ├── architecture_lambda.png │ │ ├── architecture_timeline.png │ │ ├── createPostAndTimeline.png │ │ ├── createPostAndTimeline_error.png │ │ ├── createTimeline.png │ │ ├── follow_confirm.png │ │ ├── follow_mutation.png │ │ ├── listFollower.png │ │ ├── listTimelines.png │ │ └── listTimelines_2.png │ ├── 60_full_text_search │ │ ├── architecture.png │ │ ├── front.png │ │ └── searchPosts.png │ ├── 80_e2e │ │ ├── amplifyyml_dl.png │ │ ├── build_settings.png │ │ ├── failed_to_authenticator_test.png │ │ ├── play_test_video.png │ │ ├── success_to_authenticator_test.png │ │ ├── success_to_e2e.png │ │ └── test_flow.png │ ├── 90_summary │ │ └── delete_cloud9.png │ ├── Peccy.png │ ├── apn-logo.jpg │ └── aws-open-source.jpg └── js │ └── kinesis.js └── themes └── learn ├── .editorconfig ├── .gitignore ├── .grenrc.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── archetypes ├── chapter.md └── default.md ├── i18n ├── ar.toml ├── de.toml ├── en.toml ├── es.toml ├── fr.toml ├── hi.toml ├── id.toml ├── nl.toml ├── pt.toml └── tr.toml ├── images ├── screenshot.png └── tn.png ├── layouts ├── 404.html ├── _default │ ├── list.html │ └── single.html ├── index.html ├── index.json ├── partials │ ├── custom-comments.html │ ├── custom-footer.html │ ├── custom-header.html │ ├── favicon.html │ ├── footer.html │ ├── header.html │ ├── logo.html │ ├── menu-footer.html │ ├── menu.html │ ├── meta.html │ ├── search.html │ ├── tags.html │ └── toc.html └── shortcodes │ ├── attachments.html │ ├── button.html │ ├── children.html │ ├── expand.html │ ├── img.html │ ├── mermaid.html │ ├── notice.html │ ├── ref.html │ ├── relref.html │ ├── siteparam.html │ └── siteurl.html ├── static ├── css │ ├── atom-one-dark-reasonable.css │ ├── auto-complete.css │ ├── featherlight.min.css │ ├── fontawesome-all.min.css │ ├── hugo-theme.css │ ├── hybrid.css │ ├── nucleus.css │ ├── perfect-scrollbar.min.css │ ├── tags.css │ ├── theme-aws.css │ ├── theme-blue.css │ ├── theme-green.css │ ├── theme-red.css │ └── theme.css ├── fonts │ ├── Inconsolata.eot │ ├── Inconsolata.svg │ ├── Inconsolata.ttf │ ├── Inconsolata.woff │ ├── Novecentosanswide-Normal-webfont.eot │ ├── Novecentosanswide-Normal-webfont.svg │ ├── Novecentosanswide-Normal-webfont.ttf │ ├── Novecentosanswide-Normal-webfont.woff │ ├── Novecentosanswide-Normal-webfont.woff2 │ ├── Novecentosanswide-UltraLight-webfont.eot │ ├── Novecentosanswide-UltraLight-webfont.svg │ ├── Novecentosanswide-UltraLight-webfont.ttf │ ├── Novecentosanswide-UltraLight-webfont.woff │ ├── Novecentosanswide-UltraLight-webfont.woff2 │ ├── Work_Sans_200.eot │ ├── Work_Sans_200.svg │ ├── Work_Sans_200.ttf │ ├── Work_Sans_200.woff │ ├── Work_Sans_200.woff2 │ ├── Work_Sans_300.eot │ ├── Work_Sans_300.svg │ ├── Work_Sans_300.ttf │ ├── Work_Sans_300.woff │ ├── Work_Sans_300.woff2 │ ├── Work_Sans_500.eot │ ├── Work_Sans_500.svg │ ├── Work_Sans_500.ttf │ ├── Work_Sans_500.woff │ └── Work_Sans_500.woff2 ├── images │ ├── clippy.svg │ └── logo.png ├── js │ ├── auto-complete.js │ ├── clipboard.min.js │ ├── featherlight.min.js │ ├── highlight.pack.js │ ├── html5shiv-printshiv.min.js │ ├── hugo-learn.js │ ├── jquery-3.3.1.min.js │ ├── jquery.sticky.js │ ├── learn.js │ ├── lunr.min.js │ ├── modernizr.custom-3.6.0.js │ ├── perfect-scrollbar.jquery.min.js │ ├── perfect-scrollbar.min.js │ └── search.js └── mermaid │ ├── mermaid.css │ ├── mermaid.dark.css │ ├── mermaid.forest.css │ └── mermaid.js └── theme.toml /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Update the NODE_VERSION arg in docker-compose.yml to pick a Node version: 10, 12, 14 2 | ARG NODE_VERSION=14 3 | FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:${NODE_VERSION} 4 | 5 | # VARIANT can be either 'hugo' for the standard version or 'hugo_extended' for the extended version. 6 | ARG VARIANT=hugo 7 | # VERSION can be either 'latest' or a specific version number 8 | ARG VERSION=0.87.0 9 | 10 | # Download Hugo 11 | RUN apt-get update && apt-get install -y ca-certificates openssl git curl && \ 12 | rm -rf /var/lib/apt/lists/* && \ 13 | case ${VERSION} in \ 14 | latest) \ 15 | export VERSION=$(curl -s https://api.github.com/repos/gohugoio/hugo/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') ;;\ 16 | esac && \ 17 | echo ${VERSION} && \ 18 | case $(uname -m) in \ 19 | aarch64) \ 20 | export ARCH=ARM64 ;; \ 21 | *) \ 22 | export ARCH=64bit ;; \ 23 | esac && \ 24 | echo ${ARCH} && \ 25 | wget -O ${VERSION}.tar.gz https://github.com/gohugoio/hugo/releases/download/v${VERSION}/${VARIANT}_${VERSION}_Linux-${ARCH}.tar.gz && \ 26 | tar xf ${VERSION}.tar.gz && \ 27 | mv hugo /usr/bin/hugo 28 | 29 | # Hugo dev server port 30 | EXPOSE 1313 -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Hugo (Community)", 3 | "build": { 4 | "dockerfile": "Dockerfile", 5 | "args": { 6 | // Settings are based on https://github.com/microsoft/vscode-dev-containers/tree/main/containers/hugo. 7 | // Update VARIANT to pick hugo variant. 8 | // Example variants: hugo, hugo_extended 9 | // Rebuild the container if it already exists to update. 10 | "VARIANT": "hugo", 11 | // Update VERSION to pick a specific hugo version. 12 | // Example versions: latest, 0.73.0, 0,71.1 13 | // Rebuild the container if it already exists to update. 14 | // v0.87.0 is used in Amplify Social Network Workshop CI/CD pipeline. 15 | "VERSION": "0.87.0", 16 | // Update NODE_VERSION to pick the Node.js version: 12, 14 17 | "NODE_VERSION": "14", 18 | } 19 | }, 20 | 21 | // Mount .gitconfig to push and pull changes from devcontainer 22 | "mounts": [ 23 | "source=${localEnv:HOME}/.gitconfig,target=/home/node/.gitconfig,type=bind,consistency=cached", 24 | "source=${localEnv:HOME}/.ssh,target=/home/node/.ssh,type=bind,consistency=cached" 25 | ], 26 | 27 | // Set *default* container specific settings.json values on container create. 28 | "settings": { 29 | "html.format.templating": true, 30 | }, 31 | 32 | // Add the IDs of extensions you want installed when the container is created. 33 | "extensions": [ 34 | "bungcip.better-toml", 35 | "davidanson.vscode-markdownlint" 36 | ], 37 | 38 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 39 | "forwardPorts": [ 40 | 1313 41 | ], 42 | 43 | // Use 'postCreateCommand' to run commands after the container is created. 44 | // "postCreateCommand": "uname -a", 45 | 46 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 47 | "remoteUser": "node" 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. -------------------------------------------------------------------------------- /LISENCE: -------------------------------------------------------------------------------- 1 | MIT No Attribution 2 | 3 | Copyright 2021 Amazon.com, Inc. or its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amplify Social Network App (SNS) Workshop 2 | This repository is soucrce code for [Amplify Social Network App (SNS) Workshop](https://amplify-sns.workshop.aws/) 3 | In this workshop, you will learn about AWS Amplify in hands-on way through the development of twitter-like social media applications. 4 | 5 | ## CONTRIBUTION 6 | See [CONTRIBUTING](CONTRIBUTING.md) for more information. 7 | 8 | ## Security 9 | 10 | See [Security issue notifications](CONTRIBUTING.md#security-issue-notifications) for more information. 11 | 12 | ## License 13 | 14 | This library is licensed under the MIT-0 License. See the [LICENSE](LISENCE) file. -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | # baseURL 2 | baseURL = "" 3 | languageCode = "en-EN" 4 | defaultContentLanguage = "en" 5 | 6 | # title 7 | title = "title" 8 | theme = "learn" 9 | metaDataFormat = "yaml" 10 | defaultContentLanguageInSubdir= true 11 | 12 | uglyurls = true 13 | sectionPagesMenu = "main" 14 | pygmentsCodeFences = true 15 | pygmentsStyle = "monokai" 16 | 17 | [params] 18 | # editURL 19 | # editURL = "editURL" 20 | # description 21 | description = "" 22 | # author 23 | author = "author" 24 | disableBreadcrumb = false 25 | disableNextPrev = false 26 | themeVariant = "aws" 27 | disableSearch = false 28 | disableAssetsBusting = true 29 | disableLanguageSwitchingButton = false 30 | disableShortcutsTitle = true 31 | disableInlineCopyToClipBoard = true 32 | ordersectionsby = "weight" 33 | 34 | [[menu.shortcuts]] 35 | name = " Souce code (Issues/PRs)" 36 | identifier = "fb" 37 | url = "https://github.com/aws-samples/aws-amplify-social-network-app-workshop" 38 | weight = 0 39 | 40 | [outputs] 41 | home = [ "HTML", "RSS", "JSON"] 42 | 43 | [blackfriday] 44 | plainIDAnchors = true 45 | hrefTargetBlank = true 46 | 47 | [Languages] 48 | [Languages.ja] 49 | title = "Amplify SNS Workshop" 50 | weight = 10 51 | languageName = "日本語" 52 | 53 | [Languages.en] 54 | title = "Amplify Social Network App Workshop" 55 | weight = 1 56 | languageName = "English" 57 | -------------------------------------------------------------------------------- /content/00_prerequisites/00_aws_account/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | 3 | title = "Create AWS Account" 4 | date = 2020-03-18T10:09:42+09:00 5 | 6 | weight = 1 7 | 8 | pre = "1.1. " 9 | 10 | +++ 11 | 12 | 13 | 14 | {{% notice warning%}} 15 | An AWS account is required for this workshop. 16 | If you have not created one, please refer to the following content to create an account. 17 | [How do I create and activate a new Amazon Web Services account?](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/?nc1=h_ls) 18 | {{% /notice%}} -------------------------------------------------------------------------------- /content/00_prerequisites/00_aws_account/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "AWSアカウントの作成" 3 | date = 2020-03-18T10:09:42+09:00 4 | weight = 1 5 | pre = "1.1. " 6 | +++ 7 | 8 | {{% notice warning%}} 9 | 本ワークショップにはAWSのアカウントが必要になります。 10 | 作成されていない方は以下を参考にアカウントを作成してください。 11 | [AWS Hands-on for Beginners ハンズオンはじめの一歩: AWS アカウントの作り方 & IAM 基本のキ](https://pages.awscloud.com/event_JAPAN_Ondemand_Hands-on-for-Beginners-1st-Step_LP.html) 12 | {{% /notice %}} 13 | -------------------------------------------------------------------------------- /content/00_prerequisites/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Prerequisites" 3 | date = 2020-03-18T10:09:41+09:00 4 | weight = 1 5 | chapter = true 6 | pre = "1. " 7 | +++ 8 | 9 | In this chapter, you prepare the necessary environment for this workshop. 10 | 11 | {{% children showhidden="false"%}} {{% /children%}} -------------------------------------------------------------------------------- /content/00_prerequisites/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "前提条件" 3 | date = 2020-03-18T10:09:41+09:00 4 | weight = 1 5 | chapter = true 6 | pre = "1. " 7 | +++ 8 | 9 | 本章ではワークショップの実施にあたって必要な準備を行います。 10 | 11 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/100_supplemental_resource/10_cloud9/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title="Set up Cloud9" 3 | date = 2020-03-18T20:09:42+09:00 4 | weight = 2 5 | pre = "10.1. " 6 | +++ 7 | 8 | {{% notice info%}} 9 | We recommend you to run this workshop on **local environment**. 10 | If you have difficulty setting up `Node.js` or `npm` in your local environment, you can use [AWS Cloud9](https://aws.amazon.com/jp/cloud9/); the cloud-based integrated development environment (IDE) provided by AWS . 11 | For more information, see [10.1. Set up Cloud9](/100_supplemental_resource/10_cloud9.html). 12 | However, for Amplify Mocking, port 20002 must be available and may not be available in some Cloud IDEs. 13 | Please understand in advance. 14 | {{% /notice%}} 15 | 16 | ### Setting Up Cloud9 17 | 18 | [AWS Cloud9](https://aws.amazon.com/cloud9/?nc1=h_ls) is a cloud-based integrated development environment (IDE) 19 | Log in to the AWS Management Console and launch an instance of Cloud9 that you use to develop in this workshop. 20 | 21 | 1. Choose Cloud9 from the AWS Management Console. 22 | 2. Click on the "Create environment" button. 23 | ![create iam user](/images/00_prequisites/management-console-cloud9-2.png) 24 | 1. In Name, enter a name for the environment. (e.g. amplify-handson-{date}, etc.)
"Description" is optional. Please enter a descriptive text that is easy to understand. 25 | ![create iam user](/images/00_prequisites/management-console-cloud9-3.png) 26 | 4. For Instance Type, choose m5.large. 27 | If you use other size, it may run out of memory during build. 28 | Leave the default for the other items and click "Next step". 29 | ![create iam user](/images/00_prequisites/management-console-cloud9-4.png) 30 | 5. Confirm the settings and click "Create Environment". 31 | ![create iam user](/images/00_prequisites/management-console-cloud9-5.png) 32 | 6. Wait a while and if the following screen appears, it is successful. 33 | ![create iam user](/images/00_prequisites/management-console-cloud9-6.png) 34 | 35 | ### Resize EBS 36 | 37 | [Elastic Block Store(EBS)](https://aws.amazon.com/jp/ebs) is attatched to the EC2 instance used by Cloud9. 38 | `no space left on device, write` error may be caused by insufficient storage space. 39 | We recommend you to increase volume size of EBS. 40 | 41 | ```shell 42 | INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) 43 | 44 | VOLUME_ID=$(aws ec2 describe-volumes \ 45 | --query "Volumes[?Attachments[?InstanceId=='$INSTANCE_ID']].{ID:VolumeId}" \ 46 | --output text) 47 | 48 | aws ec2 modify-volume --volume-id $VOLUME_ID --size 32 49 | ``` 50 | 51 | {{% notice info%}} 52 | `INSTANCE_ID...` calls [EC2 instance metadata](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) endpoint to fetch Instance ID of EC2 instance use by Cloud9. 53 | `VOLUME_ID...` fetches EBS volume ID using EC2 instance ID. 54 | `aws ec2...` increases volume size to 32 GiB 55 | {{% /notice %}} 56 | 57 | Confirm Cloud9 uses newly added storage. 58 | ``` 59 | sudo growpart /dev/nvme0n1 1 60 | 61 | sudo xfs_growfs -d / 62 | ``` 63 | 64 | ### Disable AWS Managed Temporary Credentials 65 | 66 | AWS Cloud9 creates managed temporary AWS credentials the first time you open the console. 67 | 68 | In the following steps, you disable the AWS Cloud9 temporary credentials in order to use another credential at after steps. 69 | 70 | AWS Cloud9 > Preferences > AWS SETTINGS > Toggle "AWS managed temporary credentials:" Off to the default. 71 | ![create iam user](/images/00_prequisites/cloud9-tmp-credential-off.png) -------------------------------------------------------------------------------- /content/100_supplemental_resource/10_cloud9/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Cloud9のセットアップ" 3 | date = 2020-03-18T20:09:42+09:00 4 | weight = 2 5 | pre = "10.1. " 6 | +++ 7 | 8 | {{% notice info%}} 9 | 本ワークショップは**ローカル環境での実施を推奨**します。 10 | ローカル環境での`node.js`や`npm`のセットアップが困難な方は、AWS が提供するクラウドベースの統合開発環境 (IDE)である[AWS Cloud9](https://aws.amazon.com/jp/cloud9/)をお使いください。 11 | ただし、Amplify Mocking(手元での動作確認)には20002番ポートが利用可能である必要があり、一部の Cloud IDE では利用できないことがあります。 12 | あらかじめご了承ください。 13 | {{% /notice %}} 14 | 15 | 16 | --- 17 | 18 | 19 | ### Cloud9 のセットアップ 20 | 21 | Amplify ワークショップではクラウドベースの統合開発環境 (IDE)である[AWS Cloud9](https://aws.amazon.com/jp/cloud9/)上で開発を行います。AWS マネジメントコンソールにログインし、今回開発に使用する Cloud9 のインスタンスを起動します。 22 | 23 | 1.マネジメントコンソールから Cloud9 を選択 ![create iam user](/images/00_prequisites/management-console-cloud9-1.png) 24 |

2.「Create environment」ボタンを押下 ![create iam user](/images/00_prequisites/management-console-cloud9-2.png) 25 |

26 | 27 | 1. 「Name」に環境名を入力します。(例 amplify-handson-<日付> 等)
「Description」は任意項目です。わかりやすい説明文を入力してください。 ![create iam user](/images/00_prequisites/management-console-cloud9-3.png) 28 |

4.「Instance Type」に m5.large を選択します。小さいサイズを選択するとビルド時にメモリが不足する可能性があります。他の項目はデフォルトのままで「Next step」を押下します。 29 | ![create iam user](/images/00_prequisites/management-console-cloud9-4.png) 30 |

5.設定内容を確認し、「Create Environment」を押下します。 31 | ![create iam user](/images/00_prequisites/management-console-cloud9-5.png) 32 |

6.しばらく待って以下のような画面が表示されれば成功です。 33 | 34 | ![create iam user](/images/00_prequisites/management-console-cloud9-6.png) 35 | 36 |

37 | 38 | ### EBS(ストレージ)のリサイズ 39 | Cloud9はEC2インスタンス上に環境を作成します。 40 | そのEC2インスタンスには[Elastic Block Store(EBS)](https://aws.amazon.com/jp/ebs)がアタッチされています。 41 | デフォルトで今回立ち上げたCloud9の作業環境に割り当てられたEBSでは`no space left on device, write`エラーが発生する場合があります。 42 | そこで、次のスクリプトを実行してEBSボリュームのサイズを引き上げます。 43 | 44 | ```shell 45 | INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) 46 | 47 | VOLUME_ID=$(aws ec2 describe-volumes \ 48 | --query "Volumes[?Attachments[?InstanceId=='$INSTANCE_ID']].{ID:VolumeId}" \ 49 | --output text) 50 | 51 | aws ec2 modify-volume --volume-id $VOLUME_ID --size 32 52 | ``` 53 | 54 | {{% notice info%}} 55 | 最初のコマンド(`INSTANCE_ID...`)では、[EC2 instance metadata](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) のエンドポイントをコールして、Cloud9が使用するEC2インスタンスのインスタンスIDを取得しています。 56 | 次のコマンド(`VOLUME_ID...`)では、AWS CLIを用いてEBSボリュームのIDを取得しています。 57 | 最後のコマンド(`aws ec2...`)では、EBSボリュームのサイズを32GiBに引き上げています。 58 | {{% /notice %}} 59 | 60 | 最後に、割り当てたストレージ領域をCloud9が利用していることを確認しましょう。 61 | ``` 62 | sudo growpart /dev/nvme0n1 1 63 | 64 | sudo xfs_growfs -d / 65 | ``` 66 | 67 | ### AWS 管理の一時認証情報 の無効化 68 | 69 | Cloud9 では最初にコンソールを開いたときに AWS 管理の一時認証情報が作成されます。
70 | 以降の手順では、Amplify CLI で発行するユーザの権限を使用するため、Cloud9 の一時認証情報を無効化します。 71 | 72 | AWS Cloud9 > Preferences > AWS SETTINGS > 「AWS managed temporary credentials:」 のトグルを Off にします。 ![create iam user](/images/00_prequisites/cloud9-tmp-credential-off.png) 73 | -------------------------------------------------------------------------------- /content/100_supplemental_resource/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Supplemental Resources" 3 | date = 2020-03-18T20:09:42+09:00 4 | weight = 100 5 | pre = "10. " 6 | +++ 7 | 8 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/100_supplemental_resource/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "補足" 3 | date = 2020-03-18T20:09:42+09:00 4 | weight = 100 5 | pre = "10. " 6 | +++ 7 | 8 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/10_getting_started/00_what_you_build/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "The goal of this workshop" 3 | date = 2020-03-18T10:09:44+09:00 4 | weight = 1 5 | pre = "2.1. " 6 | +++ 7 | 8 | ### Final Architecture 9 | ![](/images/10_getting_started/final_architecture.png) 10 | 11 | 12 | ### Tools Used 13 | * [AWS Amplify CLI](https://github.com/aws-amplify/amplify-cli): quickly provision backend resources on AWS. 14 | 15 | * [AWS Amplify JavaScript library] (https://aws-amplify.github.io/): front-end React application invokes backend resources. 16 | 17 | * [Amazon Cognito](https://aws.amazon.com/cognito/): authenticate users. 18 | 19 | * [Amazon DynamoDB](https://aws.amazon.com/dynamodb/): store the posts, follow relationships and timelines. 20 | 21 | * [AWS AppSync](https://aws.amazon.com/appsync/): host a managed GraphQL API. 22 | 23 | * [AWS Lambda](https://aws.amazon.com/lambda/): implement complicated business logics on server-side. 24 | 25 | * [Amazon Elasticsearch Service](https://aws.amazon.com/elasticsearch-service): implement full-text search. -------------------------------------------------------------------------------- /content/10_getting_started/00_what_you_build/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "本ワークショップのゴール" 3 | date = 2020-03-18T10:09:44+09:00 4 | weight = 1 5 | pre = "2.1. " 6 | +++ 7 | 8 | ### 最終的な構成 9 | ![](/images/10_getting_started/final_architecture.png) 10 | 11 | ### 使用ツール 12 | * [AWS Amplify CLI](https://github.com/aws-amplify/amplify-cli)を利用して、バックエンドのAWS上のリソースを高速にプロビジョニングします。 13 | 14 | * [AWS Amplify JavaScript library](https://aws-amplify.github.io/)を利用して、フロントエンドのReactアプリケーションからバックエンドのリソースを呼び出します。 15 | 16 | * [Amazon Cognito](https://aws.amazon.com/cognito/)を利用して、サービスを利用するユーザーの認証を行います。 17 | 18 | * [Amazon DynamoDB](https://aws.amazon.com/dynamodb/)を利用して、投稿やフォロー関係、タイムラインなどを保存します。 19 | 20 | * [AWS AppSync](https://aws.amazon.com/appsync/)を利用して、マネージドにGraphQL APIをホストします。 21 | 22 | * [AWS Lambda](https://aws.amazon.com/lambda/)を利用して、サーバーサイドで複雑なビジネスロジックを実装します。 23 | 24 | * [Amazon Elasticsearch Service](https://aws.amazon.com/elasticsearch-service)を利用して、全文検索機能を実装します。 -------------------------------------------------------------------------------- /content/10_getting_started/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Introduction" 3 | date = 2020-03-18T10:09:44+09:00 4 | weight = 2 5 | chapter = true 6 | pre = "2. " 7 | +++ 8 | 9 | 10 | {{% children showhidden="false"%}} {{% /children%}} -------------------------------------------------------------------------------- /content/10_getting_started/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "はじめに" 3 | date = 2020-03-18T10:09:44+09:00 4 | weight = 2 5 | chapter = true 6 | pre = "2. " 7 | +++ 8 | 9 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/30_mock/00_bootstrap/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Bootstrap" 3 | date = 2020-03-18T10:09:45+09:00 4 | weight = 1 5 | pre = "3.1. " 6 | +++ 7 | 8 | ### 作業ディレクトリの作成 9 | 任意のディレクトリに移動していただいた後、次のコマンドを実行し、本ワークショップの作業を行うディレクトリを作成します。 10 | 11 | ``` 12 | mkdir amplify-sns-workshop 13 | cd amplify-sns-workshop 14 | ``` 15 | 16 | {{% notice info %}} 17 | 後ほど見返した時に何のワークショップで作成したかわかりやすいようにしています 18 | {{% /notice %}} 19 | 20 | ### Creating a React app 21 | Reactのcreate-react-appコマンドを利用して、Reactウェブアプリケーションの雛形を作成していきます。 22 | 23 | {{% notice tip %}} 24 | create-react-appコマンドについての詳細は次のリンクご参照ください。[https://github.com/facebook/create-react-app](https://github.com/facebook/create-react-app). 25 | {{% /notice %}} 26 | 27 | ```bash 28 | npx create-react-app boyaki 29 | cd boyaki 30 | ``` 31 | 32 | ### Amplifyの初期化 33 | 先ほど`$ cd boyaki`で移動したReactのプロジェクトルートディレクトリ(`boyaki`)のトップで `amplify init` コマンドを実行します。 34 | 35 | ```bash 36 | amplify init 37 | ``` 38 | 39 | 途中いくつか質問をされます。以下の選択肢に注意して他はデフォルトでOKです。 40 | 41 | - environment name : `production` 42 | - Choose your default editor: `Vim (via Terminal, Mac OS only)` (お好きなエディタを選択してください) 43 | - Please choose the profile you want to use `amplify-handson` (1.3.でAmplify用に作成されたProfileを選択してください) 44 | 45 | 以下は全質問への解答例です。 46 | 47 | - Enter a name for the project `boyaki` 48 | - Enter a name for the environment `production` 49 | - Choose your default editor: `Vim (via Terminal, Mac OS only)` 50 | - Choose the type of app that you're building `javascript` 51 | - What javascript framework are you using `react` 52 | - Source Directory Path: `src` 53 | - Distribution Directory Path: `build` 54 | - Build Command: `npm run-script build` 55 | - Start Command: `npm run-script start` 56 | - Do you want to use an AWS profile? `Yes` 57 | - Please choose the profile you want to use `amplify-handson` 58 | 59 | 60 | {{% notice info %}} 61 | 事前準備で `amplify configure` を正しく完了していない場合、途中で `? Setup new user` と出て新しいユーザーをセットアップするか聞かれます。 62 | その場合、`Y` と答えると、ブラウザで AWS マネジメントコンソールが立ち上がるので、マネジメントコンソールにログインし、その後のガイドに従ってください。 63 | {{% /notice %}} 64 | 65 | {{% notice tip %}} 66 | Amplify CLIではYes/Noで回答する質問の際に`Y/n`や`y/N`と表示されます。 67 | こうした質問では`yes`や`no`に加え、`y`や`n`で回答することができます。 68 | また、`Y/n`であればYes、`y/N`であればNoがデフォルトの回答になりますので、デフォルトの回答でよければそのままEnterキーを押していただくこともできます。 69 | {{% /notice %}} 70 | 71 | Profile の選択が終わると、バックエンドの初期化を開始します。AWS CloudFormation によって、アプリケーションのバックエンドに必要な基礎的な AWS リソース(IAM Role、Amazon S3 バケットなど)が自動的に作成されます。 72 | 73 | ### 環境のテスト 74 | コードの変更反映をリアルタイムで確認できるよう、development serverを立ち上げてブラウザで確認してみましょう。 75 | 76 | ```bash 77 | npm start 78 | ``` 79 | 80 | development serverが立ち上がると、自動的にブラウザで`http://localhost:3000`が開かれます。 81 | 自動的に開かない場合はブラウザの検索窓に`http://localhost:3000`と入力してからEnterを押し、アクセスしましょう。 82 | 83 | ![](/images/30_mock/npm_start.png) 84 | 85 | 確認ができましたら、`$ npm start`を実行中のターミナルのタブはそのままに、新しくタブを開いて作業を進めていきます。 86 | 87 | {{% notice info%}} 88 | 以下はCloud9を利用している方の手順になります。Cloud9を使用していない方は次へお進みください。 89 | なお、繰り返しになりますが本ワークショップではローカル環境での実施を推奨しております。 90 | {{% /notice %}} 91 | 92 | Cloud9のPreviewメニューをクリックし、Preview Running Applicationを選択しましょう。 93 | 94 | ![preview running application](/images/10_getting_started/preview_running_application.png) 95 | 96 | お好みで、新しいタブでPreviewを表示していただくことができます。 97 | 98 | ![pop app to new window](/images/10_getting_started/pop_browser_new_window.png) 99 | 100 | 最後に、追加でターミナルのタブを開きましょう。元のターミナルはそのままにしておき、development serverを立ち上げたままにします。このとき、`cd boyaki`を実行しておきます。 101 | 102 | ![new terminal](/images/10_getting_started/c9_new_terminal.png) 103 | 104 | -------------------------------------------------------------------------------- /content/30_mock/30_post_front_end/_index.en.files/AllPosts.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useReducer } from 'react'; 2 | 3 | import {API, graphqlOperation } from 'aws-amplify'; 4 | 5 | import { listPostsSortedByTimestamp } from '../graphql/queries'; 6 | import { onCreatePost } from '../graphql/subscriptions'; 7 | 8 | import PostList from '../components/PostList'; 9 | import Sidebar from './Sidebar'; 10 | 11 | const SUBSCRIPTION = 'SUBSCRIPTION'; 12 | const INITIAL_QUERY = 'INITIAL_QUERY'; 13 | const ADDITIONAL_QUERY = 'ADDITIONAL_QUERY'; 14 | 15 | const reducer = (state, action) => { 16 | switch (action.type) { 17 | case INITIAL_QUERY: 18 | return action.posts; 19 | case ADDITIONAL_QUERY: 20 | return [...state, ...action.posts] 21 | case SUBSCRIPTION: 22 | return [action.post, ...state] 23 | default: 24 | return state; 25 | } 26 | }; 27 | 28 | export default function AllPosts() { 29 | const [posts, dispatch] = useReducer(reducer, []); 30 | const [nextToken, setNextToken] = useState(null); 31 | const [isLoading, setIsLoading] = useState(true); 32 | 33 | const getPosts = async (type, nextToken = null) => { 34 | const res = await API.graphql(graphqlOperation(listPostsSortedByTimestamp, { 35 | type: "post", 36 | sortDirection: 'DESC', 37 | limit: 20, //default = 10 38 | nextToken: nextToken, 39 | })); 40 | console.log(res); 41 | dispatch({ type: type, posts: res.data.listPostsSortedByTimestamp.items }) 42 | setNextToken(res.data.listPostsSortedByTimestamp.nextToken); 43 | setIsLoading(false); 44 | } 45 | 46 | const getAdditionalPosts = () => { 47 | if (nextToken === null) return; //Reached the last page 48 | getPosts(ADDITIONAL_QUERY, nextToken); 49 | } 50 | 51 | useEffect(() => { 52 | getPosts(INITIAL_QUERY); 53 | 54 | const subscription = API.graphql(graphqlOperation(onCreatePost)).subscribe({ 55 | next: (msg) => { 56 | console.log('allposts subscription fired') 57 | const post = msg.value.data.onCreatePost; 58 | dispatch({ type: SUBSCRIPTION, post: post }); 59 | } 60 | }); 61 | return () => subscription.unsubscribe(); 62 | }, []); 63 | 64 | 65 | return ( 66 | 67 | 70 | 76 | 77 | ) 78 | } -------------------------------------------------------------------------------- /content/30_mock/30_post_front_end/_index.en.files/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Amplify from 'aws-amplify'; 3 | import { AmplifyAuthenticator, AmplifySignUp } from '@aws-amplify/ui-react'; 4 | import { AuthState, onAuthUIStateChange } from '@aws-amplify/ui-components'; 5 | import awsconfig from './aws-exports'; 6 | 7 | import { 8 | HashRouter, 9 | Switch, 10 | Route, 11 | Redirect, 12 | } from 'react-router-dom'; 13 | 14 | import { makeStyles, createMuiTheme, ThemeProvider } from '@material-ui/core/styles'; 15 | import CssBaseline from '@material-ui/core/CssBaseline'; 16 | 17 | import AllPosts from './containers/AllPosts'; 18 | import PostsBySpecifiedUser from './containers/PostsBySpecifiedUser'; 19 | 20 | Amplify.configure(awsconfig); 21 | 22 | const drawerWidth = 240; 23 | 24 | const theme = createMuiTheme({ 25 | palette: { 26 | type: 'dark', 27 | primary: { 28 | main: '#1EA1F2', 29 | contrastText: "#fff", 30 | }, 31 | background: { 32 | default: '#15202B', 33 | paper: '#15202B', 34 | }, 35 | divider: '#37444C', 36 | }, 37 | overrides: { 38 | MuiButton: { 39 | color: 'white', 40 | }, 41 | }, 42 | typography: { 43 | fontFamily: [ 44 | 'Arial', 45 | ].join(','), 46 | }, 47 | status: { 48 | danger: 'orange', 49 | }, 50 | }); 51 | 52 | const useStyles = makeStyles(theme => ({ 53 | root: { 54 | display: 'flex', 55 | height: '100%', 56 | width: 800, 57 | marginLeft: 'auto', 58 | marginRight: 'auto', 59 | }, 60 | appBar: { 61 | marginLeft: drawerWidth, 62 | }, 63 | drawer: { 64 | width: drawerWidth, 65 | flexShrink: 0, 66 | }, 67 | drawerPaper: { 68 | width: drawerWidth, 69 | }, 70 | toolbar: theme.mixins.toolbar, 71 | content: { 72 | flexGrow: 1, 73 | backgroundColor: theme.palette.background.default, 74 | padding: theme.spacing(3), 75 | }, 76 | })); 77 | 78 | const App = () => { 79 | const [authState, setAuthState] = React.useState(); 80 | const [user, setUser] = React.useState(); 81 | 82 | const classes = useStyles(); 83 | 84 | React.useEffect(() => { 85 | return onAuthUIStateChange((nextAuthState, authData) => { 86 | setAuthState(nextAuthState); 87 | setUser(authData) 88 | }); 89 | }, []); 90 | 91 | return authState === AuthState.SignedIn && user ? ( 92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 |
105 | ) : ( 106 | 107 | 115 | 116 | ); 117 | } 118 | 119 | export default App; -------------------------------------------------------------------------------- /content/30_mock/30_post_front_end/_index.en.files/PostsBySpecifiedUser.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useReducer } from 'react'; 2 | 3 | import {API, graphqlOperation } from 'aws-amplify'; 4 | import { useParams } from 'react-router'; 5 | 6 | import { listPostsBySpecificOwner } from '../graphql/queries'; 7 | import { onCreatePost } from '../graphql/subscriptions'; 8 | 9 | import PostList from '../components/PostList'; 10 | import Sidebar from './Sidebar'; 11 | 12 | const SUBSCRIPTION = 'SUBSCRIPTION'; 13 | const INITIAL_QUERY = 'INITIAL_QUERY'; 14 | const ADDITIONAL_QUERY = 'ADDITIONAL_QUERY'; 15 | 16 | const reducer = (state, action) => { 17 | switch (action.type) { 18 | case INITIAL_QUERY: 19 | return action.posts; 20 | case ADDITIONAL_QUERY: 21 | return [...state, ...action.posts] 22 | case SUBSCRIPTION: 23 | return [action.post, ...state] 24 | default: 25 | return state; 26 | } 27 | }; 28 | 29 | export default function PostsBySpecifiedUser() { 30 | const { userId } = useParams(); 31 | 32 | const [posts, dispatch] = useReducer(reducer, []); 33 | const [nextToken, setNextToken] = useState(null); 34 | const [isLoading, setIsLoading] = useState(true); 35 | 36 | const getPosts = async (type, nextToken = null) => { 37 | const res = await API.graphql(graphqlOperation(listPostsBySpecificOwner, { 38 | owner: userId, 39 | sortDirection: 'DESC', 40 | limit: 20, 41 | nextToken: nextToken, 42 | })); 43 | console.log(res); 44 | dispatch({ type: type, posts: res.data.listPostsBySpecificOwner.items }) 45 | setNextToken(res.data.listPostsBySpecificOwner.nextToken); 46 | setIsLoading(false); 47 | } 48 | 49 | const getAdditionalPosts = () => { 50 | if (nextToken === null) return; //Reached the last page 51 | getPosts(ADDITIONAL_QUERY, nextToken); 52 | } 53 | 54 | 55 | useEffect(() => { 56 | getPosts(INITIAL_QUERY); 57 | 58 | const subscription = API.graphql(graphqlOperation(onCreatePost)).subscribe({ 59 | next: (msg) => { 60 | const post = msg.value.data.onCreatePost; 61 | if (post.owner !== userId) return; 62 | dispatch({ type: SUBSCRIPTION, post: post }); 63 | } 64 | }); 65 | return () => subscription.unsubscribe(); 66 | }, []); 67 | 68 | 69 | return ( 70 | 71 | 74 | 80 | 81 | ) 82 | } -------------------------------------------------------------------------------- /content/30_mock/30_post_front_end/_index.ja.files/AllPosts.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useReducer } from 'react'; 2 | 3 | import {API, graphqlOperation } from 'aws-amplify'; 4 | 5 | import { listPostsSortedByTimestamp } from '../graphql/queries'; 6 | import { onCreatePost } from '../graphql/subscriptions'; 7 | 8 | import PostList from '../components/PostList'; 9 | import Sidebar from './Sidebar'; 10 | 11 | const SUBSCRIPTION = 'SUBSCRIPTION'; 12 | const INITIAL_QUERY = 'INITIAL_QUERY'; 13 | const ADDITIONAL_QUERY = 'ADDITIONAL_QUERY'; 14 | 15 | const reducer = (state, action) => { 16 | switch (action.type) { 17 | case INITIAL_QUERY: 18 | return action.posts; 19 | case ADDITIONAL_QUERY: 20 | return [...state, ...action.posts] 21 | case SUBSCRIPTION: 22 | return [action.post, ...state] 23 | default: 24 | return state; 25 | } 26 | }; 27 | 28 | export default function AllPosts() { 29 | const [posts, dispatch] = useReducer(reducer, []); 30 | const [nextToken, setNextToken] = useState(null); 31 | const [isLoading, setIsLoading] = useState(true); 32 | 33 | const getPosts = async (type, nextToken = null) => { 34 | const res = await API.graphql(graphqlOperation(listPostsSortedByTimestamp, { 35 | type: "post", 36 | sortDirection: 'DESC', 37 | limit: 20, //default = 10 38 | nextToken: nextToken, 39 | })); 40 | console.log(res); 41 | dispatch({ type: type, posts: res.data.listPostsSortedByTimestamp.items }) 42 | setNextToken(res.data.listPostsSortedByTimestamp.nextToken); 43 | setIsLoading(false); 44 | } 45 | 46 | const getAdditionalPosts = () => { 47 | if (nextToken === null) return; //Reached the last page 48 | getPosts(ADDITIONAL_QUERY, nextToken); 49 | } 50 | 51 | useEffect(() => { 52 | getPosts(INITIAL_QUERY); 53 | 54 | const subscription = API.graphql(graphqlOperation(onCreatePost)).subscribe({ 55 | next: (msg) => { 56 | console.log('allposts subscription fired') 57 | const post = msg.value.data.onCreatePost; 58 | dispatch({ type: SUBSCRIPTION, post: post }); 59 | } 60 | }); 61 | return () => subscription.unsubscribe(); 62 | }, []); 63 | 64 | 65 | return ( 66 | 67 | 70 | 76 | 77 | ) 78 | } -------------------------------------------------------------------------------- /content/30_mock/30_post_front_end/_index.ja.files/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Amplify from 'aws-amplify'; 3 | import { AmplifyAuthenticator, AmplifySignUp } from '@aws-amplify/ui-react'; 4 | import { AuthState, onAuthUIStateChange } from '@aws-amplify/ui-components'; 5 | import awsconfig from './aws-exports'; 6 | 7 | import { 8 | HashRouter, 9 | Switch, 10 | Route, 11 | Redirect, 12 | } from 'react-router-dom'; 13 | 14 | import { makeStyles, createMuiTheme, ThemeProvider } from '@material-ui/core/styles'; 15 | import CssBaseline from '@material-ui/core/CssBaseline'; 16 | 17 | import AllPosts from './containers/AllPosts'; 18 | import PostsBySpecifiedUser from './containers/PostsBySpecifiedUser'; 19 | 20 | Amplify.configure(awsconfig); 21 | 22 | const drawerWidth = 240; 23 | 24 | const theme = createMuiTheme({ 25 | palette: { 26 | type: 'dark', 27 | primary: { 28 | main: '#1EA1F2', 29 | contrastText: "#fff", 30 | }, 31 | background: { 32 | default: '#15202B', 33 | paper: '#15202B', 34 | }, 35 | divider: '#37444C', 36 | }, 37 | overrides: { 38 | MuiButton: { 39 | color: 'white', 40 | }, 41 | }, 42 | typography: { 43 | fontFamily: [ 44 | 'Arial', 45 | ].join(','), 46 | }, 47 | status: { 48 | danger: 'orange', 49 | }, 50 | }); 51 | 52 | const useStyles = makeStyles(theme => ({ 53 | root: { 54 | display: 'flex', 55 | height: '100%', 56 | width: 800, 57 | marginLeft: 'auto', 58 | marginRight: 'auto', 59 | }, 60 | appBar: { 61 | marginLeft: drawerWidth, 62 | }, 63 | drawer: { 64 | width: drawerWidth, 65 | flexShrink: 0, 66 | }, 67 | drawerPaper: { 68 | width: drawerWidth, 69 | }, 70 | toolbar: theme.mixins.toolbar, 71 | content: { 72 | flexGrow: 1, 73 | backgroundColor: theme.palette.background.default, 74 | padding: theme.spacing(3), 75 | }, 76 | })); 77 | 78 | const App = () => { 79 | const [authState, setAuthState] = React.useState(); 80 | const [user, setUser] = React.useState(); 81 | 82 | const classes = useStyles(); 83 | 84 | React.useEffect(() => { 85 | return onAuthUIStateChange((nextAuthState, authData) => { 86 | setAuthState(nextAuthState); 87 | setUser(authData) 88 | }); 89 | }, []); 90 | 91 | return authState === AuthState.SignedIn && user ? ( 92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 |
105 | ) : ( 106 | 107 | 115 | 116 | ); 117 | } 118 | 119 | export default App; -------------------------------------------------------------------------------- /content/30_mock/30_post_front_end/_index.ja.files/PostsBySpecifiedUser.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useReducer } from 'react'; 2 | 3 | import {API, graphqlOperation } from 'aws-amplify'; 4 | import { useParams } from 'react-router'; 5 | 6 | import { listPostsBySpecificOwner } from '../graphql/queries'; 7 | import { onCreatePost } from '../graphql/subscriptions'; 8 | 9 | import PostList from '../components/PostList'; 10 | import Sidebar from './Sidebar'; 11 | 12 | const SUBSCRIPTION = 'SUBSCRIPTION'; 13 | const INITIAL_QUERY = 'INITIAL_QUERY'; 14 | const ADDITIONAL_QUERY = 'ADDITIONAL_QUERY'; 15 | 16 | const reducer = (state, action) => { 17 | switch (action.type) { 18 | case INITIAL_QUERY: 19 | return action.posts; 20 | case ADDITIONAL_QUERY: 21 | return [...state, ...action.posts] 22 | case SUBSCRIPTION: 23 | return [action.post, ...state] 24 | default: 25 | return state; 26 | } 27 | }; 28 | 29 | export default function PostsBySpecifiedUser() { 30 | const { userId } = useParams(); 31 | 32 | const [posts, dispatch] = useReducer(reducer, []); 33 | const [nextToken, setNextToken] = useState(null); 34 | const [isLoading, setIsLoading] = useState(true); 35 | 36 | const getPosts = async (type, nextToken = null) => { 37 | const res = await API.graphql(graphqlOperation(listPostsBySpecificOwner, { 38 | owner: userId, 39 | sortDirection: 'DESC', 40 | limit: 20, 41 | nextToken: nextToken, 42 | })); 43 | console.log(res); 44 | dispatch({ type: type, posts: res.data.listPostsBySpecificOwner.items }) 45 | setNextToken(res.data.listPostsBySpecificOwner.nextToken); 46 | setIsLoading(false); 47 | } 48 | 49 | const getAdditionalPosts = () => { 50 | if (nextToken === null) return; //Reached the last page 51 | getPosts(ADDITIONAL_QUERY, nextToken); 52 | } 53 | 54 | 55 | useEffect(() => { 56 | getPosts(INITIAL_QUERY); 57 | 58 | const subscription = API.graphql(graphqlOperation(onCreatePost)).subscribe({ 59 | next: (msg) => { 60 | const post = msg.value.data.onCreatePost; 61 | if (post.owner !== userId) return; 62 | dispatch({ type: SUBSCRIPTION, post: post }); 63 | } 64 | }); 65 | return () => subscription.unsubscribe(); 66 | }, []); 67 | 68 | 69 | return ( 70 | 71 | 74 | 80 | 81 | ) 82 | } -------------------------------------------------------------------------------- /content/30_mock/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Let's make a MVP!" 3 | date = 2020-03-18T10:09:45+09:00 4 | weight = 3 5 | chapter = true 6 | pre = "3. " 7 | +++ 8 | 9 | ### Story 10 | Yuki of your friend came up with a social media app "Boyaki" where users can post and connect short text up to 140 characters. 11 | Yuki's investor said "Show me something for the time being!" 12 | Yuki asked you to make a Minimum Viable Product (MVP) in three days. 13 | Having found possibilities in the idea of Yuki, you took on the creation of an MVP. 14 | We will take advantage of AWS Amplify to make a quick MVP. 15 | Now let's create an MVP that you want to show to investors in a short time! 16 | 17 | {{% notice tip%}} 18 | Boyaki means 'tweet a bit glumly' in Japanese. 19 | {{% /notice %}} 20 | 21 | ### What will be implemented in this section 22 | - Login function 23 | - Ability for users to post tweets in their own name 24 | - Ability to display tweets of all users as a list 25 | 26 | {{< figure src="/images/30_mock/architecture.png" title="Back-end" class="center" width="50%">}} 27 | {{< figure src="/images/30_mock/confirm.png" title="Front-end" class="center" width="90%">}} 28 | 29 | 30 | ### Table of Contents in this Section 31 | {{% children showhidden="false"%}} {{% /children%}} -------------------------------------------------------------------------------- /content/30_mock/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "MVPを作ろう!" 3 | date = 2020-03-18T10:09:45+09:00 4 | weight = 3 5 | chapter = true 6 | pre = "3. " 7 | +++ 8 | 9 | ### Story 10 | あなたの友人のユキは、ユーザーが最大140字程度の短いテキストを投稿して繋がるソーシャルメディアアプリ「Boyaki」を思いつきました。 11 | とりあえずものを見せてくれ!と投資家に言われたユキは、友人のあなたに3日でMVP(Minimum Viable Product)を作るよう依頼します。 12 | ユキのアイデアに可能性を見出したあなたはMVPの作成を引き受けました。 13 | クイックにMVPを作るためAWS Amplifyを活用することにします。 14 | さあ、投資家に見せるMVPを短期間で作成しましょう! 15 | 16 | ### 本セクションで実装するもの 17 | - ログイン機能 18 | - ユーザーが自分名義でつぶやきを投稿する機能 19 | - 全てのユーザーのつぶやきを一覧として表示する機能 20 | 21 | {{< figure src="/images/30_mock/architecture.png" title="Back-end" class="center" width="50%">}} 22 | {{< figure src="/images/30_mock/confirm.png" title="Front-end" class="center" width="90%">}} 23 | 24 | ### 本セクションの目次 25 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/40_hosting/00_manual_deployment/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Manual deployment" 3 | date = 2020-03-18T10:09:48+09:00 4 | weight = 1 5 | pre = "4.1. " 6 | +++ 7 | 8 | ### AWS Amplify Console 9 | [AWS Amplify Console](https://aws.amazon.com/jp/amplify/console/) provides a Git-based workflow for deploying and hosting applications. 10 | You decided to do manual deployment instead of Git-based because you're the only engineer this time. 11 | Use the Amplify CLI to host your application using the AWS Amplify Console. 12 | 13 | Run `$amplify add hosting` in the terminal and answer the questions as follows: 14 | 15 | ``` 16 | amplify add hosting 17 | ``` 18 | 19 | - Select the plugin module to execute `Hosting with Amplify Console (Managed hosting with custom domains, Continuous deployment) ` 20 | - Choose a type `Manual deployment` 21 | 22 | Then start hosting with the `$ amplify publish` command. 23 | 24 | ``` 25 | amplify publish 26 | ``` 27 | 28 | {{% notice info%}} 29 | `$amplify publish` is a command that changes to the cloud resources in the same way as `$amplify push`, builds a web application at local, and uploads it to update the application hosted in Amplify Console. 30 | {{% /notice%}} 31 | 32 | {{% notice warning%}} 33 | If you execute `$ amplify push` or `$ amplify publish` while `$amplify mock api` is running, 34 | sometimes your command exection fail with error message: `Parameters: [authRoleName] must have values`. 35 | In that case, please interrupt the process of `$amplify mock api` with `Ctrl+C`, and then execute `$amplify push` or `$amplify publish`. 36 | {{% /notice%}} 37 | 38 | Wait a few minutes and the execution result is returned as shown below. 39 | If you visit the last URL, you are able to browse the website. 40 | 41 | ``` 42 | ✔ Zipping artifacts completed. 43 | ✔ Deployment complete! 44 | https://production.XXXXXXXXXXX.amplifyapp.com 45 | ``` -------------------------------------------------------------------------------- /content/40_hosting/00_manual_deployment/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "手動デプロイ" 3 | date = 2020-03-18T10:09:48+09:00 4 | weight = 1 5 | pre = "4.1. " 6 | +++ 7 | 8 | ### AWS Amplify Console 9 | [AWS Amplify Console](https://aws.amazon.com/jp/amplify/console/)は、フルスタックのサーバーレスウェブアプリケーションをデプロイおよびホストするための、Git ベースのワークフローを提供しています。 10 | ただ、今回は開発しているのが自分だけ、ということもあり、Gitベースでなく手動デプロイを行うことにしました。 11 | Amplify CLIから、AWS Amplify Consoleを利用したアプリケーションホスティングを行いましょう。 12 | 13 | `$ amplify add hosting`をターミナルで実行し、以下のように質問に答えていきます。 14 | 15 | ``` 16 | amplify add hosting 17 | ``` 18 | 19 | - Select the plugin module to execute `Hosting with Amplify Console (Managed hosting with custom domains, Continuous deployment)` 20 | - Choose a type `Manual deployment` 21 | 22 | 続けて`$ amplify publish`コマンドでホスティングを開始しましょう。 23 | 24 | ``` 25 | amplify publish 26 | ``` 27 | 28 | {{% notice info %}} 29 | `$ amplify publish`は`$ amplify push`と同じようにクラウドリソースへの変更を行なった上で、ウェブアプリケーションを手元でBuildし、Amplify Consoleでホスティングされたアプリケーションを更新するコマンドです。 30 | {{% /notice %}} 31 | 32 | {{% notice warning %}} 33 | `$ amplify mock api`を動かしている状態で`$ amplify push`や`$ amplify publish`を実行した場合、`Parameters: [authRoleName] must have values`というエラーがでて`$ amplify push`や`$ amplify publish`が失敗する場合があります。 34 | その場合は、`Ctrl + C`で`$ amplify mock api`のプロセスを中断してから、`$ amplify push`や`$ amplify publish`を実行してください。 35 | {{% /notice %}} 36 | 37 | 数分待つと以下のように実行結果が返ってきます。 38 | 最後のURLにアクセスすると、ウェブサイトが閲覧できます。 39 | 40 | ``` 41 | ✔ Zipping artifacts completed. 42 | ✔ Deployment complete! 43 | https://production.XXXXXXXXXXX.amplifyapp.com 44 | ``` -------------------------------------------------------------------------------- /content/40_hosting/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Website Hosting" 3 | date = 2020-03-18T10:09:48+09:00 4 | weight = 4 5 | chapter = true 6 | pre = "4. " 7 | +++ 8 | 9 | 10 | ### Story 11 | Congratulations! Your MVP was received good reaction by investors! 12 | Yuki invited you to join the team. And you decided to become CTO. 13 | The first job as CTO is to host a website so that anyone can access this application globally. 14 | 15 | {{< figure src="/images/40_hosting/architecture_hosting.png" title="Hosting" class="center" width="50%">}} 16 | 17 | ### Table of Contents in this Section 18 | {{% children showhidden="false"%}} {{% /children%}} -------------------------------------------------------------------------------- /content/40_hosting/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "ウェブサイトホスティング" 3 | date = 2020-03-18T10:09:48+09:00 4 | weight = 4 5 | chapter = true 6 | pre = "4. " 7 | +++ 8 | 9 | ### Story 10 | おめでとうございます!あなたの作成したMVPは投資家から好評でした! 11 | 本格的にジョインしないかとユキに誘われ、CTOになることを決意します。 12 | CTO最初の仕事はグローバルにこのアプリケーションにアクセスできるよう、ウェブサイトをホスティングすることです。 13 | 14 | {{< figure src="/images/40_hosting/architecture_hosting.png" title="Hosting" class="center" width="50%">}} 15 | 16 | ### 本セクションの目次 17 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/50_follow_timeline/00_follow_back_end/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Follow機能: Back-end" 3 | date = 2020-03-18T10:09:49+09:00 4 | weight = 1 5 | pre = "5.1. " 6 | +++ 7 | 8 | まずは、タイムラインの実装に必要なフォロー機能を提供するGraphQL APIを実装していきます。 9 | 10 | {{< figure src="/images/50_follow_timeline/architecture_follow.png" title="" class="center" width="50%">}} 11 | 12 | ### GraphQL APIの作成 13 | FollowRelationshipテーブルを作成し、あるユーザーが他のユーザーをフォローしている状態を保存できるようにしましょう。 14 | `./amplify/backend/api/BoyakiGql/schema.graphql`に、次のコードを追加します。 15 | 16 | ```graphql 17 | type FollowRelationship 18 | @model( 19 | mutations: {create: "createFollowRelationship", delete: "deleteFollowRelationship", update: null} 20 | timestamps: null 21 | ) 22 | @auth(rules: [ 23 | {allow: owner, ownerField:"followerId", provider: userPools, operations:[read, create, delete]}, 24 | {allow: private, provider: userPools, operations:[read]} 25 | ]) 26 | @key(fields: ["followeeId", "followerId"]) 27 | { 28 | followeeId: ID! 29 | followerId: ID! 30 | timestamp: Int! 31 | } 32 | ``` 33 | 34 | ポイントは以下です。 35 | 36 | - フィールド 37 | - `followeeId`はフォローされている人の`username`です。 38 | - `followerId`はフォローしている人の`username`です。 39 | - `tiemstamp`はフォローした日時です。 40 | - `@model` 41 | - `mutations:...`では必要のないudpate用APIを作らない設定をします 42 | - `timestamps:...`では、デフォルトで自動的に付与される`updatedAt``createdAt`の属性を作らない設定をします。代わりにAWS Timestamp属性の`timestamp`を用います。 43 | - `@auth` 44 | - `{allow: owner...`により、ユーザーは自分を`follower`としたFollowRelationshipの作成と閲覧ができます 45 | - `{allow: private...`では他のユーザーのFollowRelasionshipの閲覧を許可しています 46 | - `@key` 47 | - `name`や`queryField`を指定しない場合、DynamoDB Table自体のPartition Key(PK)やSecondary Key(SK)を設定することができます 48 | - 今回、Postを投稿したユーザーのフォロワーのTimelineにPostを複製するため、ある`followeeId`に紐づくFollowRelationship一覧を取得する必要があります 49 | - そのため`followeeId`をPK、`followerId`をSKに指定します 50 | 51 | ### Mockingによる動作確認 52 | `$ amplify mock api`を実行して、FollowRelationship APIが期待通り動くかどうか確認しましょう。 53 | 54 | {{% notice info%}} 55 | Amplify Mocking(手元での動作確認)には20002番ポートが利用可能である必要があり、一部の Cloud IDE では利用できないことがあります。 56 | 該当するCloud IDEをご利用の方は本手順は実施せず、雰囲気を味わっていただけますと幸いです。 57 | {{% /notice %}} 58 | 59 | ```bash 60 | amplify mock api 61 | 62 | {中略} 63 | 64 | AppSync Mock endpoint is running at http://XXX.XXX.XXX.XXX:20002 65 | ``` 66 | 67 | Mock endpointをコピーし、任意のブラウザでアクセスします。 68 | 69 | #### createFollowRelationship 70 | まず、FollowRelationshipを作成してみましょう。 71 | 72 | 1. 左下の`ADD NEW Query`と書いてある場所をクリックし、`Mutation`を選択したのち`+`をクリックします。 73 | 1. `Update Auth`ボタンを押して、`Username`を`test_follower`にします 74 | 1. 左のペインにある`createFollowRelationship`をクリックし、次図のようにチェックボックスやフィールド埋めてみましょう。 75 | 1. `followeeId`: `test_followee` 76 | 1. `followerId`: `test_follower` 77 | 1. **▶︎**をクリックしてGraphQLのMutationを実行します。 78 | 79 | ![](/images/50_follow_timeline/follow_mutation.png) 80 | 81 | {{% notice info %}} 82 | `Unauthorized Error`が発生する場合、`Update Auth`ボタンを押して現在Mutationを発行しているユーザーの`Username`と、`followerId`が一致していることを確かめてください。`@auth`で設定した通り、フォローするユーザー(=フォロワー)しか`createFollowRelationship`を実行できません。エラーが発生しなかった方は`followerId`を別の`Username`として実行し、`Unauthorized Error`が発生することを確かめてみてください。 83 | {{% /notice %}} 84 | 85 | #### listFollowRelationships 86 | 次に、あるユーザーのフォロワー一覧を取得してみます。 87 | 88 | 1. 左下の`ADD NEW Mutation`と書いてある場所をクリックし、`Query`を選択したのち`+`をクリックします。 89 | 1. 左のペインにある`listFollowRelationship`をクリックし、次図のようにチェックボックスやフィールド埋めてみましょう。 90 | 1. `followeeId`: `test_followee` 91 | 1. **▶︎**をクリックしてGraphQLのQueryを実行します。 92 | 1. 右側のペインに結果が表示されます。先ほど作成したFollowRelationshipが表示されていることを確認してください。 93 | 94 | ![](/images/50_follow_timeline/listFollower.png) -------------------------------------------------------------------------------- /content/50_follow_timeline/30_function_directive/_index.en.files/schema.graphql: -------------------------------------------------------------------------------- 1 | type Post 2 | @model ( 3 | mutations: {create: "createPost", delete: "deletePost", update: null} 4 | timestamps: null 5 | subscriptions: { level: public} 6 | ) 7 | @auth(rules: [ 8 | {allow: owner, ownerField:"owner", provider: userPools, operations:[read, create, delete]} 9 | {allow: private, provider: userPools, operations:[read]} 10 | {allow: private, provider: iam ,operations:[create]} 11 | ]) 12 | @key(name: "SortByTimestamp", fields:["type", "timestamp"], queryField: "listPostsSortedByTimestamp") 13 | @key(name: "BySpecificOwner", fields:["owner", "timestamp"], queryField: "listPostsBySpecificOwner") 14 | { 15 | type: String! # always set to 'post'. used in the SortByTimestamp GSI 16 | id: ID 17 | content: String! 18 | owner: String 19 | timestamp: Int! 20 | } 21 | 22 | type FollowRelationship 23 | @model( 24 | mutations: {create: "createFollowRelationship", delete: "deleteFollowRelationship", update: null} 25 | timestamps: null 26 | ) 27 | @auth(rules: [ 28 | {allow: owner, ownerField:"followerId", provider: userPools, operations:[read, create, delete]}, 29 | {allow: private, provider: userPools, operations:[read]} 30 | {allow: private, provider: iam ,operations:[read]} 31 | ]) 32 | @key(fields: ["followeeId", "followerId"]) 33 | { 34 | followeeId: ID! 35 | followerId: ID! 36 | timestamp: Int! 37 | } 38 | 39 | type Timeline 40 | @model( 41 | mutations: {create: "createTimeline", delete: null, update: null} 42 | timestamps: null 43 | ) 44 | @auth(rules: [ 45 | {allow: owner, ownerField: "userId", provider: userPools, operations:[read, create]}, 46 | {allow: private, provider: iam ,operations:[create]}, 47 | ]) 48 | @key(fields: ["userId", "timestamp"]) 49 | { 50 | userId: ID! 51 | timestamp: Int! 52 | postId: ID! 53 | post: Post @connection(fields: ["postId"]) 54 | } 55 | 56 | type Mutation 57 | { 58 | createPostAndTimeline( 59 | content: String! 60 | ): Post 61 | @function(name: "createPostAndTimeline-${env}") 62 | @auth(rules: [ 63 | {allow: private, provider: userPools}, 64 | ]) 65 | } -------------------------------------------------------------------------------- /content/50_follow_timeline/30_function_directive/_index.ja.files/schema.graphql: -------------------------------------------------------------------------------- 1 | type Post 2 | @model ( 3 | mutations: {create: "createPost", delete: "deletePost", update: null} 4 | timestamps: null 5 | subscriptions: { level: public} 6 | ) 7 | @auth(rules: [ 8 | {allow: owner, ownerField:"owner", provider: userPools, operations:[read, create, delete]} 9 | {allow: private, provider: userPools, operations:[read]} 10 | {allow: private, provider: iam ,operations:[create]} 11 | ]) 12 | @key(name: "SortByTimestamp", fields:["type", "timestamp"], queryField: "listPostsSortedByTimestamp") 13 | @key(name: "BySpecificOwner", fields:["owner", "timestamp"], queryField: "listPostsBySpecificOwner") 14 | { 15 | type: String! # always set to 'post'. used in the SortByTimestamp GSI 16 | id: ID 17 | content: String! 18 | owner: String 19 | timestamp: Int! 20 | } 21 | 22 | type FollowRelationship 23 | @model( 24 | mutations: {create: "createFollowRelationship", delete: "deleteFollowRelationship", update: null} 25 | timestamps: null 26 | ) 27 | @auth(rules: [ 28 | {allow: owner, ownerField:"followerId", provider: userPools, operations:[read, create, delete]}, 29 | {allow: private, provider: userPools, operations:[read]} 30 | {allow: private, provider: iam ,operations:[read]} 31 | ]) 32 | @key(fields: ["followeeId", "followerId"]) 33 | { 34 | followeeId: ID! 35 | followerId: ID! 36 | timestamp: Int! 37 | } 38 | 39 | type Timeline 40 | @model( 41 | mutations: {create: "createTimeline", delete: null, update: null} 42 | timestamps: null 43 | ) 44 | @auth(rules: [ 45 | {allow: owner, ownerField: "userId", provider: userPools, operations:[read, create]}, 46 | {allow: private, provider: iam ,operations:[create]}, 47 | ]) 48 | @key(fields: ["userId", "timestamp"]) 49 | { 50 | userId: ID! 51 | timestamp: Int! 52 | postId: ID! 53 | post: Post @connection(fields: ["postId"]) 54 | } 55 | 56 | type Mutation 57 | { 58 | createPostAndTimeline( 59 | content: String! 60 | ): Post 61 | @function(name: "createPostAndTimeline-${env}") 62 | @auth(rules: [ 63 | {allow: private, provider: userPools}, 64 | ]) 65 | } -------------------------------------------------------------------------------- /content/50_follow_timeline/50_timeline_front_end/_index.en.files/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Amplify from 'aws-amplify'; 3 | import { AmplifyAuthenticator, AmplifySignUp } from '@aws-amplify/ui-react'; 4 | import { AuthState, onAuthUIStateChange } from '@aws-amplify/ui-components'; 5 | import awsconfig from './aws-exports'; 6 | 7 | import { 8 | HashRouter, 9 | Switch, 10 | Route, 11 | Redirect, 12 | } from 'react-router-dom'; 13 | 14 | import { makeStyles, createMuiTheme, ThemeProvider } from '@material-ui/core/styles'; 15 | import CssBaseline from '@material-ui/core/CssBaseline'; 16 | 17 | import AllPosts from './containers/AllPosts'; 18 | import PostsBySpecifiedUser from './containers/PostsBySpecifiedUser'; 19 | import Timeline from './containers/Timeline'; 20 | 21 | Amplify.configure(awsconfig); 22 | 23 | const drawerWidth = 240; 24 | 25 | const theme = createMuiTheme({ 26 | palette: { 27 | type: 'dark', 28 | primary: { 29 | main: '#1EA1F2', 30 | contrastText: "#fff", 31 | }, 32 | background: { 33 | default: '#15202B', 34 | paper: '#15202B', 35 | }, 36 | divider: '#37444C', 37 | }, 38 | overrides: { 39 | MuiButton: { 40 | color: 'white', 41 | }, 42 | }, 43 | typography: { 44 | fontFamily: [ 45 | 'Arial', 46 | ].join(','), 47 | }, 48 | status: { 49 | danger: 'orange', 50 | }, 51 | }); 52 | 53 | const useStyles = makeStyles(theme => ({ 54 | root: { 55 | display: 'flex', 56 | height: '100%', 57 | width: 800, 58 | marginLeft: 'auto', 59 | marginRight: 'auto', 60 | }, 61 | appBar: { 62 | marginLeft: drawerWidth, 63 | }, 64 | drawer: { 65 | width: drawerWidth, 66 | flexShrink: 0, 67 | }, 68 | drawerPaper: { 69 | width: drawerWidth, 70 | }, 71 | toolbar: theme.mixins.toolbar, 72 | content: { 73 | flexGrow: 1, 74 | backgroundColor: theme.palette.background.default, 75 | padding: theme.spacing(3), 76 | }, 77 | })); 78 | 79 | const App = () => { 80 | const [authState, setAuthState] = React.useState(); 81 | const [user, setUser] = React.useState(); 82 | 83 | const classes = useStyles(); 84 | 85 | React.useEffect(() => { 86 | return onAuthUIStateChange((nextAuthState, authData) => { 87 | setAuthState(nextAuthState); 88 | setUser(authData) 89 | }); 90 | }, []); 91 | 92 | return authState === AuthState.SignedIn && user ? ( 93 |
94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 | ) : ( 107 | 108 | 116 | 117 | ); 118 | } 119 | 120 | export default App; -------------------------------------------------------------------------------- /content/50_follow_timeline/50_timeline_front_end/_index.en.files/Timeline.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useReducer } from 'react'; 2 | 3 | import {Auth, API, graphqlOperation } from 'aws-amplify'; 4 | 5 | import { listTimelines } from '../graphql/queries'; 6 | import { onCreateTimeline } from '../graphql/subscriptions'; 7 | import _ from 'lodash'; 8 | 9 | import PostList from '../components/PostList'; 10 | import Sidebar from './Sidebar'; 11 | 12 | const SUBSCRIPTION = 'SUBSCRIPTION'; 13 | const INITIAL_QUERY = 'INITIAL_QUERY'; 14 | const ADDITIONAL_QUERY = 'ADDITIONAL_QUERY'; 15 | 16 | const reducer = (state, action) => { 17 | switch (action.type) { 18 | case INITIAL_QUERY: 19 | return action.posts; 20 | case ADDITIONAL_QUERY: 21 | return [...state, ...action.posts] 22 | case SUBSCRIPTION: 23 | return [action.post, ...state] 24 | default: 25 | return state; 26 | } 27 | }; 28 | 29 | export default function Timeline() { 30 | const [posts, dispatch] = useReducer(reducer, []); 31 | const [nextToken, setNextToken] = useState(null); 32 | const [isLoading, setIsLoading] = useState(true); 33 | const [currentUser, setCurrentUser] = useState(null); 34 | 35 | const getPosts = async (type, currentUser, nextToken = null) => { 36 | const res = await API.graphql(graphqlOperation(listTimelines, { 37 | userId: currentUser.username, 38 | sortDirection: 'DESC', 39 | limit: 20, //default = 10 40 | nextToken: nextToken, 41 | })); 42 | console.log(res); 43 | dispatch({ type: type, posts: _.map(res.data.listTimelines.items, 'post') }) 44 | setNextToken(res.data.listTimelines.nextToken); 45 | setIsLoading(false); 46 | } 47 | 48 | const getAdditionalPosts = () => { 49 | if (nextToken === null) return; //Reached the last page 50 | getPosts(ADDITIONAL_QUERY, currentUser, nextToken); 51 | } 52 | 53 | useEffect(() => { 54 | console.log('init') 55 | const init = async () => { 56 | const currentUser = await Auth.currentAuthenticatedUser(); 57 | setCurrentUser(currentUser); 58 | 59 | getPosts(INITIAL_QUERY, currentUser); 60 | } 61 | 62 | init(); 63 | }, []); 64 | 65 | useEffect(()=> { 66 | console.log(currentUser); 67 | if(!currentUser) return; 68 | console.log('make subscription') 69 | const subscription = API.graphql(graphqlOperation(onCreateTimeline, {userId: currentUser.username})).subscribe({ 70 | next: (msg) => { 71 | console.log('timeline subscription fired') 72 | console.log(msg) 73 | dispatch({ type: SUBSCRIPTION, post: msg.value.data.onCreateTimeline.post }); 74 | } 75 | }); 76 | return () => subscription.unsubscribe(); 77 | }, [currentUser]) 78 | 79 | return ( 80 | 81 | 84 | 90 | 91 | ) 92 | } -------------------------------------------------------------------------------- /content/50_follow_timeline/50_timeline_front_end/_index.ja.files/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Amplify from 'aws-amplify'; 3 | import { AmplifyAuthenticator, AmplifySignUp } from '@aws-amplify/ui-react'; 4 | import { AuthState, onAuthUIStateChange } from '@aws-amplify/ui-components'; 5 | import awsconfig from './aws-exports'; 6 | 7 | import { 8 | HashRouter, 9 | Switch, 10 | Route, 11 | Redirect, 12 | } from 'react-router-dom'; 13 | 14 | import { makeStyles, createMuiTheme, ThemeProvider } from '@material-ui/core/styles'; 15 | import CssBaseline from '@material-ui/core/CssBaseline'; 16 | 17 | import AllPosts from './containers/AllPosts'; 18 | import PostsBySpecifiedUser from './containers/PostsBySpecifiedUser'; 19 | import Timeline from './containers/Timeline'; 20 | 21 | Amplify.configure(awsconfig); 22 | 23 | const drawerWidth = 240; 24 | 25 | const theme = createMuiTheme({ 26 | palette: { 27 | type: 'dark', 28 | primary: { 29 | main: '#1EA1F2', 30 | contrastText: "#fff", 31 | }, 32 | background: { 33 | default: '#15202B', 34 | paper: '#15202B', 35 | }, 36 | divider: '#37444C', 37 | }, 38 | overrides: { 39 | MuiButton: { 40 | color: 'white', 41 | }, 42 | }, 43 | typography: { 44 | fontFamily: [ 45 | 'Arial', 46 | ].join(','), 47 | }, 48 | status: { 49 | danger: 'orange', 50 | }, 51 | }); 52 | 53 | const useStyles = makeStyles(theme => ({ 54 | root: { 55 | display: 'flex', 56 | height: '100%', 57 | width: 800, 58 | marginLeft: 'auto', 59 | marginRight: 'auto', 60 | }, 61 | appBar: { 62 | marginLeft: drawerWidth, 63 | }, 64 | drawer: { 65 | width: drawerWidth, 66 | flexShrink: 0, 67 | }, 68 | drawerPaper: { 69 | width: drawerWidth, 70 | }, 71 | toolbar: theme.mixins.toolbar, 72 | content: { 73 | flexGrow: 1, 74 | backgroundColor: theme.palette.background.default, 75 | padding: theme.spacing(3), 76 | }, 77 | })); 78 | 79 | const App = () => { 80 | const [authState, setAuthState] = React.useState(); 81 | const [user, setUser] = React.useState(); 82 | 83 | const classes = useStyles(); 84 | 85 | React.useEffect(() => { 86 | return onAuthUIStateChange((nextAuthState, authData) => { 87 | setAuthState(nextAuthState); 88 | setUser(authData) 89 | }); 90 | }, []); 91 | 92 | return authState === AuthState.SignedIn && user ? ( 93 |
94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 | ) : ( 107 | 108 | 116 | 117 | ); 118 | } 119 | 120 | export default App; -------------------------------------------------------------------------------- /content/50_follow_timeline/50_timeline_front_end/_index.ja.files/Timeline.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useReducer } from 'react'; 2 | 3 | import {Auth, API, graphqlOperation } from 'aws-amplify'; 4 | 5 | import { listTimelines } from '../graphql/queries'; 6 | import { onCreateTimeline } from '../graphql/subscriptions'; 7 | import _ from 'lodash'; 8 | 9 | import PostList from '../components/PostList'; 10 | import Sidebar from './Sidebar'; 11 | 12 | const SUBSCRIPTION = 'SUBSCRIPTION'; 13 | const INITIAL_QUERY = 'INITIAL_QUERY'; 14 | const ADDITIONAL_QUERY = 'ADDITIONAL_QUERY'; 15 | 16 | const reducer = (state, action) => { 17 | switch (action.type) { 18 | case INITIAL_QUERY: 19 | return action.posts; 20 | case ADDITIONAL_QUERY: 21 | return [...state, ...action.posts] 22 | case SUBSCRIPTION: 23 | return [action.post, ...state] 24 | default: 25 | return state; 26 | } 27 | }; 28 | 29 | export default function Timeline() { 30 | const [posts, dispatch] = useReducer(reducer, []); 31 | const [nextToken, setNextToken] = useState(null); 32 | const [isLoading, setIsLoading] = useState(true); 33 | const [currentUser, setCurrentUser] = useState(null); 34 | 35 | const getPosts = async (type, currentUser, nextToken = null) => { 36 | const res = await API.graphql(graphqlOperation(listTimelines, { 37 | userId: currentUser.username, 38 | sortDirection: 'DESC', 39 | limit: 20, //default = 10 40 | nextToken: nextToken, 41 | })); 42 | console.log(res); 43 | dispatch({ type: type, posts: _.map(res.data.listTimelines.items, 'post') }) 44 | setNextToken(res.data.listTimelines.nextToken); 45 | setIsLoading(false); 46 | } 47 | 48 | const getAdditionalPosts = () => { 49 | if (nextToken === null) return; //Reached the last page 50 | getPosts(ADDITIONAL_QUERY, currentUser, nextToken); 51 | } 52 | 53 | useEffect(() => { 54 | console.log('init') 55 | const init = async () => { 56 | const currentUser = await Auth.currentAuthenticatedUser(); 57 | setCurrentUser(currentUser); 58 | 59 | getPosts(INITIAL_QUERY, currentUser); 60 | } 61 | 62 | init(); 63 | }, []); 64 | 65 | useEffect(()=> { 66 | console.log(currentUser); 67 | if(!currentUser) return; 68 | console.log('make subscription') 69 | const subscription = API.graphql(graphqlOperation(onCreateTimeline, {userId: currentUser.username})).subscribe({ 70 | next: (msg) => { 71 | console.log('timeline subscription fired') 72 | console.log(msg) 73 | dispatch({ type: SUBSCRIPTION, post: msg.value.data.onCreateTimeline.post }); 74 | } 75 | }); 76 | return () => subscription.unsubscribe(); 77 | }, [currentUser]) 78 | 79 | return ( 80 | 81 | 84 | 90 | 91 | ) 92 | } -------------------------------------------------------------------------------- /content/50_follow_timeline/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title="Implementing Follow/Timeline" 3 | date = 2020-03-18T10:09:49+09:00 4 | weight = 5 5 | chapter = true 6 | pre = "5. " 7 | +++ 8 | 9 | 10 | ### Story 11 | After the service was published, the number of users is steadily increasing. 12 | As the number of users grows, it has become hard to follow every Post. 13 | So Yuki and you decide to implement a timeline feature that displays only the Post of the people that the user is following. 14 | Let's also implement the necessary follow-up function according to the timeline function. 15 | 16 | {{% notice info %}} 17 | For those who start with this chapter, please look at [10.1 chapter 5 or 7 chapter to start](/100_supplemental_resource/20_start_from_day23.html). 18 | It describes how to shortcut the contents of the previous chapter. 19 | {{% /notice %}} 20 | 21 | ### What implements in this section 22 | - API and DB (FollowRelationship Table) for storing and retrieving Follow relationships. 23 | - API and DB (Timeline Table) for saving and retrieving the Post list of users that each user follows 24 | - Lambda Resolver writes the follower's Timeline Table when someone creates a Post, based on the Follow relationship 25 | - So far **boyaki** reads and writes directly to the Post Table 26 | - After this section, Post creation is done via Lambda Resolver 27 | 28 | {{< figure src="/images/50_follow_timeline/architecture.png" title="" class="center" width="50%">}} 29 | 30 | 31 | ### Table of Contents in this Section 32 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/50_follow_timeline/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Follow/Timeline機能の実装" 3 | date = 2020-03-18T10:09:49+09:00 4 | weight = 5 5 | chapter = true 6 | pre = "5. " 7 | +++ 8 | 9 | ### Story 10 | サービス公開後、順調にユーザー数は増加しています。 11 | ユーザー数が増加するにつれ、すべてのPostを追うのが辛くなってきました。 12 | そこでユキとあなたは、ユーザーがフォローしている人のPostだけを表示するタイムライン機能を実装することにします。 13 | タイムライン機能に合わせて必要なフォロー機能も実装していきましょう。 14 | 15 | ### 本セクションで実装するもの 16 | - Follow関係を保存・取得するためのAPIとDB(FollowRelationship Table) 17 | - 各ユーザーがフォローするユーザーのPost一覧を保存・取得するためのAPIとDB(Timeline Table) 18 | - Follow関係に基づき、Post作成時にフォロワーのTimeline Tableに書き込むLambda Resolver 19 | - 今までは直接Post Tableに読み書きしていました 20 | - 今後はPost作成はLambda Resolverを介して行います 21 | 22 | {{< figure src="/images/50_follow_timeline/architecture.png" title="" class="center" width="50%">}} 23 | 24 | ### 本セクションの目次 25 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/60_full_text_search/00_searchable_back_end/_index.en.files/schema.graphql: -------------------------------------------------------------------------------- 1 | type Post 2 | @model ( 3 | mutations: {create: "createPost", delete: "deletePost", update: null} 4 | timestamps: null 5 | subscriptions: { level: public} 6 | ) 7 | @auth(rules: [ 8 | {allow: owner, ownerField:"owner", provider: userPools, operations:[read, create, delete]} 9 | {allow: private, provider: userPools, operations:[read]} 10 | {allow: private, provider: iam ,operations:[create]} 11 | ]) 12 | @key(name: "SortByTimestamp", fields:["type", "timestamp"], queryField: "listPostsSortedByTimestamp") 13 | @key(name: "BySpecificOwner", fields:["owner", "timestamp"], queryField: "listPostsBySpecificOwner") 14 | @searchable 15 | { 16 | type: String! # always set to 'post'. used in the SortByTimestamp GSI 17 | id: ID 18 | content: String! 19 | owner: String 20 | timestamp: Int! 21 | } 22 | 23 | type FollowRelationship 24 | @model( 25 | mutations: {create: "createFollowRelationship", delete: "deleteFollowRelationship", update: null} 26 | timestamps: null 27 | ) 28 | @auth(rules: [ 29 | {allow: owner, ownerField:"followerId", provider: userPools, operations:[read, create, delete]}, 30 | {allow: private, provider: userPools, operations:[read]} 31 | {allow: private, provider: iam ,operations:[read]} 32 | ]) 33 | @key(fields: ["followeeId", "followerId"]) 34 | { 35 | followeeId: ID! 36 | followerId: ID! 37 | timestamp: Int! 38 | } 39 | 40 | type Timeline 41 | @model( 42 | mutations: {create: "createTimeline", delete: null, update: null} 43 | timestamps: null 44 | ) 45 | @auth(rules: [ 46 | {allow: owner, ownerField: "userId", provider: userPools, operations:[read, create]}, 47 | {allow: private, provider: iam ,operations:[create]}, 48 | ]) 49 | @key(fields: ["userId", "timestamp"]) 50 | { 51 | userId: ID! 52 | timestamp: Int! 53 | postId: ID! 54 | post: Post @connection(fields: ["postId"]) 55 | } 56 | 57 | type Mutation 58 | { 59 | createPostAndTimeline( 60 | content: String! 61 | ): Post 62 | @function(name: "createPostAndTimeline-${env}") 63 | @auth(rules: [ 64 | {allow: private, provider: userPools}, 65 | ]) 66 | } -------------------------------------------------------------------------------- /content/60_full_text_search/00_searchable_back_end/_index.ja.files/schema.graphql: -------------------------------------------------------------------------------- 1 | type Post 2 | @model ( 3 | mutations: {create: "createPost", delete: "deletePost", update: null} 4 | timestamps: null 5 | subscriptions: { level: public} 6 | ) 7 | @auth(rules: [ 8 | {allow: owner, ownerField:"owner", provider: userPools, operations:[read, create, delete]} 9 | {allow: private, provider: userPools, operations:[read]} 10 | {allow: private, provider: iam ,operations:[create]} 11 | ]) 12 | @key(name: "SortByTimestamp", fields:["type", "timestamp"], queryField: "listPostsSortedByTimestamp") 13 | @key(name: "BySpecificOwner", fields:["owner", "timestamp"], queryField: "listPostsBySpecificOwner") 14 | @searchable 15 | { 16 | type: String! # always set to 'post'. used in the SortByTimestamp GSI 17 | id: ID 18 | content: String! 19 | owner: String 20 | timestamp: Int! 21 | } 22 | 23 | type FollowRelationship 24 | @model( 25 | mutations: {create: "createFollowRelationship", delete: "deleteFollowRelationship", update: null} 26 | timestamps: null 27 | ) 28 | @auth(rules: [ 29 | {allow: owner, ownerField:"followerId", provider: userPools, operations:[read, create, delete]}, 30 | {allow: private, provider: userPools, operations:[read]} 31 | {allow: private, provider: iam ,operations:[read]} 32 | ]) 33 | @key(fields: ["followeeId", "followerId"]) 34 | { 35 | followeeId: ID! 36 | followerId: ID! 37 | timestamp: Int! 38 | } 39 | 40 | type Timeline 41 | @model( 42 | mutations: {create: "createTimeline", delete: null, update: null} 43 | timestamps: null 44 | ) 45 | @auth(rules: [ 46 | {allow: owner, ownerField: "userId", provider: userPools, operations:[read, create]}, 47 | {allow: private, provider: iam ,operations:[create]}, 48 | ]) 49 | @key(fields: ["userId", "timestamp"]) 50 | { 51 | userId: ID! 52 | timestamp: Int! 53 | postId: ID! 54 | post: Post @connection(fields: ["postId"]) 55 | } 56 | 57 | type Mutation 58 | { 59 | createPostAndTimeline( 60 | content: String! 61 | ): Post 62 | @function(name: "createPostAndTimeline-${env}") 63 | @auth(rules: [ 64 | {allow: private, provider: userPools}, 65 | ]) 66 | } -------------------------------------------------------------------------------- /content/60_full_text_search/00_searchable_back_end/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "全文検索機能: Back-end" 3 | date = 2020-03-18T10:09:53+09:00 4 | weight = 1 5 | pre = "6.1. " 6 | +++ 7 | 8 | AWS Amplifyを利用した全文検索機能の実装は非常にシンプルです。 9 | 10 | ### @searchable 11 | `@searchable`ディレクティブを使用すると、該当のモデルを対象とした全文検索が可能になります。 12 | `./amplify/backend/api/BoyakiGql/schema.graphql`を開き、編集します。 13 | 14 | ```graphql 15 | type Post 16 | @model ( 17 | mutations: {create: "createPost", delete: "deletePost", update: null} 18 | timestamps: null 19 | subscriptions: { level: public} 20 | ) 21 | @auth(rules: [ 22 | {allow: owner, ownerField:"owner", provider: userPools, operations:[read, create, delete]} 23 | {allow: private, provider: userPools, operations:[read]} 24 | {allow: private, provider: iam ,operations:[create]} 25 | ]) 26 | @key(name: "SortByTimestamp", fields:["type", "timestamp"], queryField: "listPostsSortedByTimestamp") 27 | @key(name: "BySpecificOwner", fields:["owner", "timestamp"], queryField: "listPostsBySpecificOwner") 28 | @searchable 29 | { 30 | type: String! # always set to 'post'. used in the SortByTimestamp GSI 31 | id: ID 32 | content: String! 33 | owner: String 34 | timestamp: Int! 35 | } 36 | ``` 37 | 38 | 現在の`schema.graphql`は以下のようになっています。 39 | 40 | {{%attachments title="./amplify/backend/api/BoyakiGql/schema.graphql" pattern="schema.graphql"/%}} 41 | 42 | {{% notice tip %}} 43 | `@searchable`ディレクティブを使用すると、[Amazon DynamoDB Streams](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Streams.html)と[Amazon Elasticsearch Service](https://aws.amazon.com/jp/elasticsearch-service/)がプロビジョニングされます。 44 | 該当のDynamoDB TableにInsertされたデータをDynamoDB Streamsがフックし、Elasticsearchへレコードを追加します。 45 | フロントエンドからElasticsearchにクエリを投げるには、GraphQL APIにセットアップされる`searchPosts`というQueryを実行します。 46 | `@searchable`ディレクティブが付与される前にInsertされたデータに関してはElasticsearchでクエリできませんのでご注意ください。 47 | `@searchable`の詳細は[こちら](https://docs.amplify.aws/cli/graphql-transformer/directives#searchable)。 48 | {{% /notice %}} 49 | 50 | 51 | ### 動作検証 52 | 残念ながら現在、`@searchable`はAmplify Mockingに対応しておりません。 53 | そのため、今回はAWS AppSyncのマネジメントコンソールを用いて動作検証を行います。 54 | 55 | #### クラウドへの変更反映 56 | 57 | ```bash 58 | $ amplify push 59 | ``` 60 | 61 | 実行には10分程度かかります。 62 | 63 | #### Postの作成 64 | `@searchable`ディレクティブが付与される前にInsertされたデータに関してはElasticsearchでクエリできないため、あらたにPostを作成しておきます。 65 | 66 | 1. `$ amplify status`コマンドを実行し、`Amplify hosting urls`の項目を確認してURLを確認します 67 | 1. 作成したアプリを通じて、いくつかPostを作成しておきます(後ほど検索することを考えて、投稿内容を頭に残しておきましょう) 68 | 69 | #### AWS AppSync 70 | 1. [AWS AppSyncのマネジメントコンソール](https://us-east-1.console.aws.amazon.com/appsync/home?region=us-east-1#/apis)を開きます。 71 | 1. 今回作成したAPIを開きます(手順通り作成した場合は`BoyakiGql-production`という名前) 72 | 1. 左のメニューから`クエリ`を開きます 73 | 1. **▶︎**アイコンの右側にある、`ユーザープールでログイン`をクリックします 74 | 1. ログインしたいユーザーのUsername/PasswordとWebClient IDを入力します 75 | 1. WebClientIDは、`./src/aws-exports.js`内の`aws_user_pools_web_client_id`の項目を参照します 76 | 1. 例: `"aws_user_pools_web_client_id": "XXXXXXXXXXXxXXXXXXXXXXXXX"`のXXXXXX...の部分 77 | 1. 左側のペインに次のQueryを書き込みます。`Amplify`の部分には検索したいフレーズを入力しましょう 78 | ``` 79 | query MyQuery{ 80 | searchPosts ( 81 | filter: {content: { matchPhrase: "AWS Amplify"} } 82 | ){ 83 | items{ 84 | id 85 | content 86 | owner 87 | } 88 | } 89 | } 90 | ``` 91 | 1. `content`に入力したフレーズが含まれるPost一覧が取得できれば成功です 92 | 93 | ![](/images/60_full_text_search/searchPosts.png) 94 | 95 | {{% notice tip%}} 96 | `matchPhrase`はElasticsearchの`match_phrase`クエリに相当します。 97 | 半角スペース区切りで渡した検索キーワードが全てその順番に登場する場合にマッチします。 98 | その他、`match`や`multiPhrase`、`wildcard`の使用や、`or``and`条件を利用した複雑なクエリも可能です。 99 | `@searchable`で使用可能なクエリは[こちら](https://docs.amplify.aws/cli/graphql-transformer/directives#usage-5)をご参照ください。 100 | {{% /notice %}} -------------------------------------------------------------------------------- /content/60_full_text_search/10_searchable_front_end/_index.en.files/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Amplify from 'aws-amplify'; 3 | import { AmplifyAuthenticator, AmplifySignUp } from '@aws-amplify/ui-react'; 4 | import { AuthState, onAuthUIStateChange } from '@aws-amplify/ui-components'; 5 | import awsconfig from './aws-exports'; 6 | 7 | import { 8 | HashRouter, 9 | Switch, 10 | Route, 11 | Redirect, 12 | } from 'react-router-dom'; 13 | 14 | import { makeStyles, createMuiTheme, ThemeProvider } from '@material-ui/core/styles'; 15 | import CssBaseline from '@material-ui/core/CssBaseline'; 16 | 17 | import AllPosts from './containers/AllPosts'; 18 | import PostsBySpecifiedUser from './containers/PostsBySpecifiedUser'; 19 | import Timeline from './containers/Timeline'; 20 | import Search from './containers/Search'; 21 | 22 | Amplify.configure(awsconfig); 23 | 24 | const drawerWidth = 240; 25 | 26 | const theme = createMuiTheme({ 27 | palette: { 28 | type: 'dark', 29 | primary: { 30 | main: '#1EA1F2', 31 | contrastText: "#fff", 32 | }, 33 | background: { 34 | default: '#15202B', 35 | paper: '#15202B', 36 | }, 37 | divider: '#37444C', 38 | }, 39 | overrides: { 40 | MuiButton: { 41 | color: 'white', 42 | }, 43 | }, 44 | typography: { 45 | fontFamily: [ 46 | 'Arial', 47 | ].join(','), 48 | }, 49 | status: { 50 | danger: 'orange', 51 | }, 52 | }); 53 | 54 | const useStyles = makeStyles(theme => ({ 55 | root: { 56 | display: 'flex', 57 | height: '100%', 58 | width: 800, 59 | marginLeft: 'auto', 60 | marginRight: 'auto', 61 | }, 62 | appBar: { 63 | marginLeft: drawerWidth, 64 | }, 65 | drawer: { 66 | width: drawerWidth, 67 | flexShrink: 0, 68 | }, 69 | drawerPaper: { 70 | width: drawerWidth, 71 | }, 72 | toolbar: theme.mixins.toolbar, 73 | content: { 74 | flexGrow: 1, 75 | backgroundColor: theme.palette.background.default, 76 | padding: theme.spacing(3), 77 | }, 78 | })); 79 | 80 | const App = () => { 81 | const [authState, setAuthState] = React.useState(); 82 | const [user, setUser] = React.useState(); 83 | 84 | const classes = useStyles(); 85 | 86 | React.useEffect(() => { 87 | return onAuthUIStateChange((nextAuthState, authData) => { 88 | setAuthState(nextAuthState); 89 | setUser(authData) 90 | }); 91 | }, []); 92 | 93 | return authState === AuthState.SignedIn && user ? ( 94 |
95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
108 | ) : ( 109 | 110 | 118 | 119 | ); 120 | } 121 | 122 | export default App; -------------------------------------------------------------------------------- /content/60_full_text_search/10_searchable_front_end/_index.en.files/Search.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useReducer } from 'react'; 2 | 3 | import {API, graphqlOperation } from 'aws-amplify'; 4 | 5 | import { 6 | Button, 7 | TextField, 8 | } from '@material-ui/core'; 9 | 10 | import PostList from '../components/PostList'; 11 | import Sidebar from './Sidebar'; 12 | 13 | const SUBSCRIPTION = 'SUBSCRIPTION'; 14 | const INITIAL_QUERY = 'INITIAL_QUERY'; 15 | const ADDITIONAL_QUERY = 'ADDITIONAL_QUERY'; 16 | 17 | const reducer = (state, action) => { 18 | switch (action.type) { 19 | case INITIAL_QUERY: 20 | return action.posts; 21 | case ADDITIONAL_QUERY: 22 | return [...state, ...action.posts] 23 | case SUBSCRIPTION: 24 | return [action.post, ...state] 25 | default: 26 | return state; 27 | } 28 | }; 29 | 30 | export default function Search() { 31 | const [posts, dispatch] = useReducer(reducer, []); 32 | const [nextToken, setNextToken] = useState(null); 33 | const [isLoading, setIsLoading] = useState(false); 34 | const [query, setQuery] = useState(''); 35 | 36 | const searchPosts = async (type, nextToken = null) => { 37 | console.log('searchPosts called: ' + query) 38 | if (query === '') return; 39 | const res = await API.graphql(graphqlOperation(searchPostsGql, { 40 | filter: { content: { matchPhrase: query } }, 41 | limit: 20, 42 | nextToken: nextToken, 43 | })); 44 | console.log(res); 45 | dispatch({ type: type, posts: res.data.searchPosts.items }) 46 | setNextToken(res.data.searchPosts.nextToken); 47 | setIsLoading(false); 48 | } 49 | 50 | const getAdditionalPosts = () => { 51 | if (nextToken === null) return; //Reached the last page 52 | searchPosts(ADDITIONAL_QUERY, nextToken); 53 | } 54 | 55 | const handleChange = event => { 56 | setQuery(event.target.value); 57 | }; 58 | 59 | return ( 60 | 61 | 64 | 71 | 81 | 89 | 90 | } 91 | /> 92 | 93 | ) 94 | } 95 | 96 | export const searchPostsGql = /* GraphQL */ ` 97 | query SearchPosts( 98 | $filter: SearchablePostFilterInput 99 | $sort: SearchablePostSortInput 100 | $limit: Int 101 | $nextToken: String 102 | ) { 103 | searchPosts( 104 | filter: $filter 105 | sort: $sort 106 | limit: $limit 107 | nextToken: $nextToken 108 | ) { 109 | items { 110 | type 111 | id 112 | content 113 | owner 114 | } 115 | nextToken 116 | total 117 | } 118 | } 119 | `; -------------------------------------------------------------------------------- /content/60_full_text_search/10_searchable_front_end/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Full-text search: Front-end" 3 | date = 2020-03-18T10:09:53+09:00 4 | weight = 2 5 | pre = "6.2. " 6 | +++ 7 | 8 | Now that you have created a full-text search API. 9 | Let's implement the front end using it. 10 | 11 | {{< figure src="/images/60_full_text_search/front.png" title="Front-end" class="center" width="100%">}} 12 | 13 | 1. Create a search interface on the right side of the screen (`Search.js`) 14 | 1. Add path of ` https://example.com/#/search` to access the search screen (`App.js`). 15 | 1. Add a link to the search screen in the left navigation (`Sidebar.js`) 16 | 17 | ### Search.js 18 | Create `./src/containers/Search.js `and edit it. 19 | 20 | ```bash 21 | touch ./src/containers/Search.js 22 | ``` 23 | 24 | Copy the contents of `Search.js` below and replace `./src/containers/Search.js` with it. 25 | 26 | {{%attachments title="./src/containers/Search.js" pattern="Search.js"/%}} 27 | 28 | The main points are the following: 29 | 30 | ```js 31 | const searchPosts = async (type, nextToken = null) => { 32 | if (query === '') return; 33 | 34 | const res = await API.graphql(graphqlOperation(searchPostsGql, { 35 | filter: { content: { matchPhrase: query } }, 36 | limit: 20, 37 | nextToken: nextToken, 38 | })); 39 | 40 | dispatch({ type: type, posts: res.data.searchPosts.items }) 41 | setNextToken(res.data.searchPosts.nextToken); 42 | setIsLoading(false); 43 | } 44 | ``` 45 | - Do not execute a query if the contents of `query` is empty. 46 | - `API.graphql (... ` uses `matchPhrase` that is the same as you tried in the AppSync Management Console. 47 | 48 | ### Add Access to Search Components 49 | #### Changes to App.js 50 | Copy the contents of `App.js` below and replace `./src/App.js` with it. 51 | 52 | {{%attachments title="./src/App.js" pattern="App.js"/%}} 53 | 54 | ```jsx 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | ``` 65 | 66 | - Changed to render the `Search` component when access `/search`. 67 | 68 | #### Sidebar.js Changes 69 | Copy the contents of `Sidebar.js` below and replace `./src/containers/Sidebar.js` with it. 70 | 71 | {{%attachments title="./src/containers/Sidebar.js" pattern="Sidebar.js"/%}} 72 | 73 | The points are as follows: 74 | 75 | ```js 76 | { 80 | Auth.currentAuthenticatedUser().then((user) => { 81 | history.push('search'); 82 | }) 83 | }} 84 | key='search' 85 | > 86 | 87 | 88 | 89 | 90 | 91 | ``` 92 | 93 | - Added a new link to `/search` to sidebar. 94 | 95 | ### Applying Changes 96 | 97 | ```bash 98 | $ amplify publish 99 | ``` 100 | 101 | {{% notice warning%}} 102 | If you execute `$ amplify push` or `$ amplify publish` while `$amplify mock api` is running, 103 | sometimes your command execution fail with error message: `Parameters: [authRoleName] must have values`. 104 | In that case, please interrupt the process of `$amplify mock api` with `Ctrl+C`, and then execute `amplify push` or `$amplify publish`. 105 | {{% /notice%}} 106 | 107 | ### Operation Check 108 | - Open the hosted web application and click Search on the left. 109 | - Enter the search word and click on the `SEARCH` button. 110 | - Success if the result is displayed. -------------------------------------------------------------------------------- /content/60_full_text_search/10_searchable_front_end/_index.ja.files/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Amplify from 'aws-amplify'; 3 | import { AmplifyAuthenticator, AmplifySignUp } from '@aws-amplify/ui-react'; 4 | import { AuthState, onAuthUIStateChange } from '@aws-amplify/ui-components'; 5 | import awsconfig from './aws-exports'; 6 | 7 | import { 8 | HashRouter, 9 | Switch, 10 | Route, 11 | Redirect, 12 | } from 'react-router-dom'; 13 | 14 | import { makeStyles, createMuiTheme, ThemeProvider } from '@material-ui/core/styles'; 15 | import CssBaseline from '@material-ui/core/CssBaseline'; 16 | 17 | import AllPosts from './containers/AllPosts'; 18 | import PostsBySpecifiedUser from './containers/PostsBySpecifiedUser'; 19 | import Timeline from './containers/Timeline'; 20 | import Search from './containers/Search'; 21 | 22 | Amplify.configure(awsconfig); 23 | 24 | const drawerWidth = 240; 25 | 26 | const theme = createMuiTheme({ 27 | palette: { 28 | type: 'dark', 29 | primary: { 30 | main: '#1EA1F2', 31 | contrastText: "#fff", 32 | }, 33 | background: { 34 | default: '#15202B', 35 | paper: '#15202B', 36 | }, 37 | divider: '#37444C', 38 | }, 39 | overrides: { 40 | MuiButton: { 41 | color: 'white', 42 | }, 43 | }, 44 | typography: { 45 | fontFamily: [ 46 | 'Arial', 47 | ].join(','), 48 | }, 49 | status: { 50 | danger: 'orange', 51 | }, 52 | }); 53 | 54 | const useStyles = makeStyles(theme => ({ 55 | root: { 56 | display: 'flex', 57 | height: '100%', 58 | width: 800, 59 | marginLeft: 'auto', 60 | marginRight: 'auto', 61 | }, 62 | appBar: { 63 | marginLeft: drawerWidth, 64 | }, 65 | drawer: { 66 | width: drawerWidth, 67 | flexShrink: 0, 68 | }, 69 | drawerPaper: { 70 | width: drawerWidth, 71 | }, 72 | toolbar: theme.mixins.toolbar, 73 | content: { 74 | flexGrow: 1, 75 | backgroundColor: theme.palette.background.default, 76 | padding: theme.spacing(3), 77 | }, 78 | })); 79 | 80 | const App = () => { 81 | const [authState, setAuthState] = React.useState(); 82 | const [user, setUser] = React.useState(); 83 | 84 | const classes = useStyles(); 85 | 86 | React.useEffect(() => { 87 | return onAuthUIStateChange((nextAuthState, authData) => { 88 | setAuthState(nextAuthState); 89 | setUser(authData) 90 | }); 91 | }, []); 92 | 93 | return authState === AuthState.SignedIn && user ? ( 94 |
95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
108 | ) : ( 109 | 110 | 118 | 119 | ); 120 | } 121 | 122 | export default App; -------------------------------------------------------------------------------- /content/60_full_text_search/10_searchable_front_end/_index.ja.files/Search.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useReducer } from 'react'; 2 | 3 | import {API, graphqlOperation } from 'aws-amplify'; 4 | 5 | import { 6 | Button, 7 | TextField, 8 | } from '@material-ui/core'; 9 | 10 | import PostList from '../components/PostList'; 11 | import Sidebar from './Sidebar'; 12 | 13 | const SUBSCRIPTION = 'SUBSCRIPTION'; 14 | const INITIAL_QUERY = 'INITIAL_QUERY'; 15 | const ADDITIONAL_QUERY = 'ADDITIONAL_QUERY'; 16 | 17 | const reducer = (state, action) => { 18 | switch (action.type) { 19 | case INITIAL_QUERY: 20 | return action.posts; 21 | case ADDITIONAL_QUERY: 22 | return [...state, ...action.posts] 23 | case SUBSCRIPTION: 24 | return [action.post, ...state] 25 | default: 26 | return state; 27 | } 28 | }; 29 | 30 | export default function Search() { 31 | const [posts, dispatch] = useReducer(reducer, []); 32 | const [nextToken, setNextToken] = useState(null); 33 | const [isLoading, setIsLoading] = useState(false); 34 | const [query, setQuery] = useState(''); 35 | 36 | const searchPosts = async (type, nextToken = null) => { 37 | console.log('searchPosts called: ' + query) 38 | if (query === '') return; 39 | const res = await API.graphql(graphqlOperation(searchPostsGql, { 40 | filter: { content: { matchPhrase: query } }, 41 | limit: 20, 42 | nextToken: nextToken, 43 | })); 44 | console.log(res); 45 | dispatch({ type: type, posts: res.data.searchPosts.items }) 46 | setNextToken(res.data.searchPosts.nextToken); 47 | setIsLoading(false); 48 | } 49 | 50 | const getAdditionalPosts = () => { 51 | if (nextToken === null) return; //Reached the last page 52 | searchPosts(ADDITIONAL_QUERY, nextToken); 53 | } 54 | 55 | const handleChange = event => { 56 | setQuery(event.target.value); 57 | }; 58 | 59 | return ( 60 | 61 | 64 | 71 | 81 | 89 | 90 | } 91 | /> 92 | 93 | ) 94 | } 95 | 96 | export const searchPostsGql = /* GraphQL */ ` 97 | query SearchPosts( 98 | $filter: SearchablePostFilterInput 99 | $sort: SearchablePostSortInput 100 | $limit: Int 101 | $nextToken: String 102 | ) { 103 | searchPosts( 104 | filter: $filter 105 | sort: $sort 106 | limit: $limit 107 | nextToken: $nextToken 108 | ) { 109 | items { 110 | type 111 | id 112 | content 113 | owner 114 | } 115 | nextToken 116 | total 117 | } 118 | } 119 | `; -------------------------------------------------------------------------------- /content/60_full_text_search/10_searchable_front_end/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "全文検索機能: Front-end" 3 | date = 2020-03-18T10:09:53+09:00 4 | weight = 2 5 | pre = "6.2. " 6 | +++ 7 | 8 | 全文検索用APIが作成できたので、フロントエンドを実装していきます。 9 | 10 | {{< figure src="/images/60_full_text_search/front.png" title="Front-end" class="center" width="100%">}} 11 | 12 | 1. 画面右側の検索用のインタフェースを作成します(`Search.js`) 13 | 1. 検索画面へアクセスするためのURI`https://example.com/#/search`を追加します(`App.js`) 14 | 1. 左側のナビゲーションに検索画面へのリンクを追加します(`Sidebar.js`) 15 | 16 | ### Search.js 17 | `./src/containers/Search.js`を作成し、編集していきます。 18 | 19 | ```bash 20 | touch ./src/containers/Search.js 21 | ``` 22 | 23 | 以下の`Search.js`の中身をコピーし、`./src/containers/Search.js`を置き換えてください。 24 | 25 | {{%attachments title="./src/containers/Search.js" pattern="Search.js"/%}} 26 | 27 | 主なポイントは以下です。 28 | 29 | ```js 30 | const searchPosts = async (type, nextToken = null) => { 31 | if (query === '') return; 32 | 33 | const res = await API.graphql(graphqlOperation(searchPostsGql, { 34 | filter: { content: { matchPhrase: query } }, 35 | limit: 20, 36 | nextToken: nextToken, 37 | })); 38 | 39 | dispatch({ type: type, posts: res.data.searchPosts.items }) 40 | setNextToken(res.data.searchPosts.nextToken); 41 | setIsLoading(false); 42 | } 43 | ``` 44 | 45 | - `query`の中身が空であればクエリを実行しません 46 | - `API.graphql(...`では、AppSyncのマネジメントコンソールで試したのと同じ、`matchPhrase`を利用しています 47 | 48 | ### Searchコンポーネントへのアクセスを追加 49 | #### App.jsの変更 50 | 以下の`App.js`の中身をコピーし、`./src/App.js`を置き換えてください。 51 | 52 | {{%attachments title="./src/App.js" pattern="App.js"/%}} 53 | 54 | ```jsx 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | ``` 65 | 66 | - `/search`へアクセスがあった場合、先ほど実装した`Search`コンポーネントをレンダリングするよう変更しました 67 | 68 | 69 | #### Sidebar.jsの変更 70 | 以下の`Sidebar.js`の中身をコピーし、`./src/containers/Sidebar.js`を置き換えてください。 71 | 72 | {{%attachments title="./src/containers/Sidebar.js" pattern="Sidebar.js"/%}} 73 | 74 | ポイントは以下です。 75 | 76 | ```js 77 | { 81 | Auth.currentAuthenticatedUser().then((user) => { 82 | history.push('search'); 83 | }) 84 | }} 85 | key='search' 86 | > 87 | 88 | 89 | 90 | 91 | 92 | ``` 93 | 94 | - 新たに`/search`へのリンクをサイドバーへ追加しました 95 | 96 | ### 変更の反映 97 | 98 | ```bash 99 | $ amplify publish 100 | ``` 101 | 102 | {{% notice warning %}} 103 | `$ amplify mock api`を動かしている状態で`$ amplify push`や`$ amplify publish`を実行した場合、`Parameters: [authRoleName] must have values`というエラーがでて`$ amplify push`や`$ amplify publish`が失敗する場合があります。 104 | その場合は、`Ctrl + C`で`$ amplify mock api`のプロセスを中断してから、`$ amplify push`や`$ amplify publish`を実行してください。 105 | {{% /notice %}} 106 | 107 | 108 | ### 動作確認 109 | - ホストされたウェブアプリケーションを開き、左側のSearchをクリックします 110 | - 検索ワードを入力し、`SEARCH`ボタンをクリックします 111 | - 結果が表示されれば成功です -------------------------------------------------------------------------------- /content/60_full_text_search/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Add full text search" 3 | date = 2020-03-24T10:09:52+09:00 4 | weight = 6 5 | chapter = true 6 | pre = "6. " 7 | +++ 8 | 9 | 10 | ### Story 11 | Users love the Timeline/follow feature. 12 | On the other hand, you have received feedback that besides Global Timeline, users also want the feature to reach users of interest. 13 | Integrate full-text search into your application so that users can search for Post with interested words. 14 | 15 | ### What implements in this section 16 | - Add full-text search function to Post using `@searchable`. 17 | 18 | {{< figure src="/images/60_full_text_search/architecture.png" title="Back-end" class="center" width="50%">}} 19 | 20 | {{< figure src="/images/60_full_text_search/front.png" title="Front-end" class="center" width="100%">}} 21 | 22 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/60_full_text_search/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "全文検索機能の追加" 3 | date = 2020-03-24T10:09:52+09:00 4 | weight = 6 5 | chapter = true 6 | pre = "6. " 7 | +++ 8 | 9 | ### Story 10 | Timeline/Follow機能をリリースした結果、ユーザーからは非常に好評でした。 11 | その一方、Global Timeline以外にも、自分の興味のあるユーザーにリーチする機能がほしいというフィードバックが上がっています。 12 | アプリケーションに全文検索機能を組み込み、ユーザーが興味のあるワードが含まれたPostを検索できるようにしましょう。 13 | 14 | ### 本セクションで実装するもの 15 | - `@searchable`を利用した全文検索機能をPostに追加 16 | 17 | {{< figure src="/images/60_full_text_search/architecture.png" title="Back-end" class="center" width="50%">}} 18 | 19 | {{< figure src="/images/60_full_text_search/front.png" title="Front-end" class="center" width="100%">}} 20 | 21 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/70_multi_env/10_amplify_console/connect_github.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "GitHubと連携してCI/CD環境を構築する(1)" 3 | date = 2020-03-24T10:09:46+09:00 4 | weight = 2 5 | pre = "7.2. " 6 | +++ 7 | 8 | 今までは、アプリケーションをデプロイする度に、ローカル環境で`amplify publish`コマンドを発行していました。これから紹介する[Amplify Console](https://aws.amazon.com/jp/amplify/console/)を用いることで、GitHub と連携した CI/CD 環境の構築を容易に行うことができます。 9 | 10 | ### Amplify Console とは? 11 | 12 | Amplify Console は SPA(シングルページアプリケーション)や静的サイトのホスティング、CI/CD の運用を自動化するマネージドサービスです。 GitHub や AWS CodeCommit のようなソースリポジトリと連携し、CI/CD の仕組みを数クリックで構築することができます。また、ブランチごとにテスト環境を自動で構築したり、特定の環境のみ Basic 認証を付与するといった、柔軟な開発フローの設計が可能なサービスです。 Amplify Console と銘打っていますが、静的サイトであれば Amplify を用いなくても使用できるサービスです。 13 | 14 | ![](/images/07_multi_env/amplify_console.png) 15 | 16 | ### GitHub にソースコードを push する 17 | 18 | ここまで作ってきたソースコードを GitHub に Push しましょう。GitHub の操作方法について熟知されている方は、「GitHub にソースコードを push する」の章は読み飛ばしていただいて構いません。ここでは、GitHub にソースを Push する方法について解説します。 19 | 20 | {{% notice info %}} 21 | リポジトリの種類には、プライベートリポジトリを選択してください。この後に紹介する Previews 機能(プルリク毎に自動的に環境を複製する機能)はセキュリティの観点からプライベートリポジトリのみ提供されます。パブリックリポジトリの場合も、その他の機能はご利用いただくことが可能です。 22 | {{% /notice %}} 23 | 24 | まず GitHub のアカウントにログインし、画面右上の「+」ボタンから「New repository」を選択します。 25 | 26 | {{< figure src="/images/07_multi_env/new_repository.png" title="" class="left" width="320" >}} 27 | 28 | 「Create a new repository」で 任意の Repository name を入力し、リポジトリを作成します。この時、「Private」を指定することに注意してください。「Private」をどうしても選択できない場合は「Public」でリポジトリを作成します。 29 | 30 | ![](/images/07_multi_env/create_new_repository.png) 31 | 32 | リポジトリが作成できたら、GitHub の URL をコピーします。この時、「SSH」が選択されていることに注意してください。 33 | 34 | ![](/images/07_multi_env/copy_ssh_git_url.png) 35 | 36 | アプリケーションのトップディレクトリで以下のコマンドを実行し、GitHub にソースコードを push します。`git remote add origin <コピーしたgitのURL>`は先ほどコピーした git の URL に置き換えてください。 37 | 38 | ```sh 39 | rm -rf .git <-- 念の為.gitフォルダを削除 40 | git init 41 | git add . 42 | git commit -m "first commit" 43 | git remote add origin <コピーしたgitのURL> 44 | git push -u origin master 45 | ``` 46 | 47 | GitHub のページをリロードして、ソースコードが更新されていれば push は成功です。 48 | 49 | ![](/images/07_multi_env/success_to_push.png) 50 | 51 | ### 補足:「Permission denied」でソースコードの push に失敗する場合 52 | 53 | 端末で GitHub の設定が完了していない場合、「Permission denied」で push に失敗することがあります。その場合は、以下の手順を実施してから再度 push コマンドを実行してください。 54 | 55 | 公開鍵・秘密鍵のペアを作成します。途中いくつか質問されますが、全てデフォルトで Enter を押してください。 56 | 57 | ```sh 58 | cd ~/.ssh 59 | ssh-keygen -t rsa 60 | ``` 61 | 62 | `~/.ssh/id_rsa.pub` というファイルが作成されるので、このファイルの中身をコピーします。 63 | 64 | ```sh 65 | cat id_rsa.pub 66 | 67 | ssh-rsa ... から始まる文字列をコピーする 68 | 69 | ``` 70 | 71 | [https://github.com/settings/ssh/new](https://github.com/settings/ssh/new) にアクセスし、「Title」と「key」を入力します。Title にはアクセスする端末を特定するわかりやすい名前を入力してください。「Key」には先ほどコピーした「ssh-rsa ...」から始まる文字列を貼り付けてください。「Add SSH key」を押下すると、公開鍵を登録することができます。 72 | 73 | ![](/images/07_multi_env/add_ssh_key.png) 74 | 75 | この状態で再度、push コマンドを発行してください。 76 | 77 | ```sh 78 | git push -u origin master 79 | ``` 80 | -------------------------------------------------------------------------------- /content/70_multi_env/30_preview_autodetection/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Building complex development flows #2" 3 | date = 2020-05-05T14:36:37+09:00 4 | weight = 5 5 | pre = "7.5. " 6 | +++ 7 | 8 | ### Hosting pull requests 9 | 10 | Amplify Console has Preview feature that provides automatic hosting functionality for not only branches but also pull request. This feature makes reviewers able to review source code while seeing actual screen. For this time, configure automatic hosting setting against pull requests published to "develop" branch. 11 | 12 | {{% notice info %}} 13 | You can use Preview feature for private repositories only from perspective of security. Also, only GitHub supports the feature as of April 2020. 14 | {{% /notice %}} 15 | 16 | Please click "Preview" > "Enable Preview" on Amplify Console screen. 17 | 18 | ![](/images/07_multi_env/enable_preview.png) 19 | 20 | Then click "Install GitHub app". 21 | 22 | ![](/images/07_multi_env/install_preview_dialog.png) 23 | 24 | 25 | You move to GitHub. Then choose a repository you want to install Preview feature and press "Save". 26 | 27 | ![](/images/07_multi_env/install_preview.png) 28 | 29 | Once get back to Amplify Console, you can see a list of branches in the repository you selected. For this moment, let's choose "develop" branch and press "Manage" button. 30 | 31 | ![](/images/07_multi_env/select_preview_branch.png) 32 | 33 | Enable "Pull Request Preview" and choose "staging" for "Pull Request Previews - backend environment". 34 | 35 | ![](/images/07_multi_env/preview_settings.png) 36 | 37 | {{% notice tip %}} 38 | If you choose "Create new backend environment for every Pull Request" for "Pull Request Preview - backend environment", you also can build dedicated backend for each pull request. 39 | {{% /notice %}} 40 | 41 | Then configuration on Amplify Console is completed. Let's send pull request to develop branch. Create a new branch and edit something for test. As a simple example for this time, please change the text on logout button from "Logout" to "Log out" in `Sidebar.js`. 42 | 43 | 44 | ```sh 45 | git checkout -b chore/edit-logout-button-text 46 | ``` 47 | 48 | ```Sidebar.jsx 49 | 50 | 56 | Log out // <-- Changed from "Logout" to "Log out" 57 | 58 | } /> 59 | ``` 60 | 61 | ```sh 62 | git add . 63 | git commit -m "Edit Logout Button text" 64 | git push --set-upstream origin chore/edit-logout-button-text 65 | ``` 66 | 67 | Then open a pull request for the develop branch. 68 | 69 | ![](/images/07_multi_env/open_pull_request.png) 70 | 71 | After the pull request completion, you can see "AWS Amplify Console Web Preview". Just after pull request submitted, the status is "In Progress" because the hosting process is not completed. 72 | 73 | 74 | ![](/images/07_multi_env/pull_request_amplify_link.png) 75 | 76 | Once the deployment is completed, the status is changed to "Success". 77 | 78 | ![](/images/07_multi_env/success_to_deploy_preview.png) 79 | 80 | Click on the "Detail" link and move to summary screen. You can move to the screen of the hosted app by clicking "View more details on AWS Amplify (us-east-1)". 81 | 82 | ![](/images/07_multi_env/previews_detail.png) 83 | 84 | You can also see a list of pull requests for the branches on the Previews screen on Amplify Console. Once a pull request is merged, the one is removed from the list as well. 85 | 86 | ![](/images/07_multi_env/previews_pr_list.png) 87 | -------------------------------------------------------------------------------- /content/70_multi_env/30_preview_autodetection/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "複雑な開発環境の構築(2)" 3 | date = 2020-03-24T10:09:46+09:00 4 | weight = 5 5 | pre = "7.5. " 6 | +++ 7 | 8 | ### プルリクエストをホスティングする 9 | 10 | Amplify Console では、ブランチだけでなくプルリクエストのソースコードを自動的にホスティングする Preview 機能があります。この機能により、レビュー担当者が実際に画面を触りながらコードをレビューすることが可能になります。今回は、develop ブランチに対して発行されたプルリクエストを自動的にホスティングする設定を行なっていきましょう。 11 | 12 | {{% notice info %}} 13 | Preview 機能はセキュリティの観点からプライベートリポジトリにのみ有効です。また、2020/04 現在で GitHub にのみ対応しています。 14 | {{% /notice %}} 15 | 16 | Amplify Console の画面から「Preview」> 「Enable Preview」を選択します。 17 | 18 | ![](/images/07_multi_env/enable_preview.png) 19 | 20 | 「Install GitHub app」を押下します。 21 | 22 | ![](/images/07_multi_env/install_preview_dialog.png) 23 | 24 | GitHub の画面に遷移するので、Preview 機能をインストールするリポジトリを選択し、「Save」を押下します。 25 | 26 | ![](/images/07_multi_env/install_preview.png) 27 | 28 | Amplify Console の画面に戻ると、対象リポジトリのブランチ一覧が表示されています。今回は、develop ブランチを選択し、「Manage」ボタンを押下します。 29 | 30 | ![](/images/07_multi_env/select_preview_branch.png) 31 | 32 | 「Pull Request Preview」を ON にして、「Pull Request Preview - backend environment」に staging を指定します。 33 | ![](/images/07_multi_env/preview_settings.png) 34 | 35 | {{% notice tip %}} 36 | Pull Request Preview - backend environment の設定を「Create new backend environment for every Pull Request」にすると、プルリクごとに個別のバックエンドを構築することも可能です。 37 | {{% /notice %}} 38 | 39 | これで Amplify Console 側の設定は完了です。develop ブランチにプルリクエストを発行してみましょう。新しいブランチを発行し、簡単な修正を行います。変更のわかりやすい例として、`Sidebar.js`のログアウトボタンのテキストを「Logout」から「ログアウト」としてみましょう 40 | 41 | ```sh 42 | git checkout -b chore/edit-logout-button-text 43 | ``` 44 | 45 | ```Sidebar.jsx 46 | 47 | 53 | ログアウト // <-- 「ログアウト」に変更 54 | 55 | } /> 56 | ``` 57 | 58 | ```sh 59 | git add . 60 | git commit -m "Edit Logout Button text" 61 | git push --set-upstream origin chore/edit-logout-button-text 62 | ``` 63 | 64 | develop ブランチに対しプルリクエストを発行します。 65 | 66 | ![](/images/07_multi_env/open_pull_request.png) 67 | 68 | プルリクエストを発行すると、「AWS Amplify Console Web Preview」が表示されます。プルリクエストを発行した直後は、ホスティングが完了していないため「In progress」のステータスになっています。 69 | 70 | ![](/images/07_multi_env/pull_request_amplify_link.png) 71 | 72 | デプロイが完了すると、ステータスが「Success」になります。 73 | 74 | ![](/images/07_multi_env/success_to_deploy_preview.png) 75 | 76 | 「Detail」リンクをクリックし、Summary 画面に遷移します。「View more details on AWS Amplify (us-east-1)」をクリックすると、ホスティングされたアプリケーションの画面に遷移することができます。 77 | 78 | ![](/images/07_multi_env/previews_detail.png) 79 | 80 | Amplify Console の Previews 画面でも対象のプルリクエストの一覧を確認できます。プルリクエストがマージされると、一覧からも削除されます。 81 | 82 | ![](/images/07_multi_env/previews_pr_list.png) 83 | -------------------------------------------------------------------------------- /content/70_multi_env/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Developing with your team" 3 | date = 2020-05-04T15:08:21+09:00 4 | weight = 7 5 | chapter = true 6 | pre = "7. " 7 | +++ 8 | 9 | ### Story 10 | 11 | Your business has been going well and the organization of engineers has been growing up. In line with that, you are getting to need some better manner to develop with multiple members efficiently. Let's build an environment that is great for team development by connecting GitHub with AWS Amplify! 12 | 13 | {{% notice info %}} 14 | If you start this workshop from this section, please look at [10.1 If you want to start from section 5 or 7](/en/100_supplemental_resource/20_start_from_day23.html). You can get how to skip previous contents there. 15 | {{% /notice %}} 16 | 17 | ### What you build in this section 18 | 19 | {{% children showhidden="false" %}}{{% /children%}} 20 | 21 | ![](/images/07_multi_env/multi_env.png) 22 | -------------------------------------------------------------------------------- /content/70_multi_env/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "複数メンバーでの開発" 3 | date = 2020-03-18T10:09:52+09:00 4 | weight = 7 5 | chapter = true 6 | pre = "7. " 7 | +++ 8 | 9 | ### Story 10 | 11 | あなたの事業は順調に成長し、同時にエンジニア組織も拡大し始めました。それに伴い、複数のメンバーで効率的に開発ができる仕組みが必要になってきました。GitHub と Amplify をうまく連携させて、チーム開発を行うための基盤を構築していきましょう! 12 | 13 | ### 本セクションで構築するもの 14 | 15 | {{% children showhidden="false" %}}{{% /children%}} 16 | 17 | ![](/images/07_multi_env/multi_env.png) 18 | -------------------------------------------------------------------------------- /content/80_e2e_test/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "End to End(E2E) Testing" 3 | date = 2020-05-05T15:09:48+09:00 4 | weight = 8 5 | chapter = true 6 | pre = "8. " 7 | +++ 8 | 9 | ### Story 10 | 11 | The more you have added new features to your application, you feel difficulty to keep the quality high. So, you decided to implement E2E(End to End) Test as part of quality improvement plans. 12 | 13 | ### What you build at this chapter 14 | 15 | {{% children showhidden="false" %}}{{% /children%}} 16 | 17 | ![](/images/07_multi_env/e2e.png) 18 | -------------------------------------------------------------------------------- /content/80_e2e_test/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "E2Eテスト" 3 | date = 2020-03-18T10:09:52+09:00 4 | weight = 8 5 | chapter = true 6 | pre = "8. " 7 | +++ 8 | 9 | ### Story 10 | 11 | アプリケーションの機能が増えてくると同時に安定した品質の担保が難しくなってきました。あなたは、品質向上の取り組みの一環として E2E(End to End) Test を導入することに決めました。 12 | 13 | ### 本セクションで構築するもの 14 | 15 | {{% children showhidden="false" %}}{{% /children%}} 16 | 17 | ![](/images/07_multi_env/e2e.png) 18 | -------------------------------------------------------------------------------- /content/90_summary/10_clean_up/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "cleanup" 3 | date = 2020-03-18T10:09:55+09:00 4 | weight = 2 5 | pre = "9.1. " 6 | +++ 7 | 8 | ### Amplify-Related Resources 9 | 10 | Navigate to the root directory of your application and run the following command: 11 | 12 | ``` 13 | $ amplify delete 14 | ``` 15 | 16 | ### Cloud9 17 | If you use AWS Cloud9, delete your AWS Cloud9 environment as well. Select the environment you created and press the "Delete" button. 18 | 19 | ![Cloud9 Cleanup](/images/90_summary/delete_cloud9.png) -------------------------------------------------------------------------------- /content/90_summary/10_clean_up/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "後片付け" 3 | date = 2020-03-18T10:09:55+09:00 4 | weight = 2 5 | pre = "9.1. " 6 | +++ 7 | 8 | ### Amplify 関連のリソース 9 | 10 | アプリケーションのルートディレクトリに移動し、次のコマンドを実行します。 11 | 12 | ``` 13 | $ amplify delete 14 | ``` 15 | 16 | ### Cloud9 17 | 18 | Cloud9 を利用した場合、Cloud9 の環境も削除します。作成した環境を選択し、「Delete」ボタンを押下します。 19 | 20 | ![Cloud9 Cleanup](/images/90_summary/delete_cloud9.png) 21 | -------------------------------------------------------------------------------- /content/90_summary/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Closing" 3 | date = 2020-03-18T10:09:54+09:00 4 | weight = 9 5 | chapter = true 6 | pre = "9. " 7 | +++ 8 | 9 | 10 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/90_summary/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "終わりに" 3 | date = 2020-03-18T10:09:54+09:00 4 | weight = 9 5 | chapter = true 6 | pre = "9. " 7 | +++ 8 | 9 | {{% children showhidden="false" %}}{{% /children%}} -------------------------------------------------------------------------------- /content/_index.en.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Amplify Social Network App Workshop" 3 | date = 2020-03-18T10:09:42+09:00 4 | weight = 1 5 | +++ 6 | 7 | Welcome to Amplify Social Network App(or SNS) Workshop! In this workshop, you will learn about AWS Amplify in hands-on way through the development of twitter-like social media applications. 8 | 9 | ## Audience 10 | - Application developer who wants to develop at explosion speed 11 | - Server-side engineers who want to get started with front-end development 12 | 13 | ## Overall Workshop 14 | This workshop assumes the situation by the procurement stage of startups (Seed, Early, Middle/Late, etc.). 15 | This hands-on application can be created as shown in the image below. 16 | ![](./final_app_image.png) 17 | 18 | ### Stage 1 - Seed Stage (Chapter 1 - Chapter 4) 19 | #### Develop the app as the first engineer/CTO of startup! 20 | You are the first developer of startup. You decided to help the CEO develop social media applications, but you only have three days to explain to investors (Pitch/DemoDay). It is enough to implement the minimum required functionality, but you need to launch at the fastest possible time. 21 | 22 | #### What you can learn 23 | As you become familiar with the basics of Amplify, you will learn how to use User Authentication, GraphQL to implement timelines, and how to use Amplify Mocking. You will also understand that Amplify not only allows you to implement your application at the fastest time, but also supports future scalability. 24 | 25 | ### Stage 2 - Early Stage (Chapter 5 - Chapter 6) 26 | #### Acquire a certain number of users and respond to the growing challenges and feature demands! 27 | 28 | We implemented and released a minimum of functionality, and succeeded in acquiring a certain number of users. Since it was built with Amplify, it is not necessary to manage servers. However it is necessary to answer the challenges and functional needs that have become clear, such as fixing the validation that was done on the front end and improving searchability. 29 | 30 | #### What you can learn 31 | Learn how to incorporate the high searchability of Elasticsearch into applications using GraphQL, and how to enforce input restrictions on the cloud side when using GraphQL. 32 | 33 | ### Stage 3 - Middle Stage (Chapter 7 - Chapter 8) 34 | #### Businesses are on track and users and engineers are growing rapidly 35 | 36 | As a result of continued expansion, the business goes well and at the same time the engineering organization began to expand. As multiple feature developments have progressed in parallel, you need to think about efficient team development. 37 | 38 | #### What you can learn 39 | 40 | Through the addition of functions, we will learn how to develop a team using multiple Amplify backend environments and build a verification environment for each function using Amplify Console. 41 | 42 | ## Recommended Environment 43 | 44 | - Development Environment 45 | - "Node.js 10.x/npm 6.x" available for Mac/Windows/Linux 46 | - The operating environment is checked in v12.16.1/v6.13.4 47 | - Please note that for Cloud IDEs such as AWS Cloud9, port 20002 is not available during Amplify Mocking. 48 | - Browser environment 49 | - Google Chrome (most recent 2 versions) 50 | - Mozilla Firefox (most recent 2 versions) 51 | - Internet Explorer v11 52 | - Microsoft Edge (most recent 2 versions) -------------------------------------------------------------------------------- /content/_index.ja.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Amplify SNS Workshop" 3 | date = 2020-03-18T10:09:42+09:00 4 | weight = 1 5 | +++ 6 | 7 | Amplify SNS Workshop へようこそ!本ワークショップでは Twitter ライクなソーシャルメディアアプリケーションの開発を通して、実践的に AWS Amplify について学ぶことが出来ます。 8 | 9 | ## 対象者 10 | - 爆速で開発したいアプリ開発者 11 | - フロントエンド開発に入門したいサーバーサイドエンジニア 12 | 13 | ## ワークショップ全体像 14 | 本ワークショップは、スタートアップの調達ステージ別(シード/アーリー/ミドル/レイターなど)のシチュエーションを想定し進めていきます。 15 | 本ハンズオンでは以下イメージのようなアプリケーションが出来上がります。 16 | ![](./final_app_image.png) 17 | 18 | ### Stage 1 - シードステージ(第1章 ~ 第4章) 19 | #### スタートアップ1人目のエンジニア/CTOとして、アプリを開発する! 20 | あなたはスタートアップ1人目の開発者です。CEOと相談をしソーシャルメディアアプリケーションを開発することが決まりましたが、投資家への説明(Pitch/DemoDay)まではあと3日しかありません。必要最低限の機能でいいものの、最速での立ち上げが求められています。 21 | 22 | #### 学習できること 23 | Amplifyの基礎的な使い方に慣れつつ、ユーザ認証機能や GraphQL を用いたタイムラインの実装、Amplify Mocking の使い方を学びます。また、Amplify が最速でアプリケーションを実装できるだけでなく、将来的なスケーラビリティまでサポートすることを理解します。 24 | 25 | ### Stage 2 - アーリーステージ(第5章 ~ 第6章) 26 | #### ユーザを一定数獲得し、増え始めた課題や機能要望に対応する! 27 | 28 | ひとまず最低限の機能を実装しリリースし、一定数のユーザを獲得することに成功しました。Amplify で構築したためサーバの運用は不要なものの、止むを得ずフロントエンドで行っていたバリデーションの修正や、検索性の向上など、明確になってきた課題や機能要望に答える必要があります。 29 | 30 | #### 学習できること 31 | Elasticsearch による高い検索性を GraphQL を利用したアプリケーションに組み込む方法や、GraphQL 利用時にクラウド側で入力規制等を行う方法を学びます。 32 | 33 | ### Stage 3 - ミドルステージ(第7章 - 第8章) 34 | #### 事業が軌道に乗りユーザもエンジニアも急拡大 35 | 36 | 機能拡充を続けた結果ビジネスは起動に乗り始め、同時にエンジニア組織も拡大し始めました。並行して複数の機能開発が進むことも増えたため、効率の良いチーム開発について考える必要があります。 37 | 38 | #### 学習できること 39 | 40 | 実際に機能追加を通して、複数の Amplify バックエンド環境を用いるチーム開発や、Amplify Console を用いた機能毎の検証環境の構築について学びます。 41 | 42 | ## 推奨環境 43 | 44 | - 開発環境 45 | - "Node.js 10.x / npm 6.x" が利用可能な Mac/Windows/Linux 46 | - 動作環境の確認は v12.16.1/v6.13.4 で行なっています 47 | - AWS Cloud9 のようなクラウド IDE の場合、Amplify Mocking の際に 20002 番ポートが利用出来ないケースがあるのでご注意ください 48 | - ブラウザ環境 49 | - Google Chrome (most recent 2 versions) 50 | - Mozilla Firefox (most recent 2 versions) 51 | - Internet Explorer v11 52 | - Microsoft Edge (most recent 2 versions) -------------------------------------------------------------------------------- /content/final_app_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/content/final_app_image.png -------------------------------------------------------------------------------- /layouts/index.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "pageTitle": "Index", 3 | "relativePagePath": "/{{ $.Site.Language}}/" 4 | },{{ $totalPage := newScratch }}{{ $totalPage.Add "pageCount" 1}}{{ $currentNode := . }}{{ $showvisitedlinks := .Site.Params.showVisitedLinks }}{{range .Site.Home.Sections.ByWeight}}{{ template "section-tree-nav1" dict "parent" .Parent "sect" . "currentnode" $currentNode "showvisitedlinks" $showvisitedlinks "pageCount" 0 "totalPage" $totalPage}}{{end}}{{ define "section-tree-nav1" }}{{ $showvisitedlinks := .showvisitedlinks }}{{ $currentNode := .currentnode }}{{with .sect}}{{if .IsSection}}{{$pageCount := $.totalPage.Get "pageCount"}}{{$res := add 1 $pageCount}}{{$.totalPage.Set "pageCount" $res}}{{ $numberOfPages := (add (len .Pages) (len .Sections)) }}{{ if ne $numberOfPages 0 }}{{ $currentNode.Scratch.Set "pages" .Pages }}{{ if .Sections}}{{ $currentNode.Scratch.Set "pages" (.Pages | union .Sections) }}{{end}}{{ $pages := ($currentNode.Scratch.Get "pages") }}{{if eq .Site.Params.ordersectionsby "title"}} {{ range $pages.ByTitle }}{{ if and .Params.hidden (not $.showhidden) }} {{else}}{{ template "section-tree-nav1" dict "sect" . "currentnode" $currentNode "showvisitedlinks" $showvisitedlinks "totalPage" $.totalPaget }}{{end}}{{ end }}{{else}}{{ range $pages.ByWeight }}{{ if and .Params.hidden (not $.showhidden) }} {{else}}{{ template "section-tree-nav1" dict "sect" . "currentnode" $currentNode "showvisitedlinks" $showvisitedlinks "totalPage" $.totalPage}}{{end}}{{ end }}{{end}}{{ end }}{{else}}{{ if not .Params.Hidden }}{{$pageCount := $.totalPage.Get "pageCount"}}{{$res := add 1 $pageCount}}{{$.totalPage.Set "pageCount" $res}}{{ end }}{{end}}{{ end }}{{ end }}{{ $totalPage2 := newScratch }}{{ $totalPage2.Add "pageCount" 1}}{{ $currentNode := . }}{{ $showvisitedlinks := .Site.Params.showVisitedLinks }}{{range .Site.Home.Sections.ByWeight}}{{ template "section-tree-nav2" dict "parent" .Parent "sect" . "currentnode" $currentNode "showvisitedlinks" $showvisitedlinks "pageCount" 0 "totalPage" $totalPage "totalPage2" $totalPage2}}{{end}}]{{ define "section-tree-nav2" }}{{ $showvisitedlinks := .showvisitedlinks }}{{ $currentNode := .currentnode }}{{with .sect}}{{if .IsSection}}{{$pageCount := $.totalPage2.Get "pageCount"}}{{$res := add 1 $pageCount}}{{$.totalPage2.Set "pageCount" $res}}{{$maxPage := $.totalPage.Get "pageCount"}} 5 | { 6 | "pageTitle": "{{.Title}}", 7 | "relativePagePath": "{{.RelPermalink}}" 8 | }{{if ne $res $maxPage }},{{end}}{{ $numberOfPages := (add (len .Pages) (len .Sections)) }}{{ if ne $numberOfPages 0 }}{{ $currentNode.Scratch.Set "pages" .Pages }}{{ if .Sections}}{{ $currentNode.Scratch.Set "pages" (.Pages | union .Sections) }}{{end}}{{ $pages := ($currentNode.Scratch.Get "pages") }}{{if eq .Site.Params.ordersectionsby "title"}} {{ range $pages.ByTitle }}{{ if and .Params.hidden (not $.showhidden) }} {{else}}{{ template "section-tree-nav2" dict "sect" . "currentnode" $currentNode "showvisitedlinks" $showvisitedlinks "totalPage" $.totalPage "totalPage2" $.totalPage2t }}{{end}}{{ end }}{{else}}{{ range $pages.ByWeight }}{{ if and .Params.hidden (not $.showhidden) }} {{else}}{{ template "section-tree-nav2" dict "sect" . "currentnode" $currentNode "showvisitedlinks" $showvisitedlinks "totalPage" $.totalPage "totalPage2" $.totalPage2}}{{end}}{{ end }}{{end}}{{ end }}{{else}}{{ if not .Params.Hidden }}{{$pageCount := $.totalPage2.Get "pageCount"}}{{$res := add 1 $pageCount}}{{$.totalPage2.Set "pageCount" $res}}{{$maxPage := $.totalPage.Get "pageCount"}} 9 | { 10 | "pageTitle": "{{.Title}}", 11 | "relativePagePath": "{{.RelPermalink}}" 12 | }{{if ne $res $maxPage }},{{end}}{{ end }}{{end}}{{ end }}{{ end }} -------------------------------------------------------------------------------- /layouts/partials/custom-footer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 | 9 | -------------------------------------------------------------------------------- /static/images/00_prequisites/add_authentication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/add_authentication.png -------------------------------------------------------------------------------- /static/images/00_prequisites/add_hosting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/add_hosting.png -------------------------------------------------------------------------------- /static/images/00_prequisites/addingtags.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/addingtags.jpg -------------------------------------------------------------------------------- /static/images/00_prequisites/amplify-configure-new-iam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/amplify-configure-new-iam.png -------------------------------------------------------------------------------- /static/images/00_prequisites/amplify_publish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/amplify_publish.png -------------------------------------------------------------------------------- /static/images/00_prequisites/attachingpolicy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/attachingpolicy.jpg -------------------------------------------------------------------------------- /static/images/00_prequisites/cloud9-tmp-credential-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/cloud9-tmp-credential-off.png -------------------------------------------------------------------------------- /static/images/00_prequisites/confirmationcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/confirmationcode.jpg -------------------------------------------------------------------------------- /static/images/00_prequisites/createnewaccount.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/createnewaccount.jpg -------------------------------------------------------------------------------- /static/images/00_prequisites/creatingiamuser.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/creatingiamuser.jpg -------------------------------------------------------------------------------- /static/images/00_prequisites/creatinguserconfirmation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/creatinguserconfirmation.jpg -------------------------------------------------------------------------------- /static/images/00_prequisites/iam-1-create-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/iam-1-create-user.png -------------------------------------------------------------------------------- /static/images/00_prequisites/iam-2-attach-policy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/iam-2-attach-policy.png -------------------------------------------------------------------------------- /static/images/00_prequisites/iam-3-create-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/iam-3-create-user.png -------------------------------------------------------------------------------- /static/images/00_prequisites/iam-4-save-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/iam-4-save-url.png -------------------------------------------------------------------------------- /static/images/00_prequisites/inputcredentials.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/inputcredentials.jpg -------------------------------------------------------------------------------- /static/images/00_prequisites/management-console-cloud9-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/management-console-cloud9-1.png -------------------------------------------------------------------------------- /static/images/00_prequisites/management-console-cloud9-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/management-console-cloud9-2.png -------------------------------------------------------------------------------- /static/images/00_prequisites/management-console-cloud9-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/management-console-cloud9-3.png -------------------------------------------------------------------------------- /static/images/00_prequisites/management-console-cloud9-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/management-console-cloud9-4.png -------------------------------------------------------------------------------- /static/images/00_prequisites/management-console-cloud9-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/management-console-cloud9-5.png -------------------------------------------------------------------------------- /static/images/00_prequisites/management-console-cloud9-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/management-console-cloud9-6.png -------------------------------------------------------------------------------- /static/images/00_prequisites/open_new_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/open_new_window.png -------------------------------------------------------------------------------- /static/images/00_prequisites/preview_runnning_application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/preview_runnning_application.png -------------------------------------------------------------------------------- /static/images/00_prequisites/signin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/signin.jpg -------------------------------------------------------------------------------- /static/images/00_prequisites/signinsignout.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/signinsignout.mov.gif -------------------------------------------------------------------------------- /static/images/00_prequisites/signupform.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/signupform.jpg -------------------------------------------------------------------------------- /static/images/00_prequisites/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/terminal.png -------------------------------------------------------------------------------- /static/images/00_prequisites/usercreated.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/usercreated.jpg -------------------------------------------------------------------------------- /static/images/00_prequisites/vue-router.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/00_prequisites/vue-router.gif -------------------------------------------------------------------------------- /static/images/07_multi_env/.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/.png -------------------------------------------------------------------------------- /static/images/07_multi_env/Screen Shot 2020-05-15 at 16.32.06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/Screen Shot 2020-05-15 at 16.32.06.png -------------------------------------------------------------------------------- /static/images/07_multi_env/access_control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/access_control.png -------------------------------------------------------------------------------- /static/images/07_multi_env/add_ssh_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/add_ssh_key.png -------------------------------------------------------------------------------- /static/images/07_multi_env/amplify_console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/amplify_console.png -------------------------------------------------------------------------------- /static/images/07_multi_env/branch_name_prefix_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/branch_name_prefix_settings.png -------------------------------------------------------------------------------- /static/images/07_multi_env/build_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/build_settings.png -------------------------------------------------------------------------------- /static/images/07_multi_env/compare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/compare.png -------------------------------------------------------------------------------- /static/images/07_multi_env/complete_deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/complete_deploy.png -------------------------------------------------------------------------------- /static/images/07_multi_env/confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/confirm.png -------------------------------------------------------------------------------- /static/images/07_multi_env/connect_develop-branch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/connect_develop-branch.png -------------------------------------------------------------------------------- /static/images/07_multi_env/connect_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/connect_github.png -------------------------------------------------------------------------------- /static/images/07_multi_env/copy_ssh_git_url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/copy_ssh_git_url.png -------------------------------------------------------------------------------- /static/images/07_multi_env/create_build_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/create_build_settings.png -------------------------------------------------------------------------------- /static/images/07_multi_env/create_new_repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/create_new_repository.png -------------------------------------------------------------------------------- /static/images/07_multi_env/design_env.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/design_env.png -------------------------------------------------------------------------------- /static/images/07_multi_env/e2e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/e2e.png -------------------------------------------------------------------------------- /static/images/07_multi_env/edit_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/edit_app.png -------------------------------------------------------------------------------- /static/images/07_multi_env/enable_basic_auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/enable_basic_auth.png -------------------------------------------------------------------------------- /static/images/07_multi_env/enable_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/enable_preview.png -------------------------------------------------------------------------------- /static/images/07_multi_env/general_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/general_settings.png -------------------------------------------------------------------------------- /static/images/07_multi_env/install_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/install_preview.png -------------------------------------------------------------------------------- /static/images/07_multi_env/install_preview_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/install_preview_dialog.png -------------------------------------------------------------------------------- /static/images/07_multi_env/master_env.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/master_env.png -------------------------------------------------------------------------------- /static/images/07_multi_env/multi_env.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/multi_env.png -------------------------------------------------------------------------------- /static/images/07_multi_env/new_repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/new_repository.png -------------------------------------------------------------------------------- /static/images/07_multi_env/open_pull_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/open_pull_request.png -------------------------------------------------------------------------------- /static/images/07_multi_env/preview_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/preview_settings.png -------------------------------------------------------------------------------- /static/images/07_multi_env/previews_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/previews_detail.png -------------------------------------------------------------------------------- /static/images/07_multi_env/previews_pr_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/previews_pr_list.png -------------------------------------------------------------------------------- /static/images/07_multi_env/production_user_not_exist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/production_user_not_exist.png -------------------------------------------------------------------------------- /static/images/07_multi_env/pull_request_amplify_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/pull_request_amplify_link.png -------------------------------------------------------------------------------- /static/images/07_multi_env/re_deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/re_deploy.png -------------------------------------------------------------------------------- /static/images/07_multi_env/rebuild.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/rebuild.png -------------------------------------------------------------------------------- /static/images/07_multi_env/select_branch_env.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/select_branch_env.png -------------------------------------------------------------------------------- /static/images/07_multi_env/select_branch_env.png_design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/select_branch_env.png_design.png -------------------------------------------------------------------------------- /static/images/07_multi_env/select_branch_env_design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/select_branch_env_design.png -------------------------------------------------------------------------------- /static/images/07_multi_env/select_preview_branch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/select_preview_branch.png -------------------------------------------------------------------------------- /static/images/07_multi_env/setting_basic_auth_pass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/setting_basic_auth_pass.png -------------------------------------------------------------------------------- /static/images/07_multi_env/share_multi_backend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/share_multi_backend.png -------------------------------------------------------------------------------- /static/images/07_multi_env/staging_confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/staging_confirm.png -------------------------------------------------------------------------------- /static/images/07_multi_env/staging_confirm_design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/staging_confirm_design.png -------------------------------------------------------------------------------- /static/images/07_multi_env/staging_env.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/staging_env.png -------------------------------------------------------------------------------- /static/images/07_multi_env/start-autodetect-branch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/start-autodetect-branch.png -------------------------------------------------------------------------------- /static/images/07_multi_env/start_build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/start_build.png -------------------------------------------------------------------------------- /static/images/07_multi_env/success_to_create_design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/success_to_create_design.png -------------------------------------------------------------------------------- /static/images/07_multi_env/success_to_create_staging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/success_to_create_staging.png -------------------------------------------------------------------------------- /static/images/07_multi_env/success_to_deploy_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/success_to_deploy_preview.png -------------------------------------------------------------------------------- /static/images/07_multi_env/success_to_push.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/success_to_push.png -------------------------------------------------------------------------------- /static/images/07_multi_env/sync_github_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/07_multi_env/sync_github_account.png -------------------------------------------------------------------------------- /static/images/10_getting_started/c9_new_terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/10_getting_started/c9_new_terminal.png -------------------------------------------------------------------------------- /static/images/10_getting_started/final_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/10_getting_started/final_architecture.png -------------------------------------------------------------------------------- /static/images/10_getting_started/pop_browser_new_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/10_getting_started/pop_browser_new_window.png -------------------------------------------------------------------------------- /static/images/10_getting_started/preview_running_application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/10_getting_started/preview_running_application.png -------------------------------------------------------------------------------- /static/images/30_mock/.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/.png -------------------------------------------------------------------------------- /static/images/30_mock/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/architecture.png -------------------------------------------------------------------------------- /static/images/30_mock/architecture_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/architecture_api.png -------------------------------------------------------------------------------- /static/images/30_mock/architecture_auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/architecture_auth.png -------------------------------------------------------------------------------- /static/images/30_mock/auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/auth.png -------------------------------------------------------------------------------- /static/images/30_mock/confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/confirm.png -------------------------------------------------------------------------------- /static/images/30_mock/getPost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/getPost.png -------------------------------------------------------------------------------- /static/images/30_mock/graphql_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/graphql_1.png -------------------------------------------------------------------------------- /static/images/30_mock/graphql_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/graphql_2.png -------------------------------------------------------------------------------- /static/images/30_mock/graphql_change_auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/graphql_change_auth.png -------------------------------------------------------------------------------- /static/images/30_mock/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/key.png -------------------------------------------------------------------------------- /static/images/30_mock/listPosts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/listPosts.png -------------------------------------------------------------------------------- /static/images/30_mock/npm_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/30_mock/npm_start.png -------------------------------------------------------------------------------- /static/images/40_hosting/architecture_hosting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/40_hosting/architecture_hosting.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/architecture.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/architecture_follow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/architecture_follow.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/architecture_lambda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/architecture_lambda.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/architecture_timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/architecture_timeline.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/createPostAndTimeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/createPostAndTimeline.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/createPostAndTimeline_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/createPostAndTimeline_error.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/createTimeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/createTimeline.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/follow_confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/follow_confirm.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/follow_mutation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/follow_mutation.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/listFollower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/listFollower.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/listTimelines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/listTimelines.png -------------------------------------------------------------------------------- /static/images/50_follow_timeline/listTimelines_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/50_follow_timeline/listTimelines_2.png -------------------------------------------------------------------------------- /static/images/60_full_text_search/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/60_full_text_search/architecture.png -------------------------------------------------------------------------------- /static/images/60_full_text_search/front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/60_full_text_search/front.png -------------------------------------------------------------------------------- /static/images/60_full_text_search/searchPosts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/60_full_text_search/searchPosts.png -------------------------------------------------------------------------------- /static/images/80_e2e/amplifyyml_dl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/80_e2e/amplifyyml_dl.png -------------------------------------------------------------------------------- /static/images/80_e2e/build_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/80_e2e/build_settings.png -------------------------------------------------------------------------------- /static/images/80_e2e/failed_to_authenticator_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/80_e2e/failed_to_authenticator_test.png -------------------------------------------------------------------------------- /static/images/80_e2e/play_test_video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/80_e2e/play_test_video.png -------------------------------------------------------------------------------- /static/images/80_e2e/success_to_authenticator_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/80_e2e/success_to_authenticator_test.png -------------------------------------------------------------------------------- /static/images/80_e2e/success_to_e2e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/80_e2e/success_to_e2e.png -------------------------------------------------------------------------------- /static/images/80_e2e/test_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/80_e2e/test_flow.png -------------------------------------------------------------------------------- /static/images/90_summary/delete_cloud9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/90_summary/delete_cloud9.png -------------------------------------------------------------------------------- /static/images/Peccy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/Peccy.png -------------------------------------------------------------------------------- /static/images/apn-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/apn-logo.jpg -------------------------------------------------------------------------------- /static/images/aws-open-source.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/static/images/aws-open-source.jpg -------------------------------------------------------------------------------- /static/js/kinesis.js: -------------------------------------------------------------------------------- 1 | // variable injection 2 | var awsRegion = document.getElementById('awsRegion').title; 3 | var kinesisStreamName = document.getElementById('kinesisStreamName').title; 4 | var cognitoPoolId = document.getElementById('cognitoPoolId').title; 5 | var deliveryId = document.getElementById('contentId').title; 6 | var version = document.getElementById('versions').title; 7 | var language = document.getElementById('language').title; 8 | var scriptVersion = '2019-12-16' 9 | 10 | // Send log to Kinesis 11 | var sendLog = function () { 12 | console.log('Send Log') 13 | // Configure Credentials to use Cognito 14 | AWS.config.credentials = new AWS.CognitoIdentityCredentials({ 15 | IdentityPoolId: cognitoPoolId 16 | }); 17 | AWS.config.region = awsRegion; 18 | AWS.config.credentials.get(function (err) { 19 | if (err) { 20 | // alert('Error retrieving credentials.'); 21 | console.error(err); 22 | return; 23 | } 24 | // create Amazon Kinesis service object 25 | var kinesis = new AWS.Kinesis({ 26 | apiVersion: '2013-12-02' 27 | }); 28 | 29 | // create user Id 30 | var userId 31 | // check whether user use HTML5 32 | if (window.localStorage) { 33 | // generate userId if not data in localstorage 34 | userId = localStorage.getItem('userId'); 35 | isRegistered = 'false'; 36 | if (userId == null) { 37 | userId = AWS.config.credentials.identityId; 38 | localStorage.setItem('userId', userId); 39 | isRegistered = 'true'; 40 | } 41 | } else { 42 | userId = 'guestUser' 43 | } 44 | 45 | var recordData = []; 46 | var record = { 47 | Data: JSON.stringify({ 48 | page_path: window.location.pathname, 49 | delivery_id: deliveryId, 50 | user_id: userId, 51 | is_regitered: isRegistered, 52 | version: version, 53 | language: language, 54 | scriptVersion: scriptVersion 55 | }), 56 | PartitionKey: 'partition-' + userId 57 | }; 58 | recordData.push(record); 59 | 60 | kinesis.putRecords({ 61 | Records: recordData, 62 | StreamName: kinesisStreamName 63 | }, function (err, data) { 64 | if (err) { 65 | console.error(err); 66 | } 67 | }); 68 | 69 | }); 70 | }; 71 | 72 | // Call Send Log Func by every 10 sec when the Tab is focused 73 | var interval_sec = 10; 74 | var is_focus = true; 75 | 76 | window.onfocus = function () { 77 | console.log('Start focus') 78 | is_focus = true; 79 | } 80 | window.onblur = function () { 81 | console.log('End focus') 82 | is_focus = false; 83 | } 84 | 85 | var check_interval = setInterval(function () { 86 | console.log(is_focus) 87 | if (is_focus) { 88 | sendLog(); 89 | } 90 | }, interval_sec * 1000); -------------------------------------------------------------------------------- /themes/learn/.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 2 9 | indent_style = space 10 | trim_trailing_whitespace = true 11 | 12 | [*.js] 13 | insert_final_newline = true 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /themes/learn/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | public/ 3 | exampleSite/public 4 | -------------------------------------------------------------------------------- /themes/learn/.grenrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dataSource: "prs" 3 | prefix: "v" 4 | onlyMilestones: false 5 | changelogFilename: "CHANGELOG.md" 6 | includeMessages: "all" 7 | ignoreIssuesWith: 8 | - "support" 9 | ignoreLabels: 10 | - "duplicate" 11 | - "invalid" 12 | - "wontfix" 13 | groupBy: 14 | New features: 15 | - "feature" 16 | Bug Fixes: 17 | - "bug" 18 | Enhancements: 19 | - "enhancement" 20 | Internationalisation: 21 | - "i18n" 22 | Theme Meta: 23 | - "meta" 24 | Uncategorised: 25 | - "closed" 26 | -------------------------------------------------------------------------------- /themes/learn/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Grav 4 | Copyright (c) 2016 MATHIEU CORNIC 5 | Copyright (c) 2017 Valere JEANTET 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /themes/learn/README.md: -------------------------------------------------------------------------------- 1 | # Hugo Learn Theme 2 | 3 | This repository contains a theme for [Hugo](https://gohugo.io/), based on great [Grav Learn Theme](https://learn.getgrav.org/). 4 | 5 | Visit the [theme documentation](https://learn.netlify.com/en/) to see what is going on. It is actually built with this theme. 6 | 7 | [![wercker status](https://app.wercker.com/status/233466a2be73fcea400e7dc02ef6adf9/s/master "wercker status")](https://app.wercker.com/project/byKey/233466a2be73fcea400e7dc02ef6adf9) 8 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmatcornic%2Fhugo-theme-learn.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmatcornic%2Fhugo-theme-learn?ref=badge_shield) 9 | 10 | ## Main features 11 | 12 | - Automatic Search 13 | - Multilingual mode 14 | - Unlimited menu levels 15 | - Automatic next/prev buttons to navigate through menu entries 16 | - Image resizing, shadow… 17 | - Attachments files 18 | - List child pages 19 | - Mermaid diagram (flowchart, sequence, gantt) 20 | - Customizable look and feel and themes variants 21 | - Buttons, Tip/Note/Info/Warning boxes, Expand 22 | 23 | ## Installation 24 | 25 | Navigate to your themes folder in your Hugo site and use the following commands: 26 | 27 | ``` 28 | $ cd themes 29 | $ git clone https://github.com/matcornic/hugo-theme-learn.git 30 | ``` 31 | 32 | Check that your Hugo version is minimum `0.25` with `hugo version`. 33 | 34 | ![Overview](https://github.com/matcornic/hugo-theme-learn/raw/master/images/tn.png) 35 | 36 | ## Usage 37 | 38 | - [Visit the documentation](https://learn.netlify.com/en/) 39 | 40 | ## Download old versions (prior to 2.0.0) 41 | 42 | If you need old version for compatibility purpose, either download [theme source code from releases](https://github.com/matcornic/hugo-theme-learn/releases) or use the right git tag. For example, with `1.1.0` 43 | 44 | - Direct download way: https://github.com/matcornic/hugo-theme-learn/archive/1.1.0.zip 45 | - Git way: 46 | 47 | ```shell 48 | cd themes/hugo-theme-learn 49 | git checkout tags/1.1.0 50 | ``` 51 | 52 | For both solutions, the documentation is available at https://github.com/matcornic/hugo-theme-learn/releases/download/1.1.0/hugo-learn-doc-1.1.0.zip 53 | 54 | ## Credits 55 | 56 | Many thanks to [@vjeantet](https://github.com/vjeantet/) for the fork [docdock](https://github.com/vjeantet/hugo-theme-docdock). The v2 of this theme is mainly based on his work ! 57 | 58 | 59 | ## License 60 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmatcornic%2Fhugo-theme-learn.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmatcornic%2Fhugo-theme-learn?ref=badge_large) 61 | -------------------------------------------------------------------------------- /themes/learn/archetypes/chapter.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "{{ replace .Name "-" " " | title }}" 3 | date = {{ .Date }} 4 | weight = 5 5 | chapter = true 6 | pre = "X. " 7 | +++ 8 | 9 | Lorem Ipsum. -------------------------------------------------------------------------------- /themes/learn/archetypes/default.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "{{ replace .Name "-" " " | title }}" 3 | date = {{ .Date }} 4 | weight = 5 5 | +++ 6 | 7 | Lorem Ipsum. -------------------------------------------------------------------------------- /themes/learn/i18n/ar.toml: -------------------------------------------------------------------------------- 1 | [Search-placeholder] 2 | other = "...البحث" 3 | 4 | [Clear-History] 5 | other = "مسح السجل" 6 | 7 | [Attachments-label] 8 | other = "مرفقات" 9 | 10 | [title-404] 11 | other = "خطأ" 12 | 13 | [message-404] 14 | other = ".¯\\_(ツ)_/¯أوبس. يبدو أن هذه الصفحة غير موجودة" 15 | 16 | [Go-to-homepage] 17 | other = "الذهاب إلى الصفحة الرئيسية" 18 | 19 | [Edit-this-page] 20 | other = "تعديل هذه الصفحة" 21 | 22 | [Shortcuts-Title] 23 | other = "المزيد" 24 | 25 | [Expand-title] 26 | other = "...قم بتوسيع" -------------------------------------------------------------------------------- /themes/learn/i18n/de.toml: -------------------------------------------------------------------------------- 1 | [Search-placeholder] 2 | other = "Suchen..." 3 | 4 | [Clear-History] 5 | other = "Verlauf löschen" 6 | 7 | [Attachments-label] 8 | other = "Anhänge" 9 | 10 | [title-404] 11 | other = "Fehler" 12 | 13 | [message-404] 14 | other = "Huch. Diese Seite scheint nicht zu existieren ¯\\_(ツ)_/¯." 15 | 16 | [Go-to-homepage] 17 | other = "Gehe zur Homepage" 18 | 19 | [Edit-this-page] 20 | other = "Bearbeite diese Seite" 21 | 22 | [Shortcuts-Title] 23 | other = "Mehr" 24 | 25 | [Expand-title] 26 | other = "Erweitere mich..." -------------------------------------------------------------------------------- /themes/learn/i18n/en.toml: -------------------------------------------------------------------------------- 1 | [Search-placeholder] 2 | other = "Search..." 3 | 4 | [Clear-History] 5 | other = "Clear History" 6 | 7 | [Attachments-label] 8 | other = "Attachments" 9 | 10 | [title-404] 11 | other = "Error" 12 | 13 | [message-404] 14 | other = "Woops. Looks like this page doesn't exist ¯\\_(ツ)_/¯." 15 | 16 | [Go-to-homepage] 17 | other = "Go to homepage" 18 | 19 | [Edit-this-page] 20 | other = "Edit this page" 21 | 22 | [Shortcuts-Title] 23 | other = "More" 24 | 25 | [Expand-title] 26 | other = "Expand me..." -------------------------------------------------------------------------------- /themes/learn/i18n/es.toml: -------------------------------------------------------------------------------- 1 | [Search-placeholder] 2 | other = "Buscar..." 3 | 4 | [Clear-History] 5 | other = "Borrar Historial" 6 | 7 | [Attachments-label] 8 | other = "Adjuntos" 9 | 10 | [title-404] 11 | other = "Error" 12 | 13 | [message-404] 14 | other = "Ups. Parece que la página no existe ¯\\_(ツ)_/¯." 15 | 16 | [Go-to-homepage] 17 | other = "Ir al inicio" 18 | 19 | [Edit-this-page] 20 | other = "Editar esta página" 21 | 22 | [Shortcuts-Title] 23 | other = "Más" 24 | 25 | [Expand-title] 26 | other = "Expandir..." 27 | -------------------------------------------------------------------------------- /themes/learn/i18n/fr.toml: -------------------------------------------------------------------------------- 1 | [Search-placeholder] 2 | other = "Rechercher..." 3 | 4 | [Clear-History] 5 | other = "Supprimer l'historique" 6 | 7 | [Attachments-label] 8 | other = "Pièces jointes" 9 | 10 | [title-404] 11 | other = "Erreur" 12 | 13 | [message-404] 14 | other = "Oups. On dirait que cette page n'existe pas ¯\\_(ツ)_/¯" 15 | 16 | [Go-to-homepage] 17 | other = "Vers la page d'accueil" 18 | 19 | [Edit-this-page] 20 | other = "Modifier la page" 21 | 22 | [Shortcuts-Title] 23 | other = "Aller plus loin" 24 | 25 | [Expand-title] 26 | other = "Déroulez-moi..." -------------------------------------------------------------------------------- /themes/learn/i18n/hi.toml: -------------------------------------------------------------------------------- 1 | [Search-placeholder] 2 | other = "खोजे..." 3 | 4 | [Clear-History] 5 | other = "इतिहास मिटाएँ" 6 | 7 | [Attachments-label] 8 | other = "संलग्नंक (अटैचमेंट)" 9 | 10 | [title-404] 11 | other = "त्रुटि" 12 | 13 | [message-404] 14 | other = "यह पृष्ठ अभि अनुपलब्ध है!" 15 | 16 | [Go-to-homepage] 17 | other = "मुख्य पृष्ठ पर जाऐ" 18 | 19 | [Edit-this-page] 20 | other = "यह पृष्ठ संपादित करें" 21 | 22 | [Shortcuts-Title] 23 | other = "अधिक सामग्री दिखाएं" 24 | 25 | [Expand-title] 26 | other = "विस्तार करे..." 27 | -------------------------------------------------------------------------------- /themes/learn/i18n/id.toml: -------------------------------------------------------------------------------- 1 | [Search-placeholder] 2 | other = "Telusuri..." 3 | 4 | [Clear-History] 5 | other = "Bersihkan Riwayat" 6 | 7 | [Attachments-label] 8 | other = "Lampiran" 9 | 10 | [title-404] 11 | other = "Kesalahan" 12 | 13 | [message-404] 14 | other = "Oops. Sepertinya halaman ini tidak ada ¯\\_(ツ)_/¯." 15 | 16 | [Go-to-homepage] 17 | other = "Ke halaman depan" 18 | 19 | [Edit-this-page] 20 | other = "Edit halaman ini" 21 | 22 | [Shortcuts-Title] 23 | other = "Lainnya" 24 | 25 | [Expand-title] 26 | other = "Bentangkan..." 27 | -------------------------------------------------------------------------------- /themes/learn/i18n/nl.toml: -------------------------------------------------------------------------------- 1 | [Search-placeholder] 2 | other = "Zoeken..." 3 | 4 | [Clear-History] 5 | other = "Wis geschiedenis" 6 | 7 | [Attachments-label] 8 | other = "Bijlagen" 9 | 10 | [title-404] 11 | other = "Error" 12 | 13 | [message-404] 14 | other = "Blijkbaar bestaat deze pagina niet ¯\\_(ツ)_/¯." 15 | 16 | [Go-to-homepage] 17 | other = "Naar startpagina" 18 | 19 | [Edit-this-page] 20 | other = "Deze pagina bewerken" 21 | 22 | [Shortcuts-Title] 23 | other = "Snelkoppelingen" 24 | 25 | [Expand-title] 26 | other = "Lees meer..." 27 | -------------------------------------------------------------------------------- /themes/learn/i18n/pt.toml: -------------------------------------------------------------------------------- 1 | [Search-placeholder] 2 | other = "Procurar..." 3 | 4 | [Clear-History] 5 | other = "Limpar Histórico" 6 | 7 | [Attachments-label] 8 | other = "Anexos" 9 | 10 | [title-404] 11 | other = "Erro" 12 | 13 | [message-404] 14 | other = "Ops. Parece que a página não existe ¯\\_(ツ)_/¯." 15 | 16 | [Go-to-homepage] 17 | other = "Ir para o início" 18 | 19 | [Edit-this-page] 20 | other = "Editar esta página" 21 | 22 | [Shortcuts-Title] 23 | other = "Mais" 24 | 25 | [Expand-title] 26 | other = "Expandir..." 27 | -------------------------------------------------------------------------------- /themes/learn/i18n/tr.toml: -------------------------------------------------------------------------------- 1 | [Search-placeholder] 2 | other = "Ara..." 3 | 4 | [Clear-History] 5 | other = "Geçmişi Temizle" 6 | 7 | [Attachments-label] 8 | other = "Ekler" 9 | 10 | [title-404] 11 | other = "Hata" 12 | 13 | [message-404] 14 | other = "Uups. Görünüşe göre böyle bir sayfa yok ¯\\_(ツ)_/¯" 15 | 16 | [Go-to-homepage] 17 | other = "Anasayfaya dön" 18 | 19 | [Edit-this-page] 20 | other = "Sayfayı düzenle" 21 | 22 | [Shortcuts-Title] 23 | other = "Dahası Var" 24 | 25 | [Expand-title] 26 | other = "Genişlet..." 27 | -------------------------------------------------------------------------------- /themes/learn/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/images/screenshot.png -------------------------------------------------------------------------------- /themes/learn/images/tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/images/tn.png -------------------------------------------------------------------------------- /themes/learn/layouts/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ partial "meta.html" . }} {{ partial "favicon.html" . }} {{ .Scratch.Add "title" "" }}{{ if eq .Site.Data.titles .Title }}{{ .Scratch.Set "title" (index .Site.Data.titles .Title).title }}{{ else }}{{ .Scratch.Set "title" .Title}}{{end}} 6 | {{ .Scratch.Get "title" }} 7 | 8 | {{ $assetBusting := not .Site.Params.disableAssetsBusting }} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {{with .Site.Params.themeVariant}} 17 | 18 | {{end}} 19 | 34 | {{ partial "custom-header.html" . }} 35 | 36 | 37 | 38 | 39 |
40 |
41 |
42 |
43 |

{{T "title-404"}}

44 |

Page not found!

45 |

{{T "message-404"}}

46 |

47 |

{{T "Go-to-homepage"}}

48 |
49 |
50 | 51 |
52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /themes/learn/layouts/_default/list.html: -------------------------------------------------------------------------------- 1 | {{ partial "header.html" . }} 2 | 3 | {{ if eq .Kind "section" }} 4 | {{ .Content }} 5 | {{end}} 6 | 7 | {{ if or (eq .Kind "taxonomy") (eq .Kind "taxonomyTerm") }} 8 |
    9 | {{ range .Pages }} 10 |
  • {{.Title}}
  • 11 | {{ end }} 12 |
13 | {{end}} 14 | 15 |
16 | {{with .Params.LastModifierDisplayName}} 17 | {{ . }} {{with $.Date}} {{ .Format "02/01/2006" }}{{end}} 18 | 19 | {{end}} 20 |
21 | 22 | {{ partial "footer.html" . }} -------------------------------------------------------------------------------- /themes/learn/layouts/_default/single.html: -------------------------------------------------------------------------------- 1 | {{ partial "header.html" . }} 2 | 3 | {{ .Content }} 4 | 5 |
6 | {{with .Params.LastModifierDisplayName}} 7 | {{ . }} {{with $.Date}} {{ .Format "02/01/2006" }}{{end}} 8 | 9 | {{end}} 10 |
11 | 12 | 13 | {{ partial "footer.html" . }} 14 | -------------------------------------------------------------------------------- /themes/learn/layouts/index.html: -------------------------------------------------------------------------------- 1 | {{ partial "header.html" . }} 2 | 3 | navigation 4 | 5 | 6 | {{.Site.Home.Content}} 7 | 8 | {{ partial "footer.html" . }} 9 | -------------------------------------------------------------------------------- /themes/learn/layouts/index.json: -------------------------------------------------------------------------------- 1 | [{{ range $index, $page := .Site.Pages }} 2 | {{- if ne $page.Type "json" -}} 3 | {{- if and $index (gt $index 0) -}},{{- end }} 4 | { 5 | "uri": "{{ $page.Permalink }}", 6 | "title": "{{ htmlEscape $page.Title}}", 7 | "tags": [{{ range $tindex, $tag := $page.Params.tags }}{{ if $tindex }}, {{ end }}"{{ $tag| htmlEscape }}"{{ end }}], 8 | "description": "{{ htmlEscape .Description}}", 9 | "content": {{$page.Plain | jsonify}} 10 | } 11 | {{- end -}} 12 | {{- end -}}] -------------------------------------------------------------------------------- /themes/learn/layouts/partials/custom-comments.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /themes/learn/layouts/partials/custom-footer.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/layouts/partials/custom-footer.html -------------------------------------------------------------------------------- /themes/learn/layouts/partials/custom-header.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /themes/learn/layouts/partials/favicon.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /themes/learn/layouts/partials/logo.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
6 | -------------------------------------------------------------------------------- /themes/learn/layouts/partials/menu-footer.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | -------------------------------------------------------------------------------- /themes/learn/layouts/partials/meta.html: -------------------------------------------------------------------------------- 1 | 2 | {{ with .Site.Params.author }}{{ end }} 3 | -------------------------------------------------------------------------------- /themes/learn/layouts/partials/search.html: -------------------------------------------------------------------------------- 1 | 6 | {{ $assetBusting := not .Site.Params.disableAssetsBusting }} 7 | 8 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /themes/learn/layouts/partials/tags.html: -------------------------------------------------------------------------------- 1 | {{ if .Params.tags }} 2 |
3 | {{range .Params.tags}} 4 | {{ . }} 5 | {{end}} 6 |
7 | {{end}} -------------------------------------------------------------------------------- /themes/learn/layouts/partials/toc.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{ .TableOfContents }} 4 |
5 |
6 | -------------------------------------------------------------------------------- /themes/learn/layouts/shortcodes/attachments.html: -------------------------------------------------------------------------------- 1 | {{ $_hugo_config := `{ "version": 1 }` }} 2 |
3 | 7 | {{if eq .Page.File.BaseFileName "index"}} 8 | {{$.Scratch.Add "filesName" "files"}} 9 | {{else}} 10 | {{$.Scratch.Add "filesName" (printf "%s.files" .Page.File.BaseFileName)}} 11 | {{end}} 12 |
13 | {{ range (readDir (printf "./content/%s%s" .Page.File.Dir ($.Scratch.Get "filesName")) ) }} 14 | {{ $fileDir := replace $.Page.File.Dir "\\" "/" }} 15 | {{if ($.Get "pattern")}} 16 | {{if (findRE ($.Get "pattern") .Name)}} 17 |
  • 18 | 19 | {{.Name}} 20 | 21 | ({{div .Size 1024 }} kb) 22 |
  • 23 | {{end}} 24 | {{else}} 25 |
  • 26 | 27 | {{.Name}} 28 | 29 | ({{div .Size 1024 }} kb) 30 |
  • 31 | {{end}} 32 | {{end}} 33 |
    34 | {{.Inner}} 35 |
    36 | 37 | -------------------------------------------------------------------------------- /themes/learn/layouts/shortcodes/button.html: -------------------------------------------------------------------------------- 1 | {{ $_hugo_config := `{ "version": 1 }` }} 2 | 3 | {{ $icon := .Get "icon" }} 4 | {{ $iconposition := .Get "icon-position" }} 5 | {{ if ($icon) }} 6 | {{ if or (not ($iconposition)) (eq $iconposition "left") }} 7 | 8 | {{ end }} 9 | {{ end }} 10 | {{ .Inner }} 11 | {{ if and ($icon) (eq $iconposition "right")}} 12 | 13 | {{ end }} 14 | 15 | -------------------------------------------------------------------------------- /themes/learn/layouts/shortcodes/expand.html: -------------------------------------------------------------------------------- 1 | {{ $_hugo_config := `{ "version": 1 }` }} 2 |
    3 |
    4 | 5 | 6 | {{$expandMessage := T "Expand-title"}} 7 | {{ if .IsNamedParams }} 8 | {{.Get "default" | default $expandMessage}} 9 | {{else}} 10 | {{.Get 0 | default $expandMessage}} 11 | {{end}} 12 | 13 |
    14 | 17 |
    -------------------------------------------------------------------------------- /themes/learn/layouts/shortcodes/img.html: -------------------------------------------------------------------------------- 1 | {{ $img := $.Page.Resources.GetMatch (.Get 0)}} 2 |
    3 | {{(.Get 1)}} 4 |
    -------------------------------------------------------------------------------- /themes/learn/layouts/shortcodes/mermaid.html: -------------------------------------------------------------------------------- 1 | {{ $_hugo_config := `{ "version": 1 }` }} 2 |
    {{ safeHTML .Inner }}
    3 | -------------------------------------------------------------------------------- /themes/learn/layouts/shortcodes/notice.html: -------------------------------------------------------------------------------- 1 | {{ $_hugo_config := `{ "version": 1 }` }} 2 |
    {{ .Inner }}
    3 | -------------------------------------------------------------------------------- /themes/learn/layouts/shortcodes/ref.html: -------------------------------------------------------------------------------- 1 | {{- if in (.Get 0) "/_index.md" -}} 2 | {{- $paths := (split (.Get 0) "_index.md") -}} 3 | {{- $pagepath := index $paths 0 -}} 4 | {{- $anchor := index $paths 1 -}} 5 | {{- with .Site.GetPage "section" (trim $pagepath "/") -}} 6 | {{- ( printf "%s%s" $pagepath $anchor ) | relLangURL -}} 7 | {{- end -}} 8 | {{- else -}} 9 | {{- with .Site.GetPage "section" (.Get 0) }} 10 | {{- .RelPermalink -}} 11 | {{- else -}} 12 | {{- .Get 0 | relref .Page -}} 13 | {{- end -}} 14 | {{- end -}} -------------------------------------------------------------------------------- /themes/learn/layouts/shortcodes/relref.html: -------------------------------------------------------------------------------- 1 | {{- if in (.Get 0) "/_index.md" -}} 2 | {{- $paths := (split (.Get 0) "_index.md") -}} 3 | {{- $pagepath := index $paths 0 -}} 4 | {{- $anchor := index $paths 1 -}} 5 | {{- with .Site.GetPage "section" (trim $pagepath "/") -}} 6 | {{- ( printf "%s%s" $pagepath $anchor ) | relLangURL -}} 7 | {{- end -}} 8 | {{- else -}} 9 | {{- with .Site.GetPage "section" (.Get 0) }} 10 | {{- .RelPermalink -}} 11 | {{- else -}} 12 | {{- .Get 0 | relref .Page -}} 13 | {{- end -}} 14 | {{- end -}} -------------------------------------------------------------------------------- /themes/learn/layouts/shortcodes/siteparam.html: -------------------------------------------------------------------------------- 1 | {{- $paramName := (.Get 0) -}} 2 | {{- $siteParams := .Site.Params -}} 3 | {{- with $paramName -}} 4 | {{- with $siteParams -}} 5 | {{- index . (lower $paramName) -}} 6 | {{- end -}} 7 | {{- end -}} -------------------------------------------------------------------------------- /themes/learn/layouts/shortcodes/siteurl.html: -------------------------------------------------------------------------------- 1 | {{ .Page.Site.BaseURL }} -------------------------------------------------------------------------------- /themes/learn/static/css/atom-one-dark-reasonable.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Atom One Dark With support for ReasonML by Gidi Morris, based off work by Daniel Gamage 4 | 5 | Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax 6 | 7 | */ 8 | .hljs { 9 | display: block; 10 | overflow-x: auto; 11 | padding: 0.5em; 12 | line-height: 1.3em; 13 | color: #abb2bf; 14 | background: #282c34; 15 | border-radius: 5px; 16 | } 17 | .hljs-keyword, .hljs-operator { 18 | color: #F92672; 19 | } 20 | .hljs-pattern-match { 21 | color: #F92672; 22 | } 23 | .hljs-pattern-match .hljs-constructor { 24 | color: #61aeee; 25 | } 26 | .hljs-function { 27 | color: #61aeee; 28 | } 29 | .hljs-function .hljs-params { 30 | color: #A6E22E; 31 | } 32 | .hljs-function .hljs-params .hljs-typing { 33 | color: #FD971F; 34 | } 35 | .hljs-module-access .hljs-module { 36 | color: #7e57c2; 37 | } 38 | .hljs-constructor { 39 | color: #e2b93d; 40 | } 41 | .hljs-constructor .hljs-string { 42 | color: #9CCC65; 43 | } 44 | .hljs-comment, .hljs-quote { 45 | color: #b18eb1; 46 | font-style: italic; 47 | } 48 | .hljs-doctag, .hljs-formula { 49 | color: #c678dd; 50 | } 51 | .hljs-section, .hljs-name, .hljs-selector-tag, .hljs-deletion, .hljs-subst { 52 | color: #e06c75; 53 | } 54 | .hljs-literal { 55 | color: #56b6c2; 56 | } 57 | .hljs-string, .hljs-regexp, .hljs-addition, .hljs-attribute, .hljs-meta-string { 58 | color: #98c379; 59 | } 60 | .hljs-built_in, .hljs-class .hljs-title { 61 | color: #e6c07b; 62 | } 63 | .hljs-attr, .hljs-variable, .hljs-template-variable, .hljs-type, .hljs-selector-class, .hljs-selector-attr, .hljs-selector-pseudo, .hljs-number { 64 | color: #d19a66; 65 | } 66 | .hljs-symbol, .hljs-bullet, .hljs-link, .hljs-meta, .hljs-selector-id, .hljs-title { 67 | color: #61aeee; 68 | } 69 | .hljs-emphasis { 70 | font-style: italic; 71 | } 72 | .hljs-strong { 73 | font-weight: bold; 74 | } 75 | .hljs-link { 76 | text-decoration: underline; 77 | } 78 | -------------------------------------------------------------------------------- /themes/learn/static/css/auto-complete.css: -------------------------------------------------------------------------------- 1 | .autocomplete-suggestions { 2 | text-align: left; 3 | cursor: default; 4 | border: 1px solid #ccc; 5 | border-top: 0; 6 | background: #fff; 7 | box-shadow: -1px 1px 3px rgba(0,0,0,.1); 8 | 9 | /* core styles should not be changed */ 10 | position: absolute; 11 | display: none; 12 | z-index: 9999; 13 | max-height: 254px; 14 | overflow: hidden; 15 | overflow-y: auto; 16 | box-sizing: border-box; 17 | 18 | } 19 | .autocomplete-suggestion { 20 | position: relative; 21 | cursor: pointer; 22 | padding: 7px; 23 | line-height: 23px; 24 | white-space: nowrap; 25 | overflow: hidden; 26 | text-overflow: ellipsis; 27 | color: #333; 28 | } 29 | 30 | .autocomplete-suggestion b { 31 | font-weight: normal; 32 | color: #1f8dd6; 33 | } 34 | 35 | .autocomplete-suggestion.selected { 36 | background: #333; 37 | color: #fff; 38 | } 39 | 40 | .autocomplete-suggestion:hover { 41 | background: #444; 42 | color: #fff; 43 | } 44 | 45 | .autocomplete-suggestion > .context { 46 | font-size: 12px; 47 | } 48 | -------------------------------------------------------------------------------- /themes/learn/static/css/featherlight.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Featherlight - ultra slim jQuery lightbox 3 | * Version 1.7.13 - http://noelboss.github.io/featherlight/ 4 | * 5 | * Copyright 2018, Noël Raoul Bossart (http://www.noelboss.com) 6 | * MIT Licensed. 7 | **/ 8 | html.with-featherlight{overflow:hidden}.featherlight{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:2147483647;text-align:center;white-space:nowrap;cursor:pointer;background:#333;background:rgba(0,0,0,0)}.featherlight:last-of-type{background:rgba(0,0,0,.8)}.featherlight:before{content:'';display:inline-block;height:100%;vertical-align:middle}.featherlight .featherlight-content{position:relative;text-align:left;vertical-align:middle;display:inline-block;overflow:auto;padding:25px 25px 0;border-bottom:25px solid transparent;margin-left:5%;margin-right:5%;max-height:95%;background:#fff;cursor:auto;white-space:normal}.featherlight .featherlight-inner{display:block}.featherlight link.featherlight-inner,.featherlight script.featherlight-inner,.featherlight style.featherlight-inner{display:none}.featherlight .featherlight-close-icon{position:absolute;z-index:9999;top:0;right:0;line-height:25px;width:25px;cursor:pointer;text-align:center;font-family:Arial,sans-serif;background:#fff;background:rgba(255,255,255,.3);color:#000;border:0;padding:0}.featherlight .featherlight-close-icon::-moz-focus-inner{border:0;padding:0}.featherlight .featherlight-image{width:100%}.featherlight-iframe .featherlight-content{border-bottom:0;padding:0;-webkit-overflow-scrolling:touch}.featherlight iframe{border:0}.featherlight *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@media only screen and (max-width:1024px){.featherlight .featherlight-content{margin-left:0;margin-right:0;max-height:98%;padding:10px 10px 0;border-bottom:10px solid transparent}}@media print{html.with-featherlight>*>:not(.featherlight){display:none}} -------------------------------------------------------------------------------- /themes/learn/static/css/hybrid.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | vim-hybrid theme by w0ng (https://github.com/w0ng/vim-hybrid) 4 | 5 | */ 6 | 7 | /*background color*/ 8 | .hljs { 9 | display: block; 10 | overflow-x: auto; 11 | padding: 0.5em; 12 | background: #1d1f21; 13 | } 14 | 15 | /*selection color*/ 16 | .hljs::selection, 17 | .hljs span::selection { 18 | background: #373b41; 19 | } 20 | 21 | .hljs::-moz-selection, 22 | .hljs span::-moz-selection { 23 | background: #373b41; 24 | } 25 | 26 | /*foreground color*/ 27 | .hljs { 28 | color: #c5c8c6; 29 | } 30 | 31 | /*color: fg_yellow*/ 32 | .hljs-title, 33 | .hljs-name { 34 | color: #f0c674; 35 | } 36 | 37 | /*color: fg_comment*/ 38 | .hljs-comment, 39 | .hljs-meta, 40 | .hljs-meta .hljs-keyword { 41 | color: #707880; 42 | } 43 | 44 | /*color: fg_red*/ 45 | .hljs-number, 46 | .hljs-symbol, 47 | .hljs-literal, 48 | .hljs-deletion, 49 | .hljs-link { 50 | color: #cc6666 51 | } 52 | 53 | /*color: fg_green*/ 54 | .hljs-string, 55 | .hljs-doctag, 56 | .hljs-addition, 57 | .hljs-regexp, 58 | .hljs-selector-attr, 59 | .hljs-selector-pseudo { 60 | color: #b5bd68; 61 | } 62 | 63 | /*color: fg_purple*/ 64 | .hljs-attribute, 65 | .hljs-code, 66 | .hljs-selector-id { 67 | color: #b294bb; 68 | } 69 | 70 | /*color: fg_blue*/ 71 | .hljs-keyword, 72 | .hljs-selector-tag, 73 | .hljs-bullet, 74 | .hljs-tag { 75 | color: #81a2be; 76 | } 77 | 78 | /*color: fg_aqua*/ 79 | .hljs-subst, 80 | .hljs-variable, 81 | .hljs-template-tag, 82 | .hljs-template-variable { 83 | color: #8abeb7; 84 | } 85 | 86 | /*color: fg_orange*/ 87 | .hljs-type, 88 | .hljs-built_in, 89 | .hljs-builtin-name, 90 | .hljs-quote, 91 | .hljs-section, 92 | .hljs-selector-class { 93 | color: #de935f; 94 | } 95 | 96 | .hljs-emphasis { 97 | font-style: italic; 98 | } 99 | 100 | .hljs-strong { 101 | font-weight: bold; 102 | } 103 | -------------------------------------------------------------------------------- /themes/learn/static/css/tags.css: -------------------------------------------------------------------------------- 1 | /* Tags */ 2 | 3 | #head-tags{ 4 | margin-left:1em; 5 | margin-top:1em; 6 | } 7 | 8 | #body .tags a.tag-link { 9 | display: inline-block; 10 | line-height: 2em; 11 | font-size: 0.8em; 12 | position: relative; 13 | margin: 0 16px 8px 0; 14 | padding: 0 10px 0 12px; 15 | background: #8451a1; 16 | 17 | -webkit-border-bottom-right-radius: 3px; 18 | border-bottom-right-radius: 3px; 19 | -webkit-border-top-right-radius: 3px; 20 | border-top-right-radius: 3px; 21 | 22 | -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.2); 23 | box-shadow: 0 1px 2px rgba(0,0,0,0.2); 24 | color: #fff; 25 | } 26 | 27 | #body .tags a.tag-link:before { 28 | content: ""; 29 | position: absolute; 30 | top:0; 31 | left: -1em; 32 | width: 0; 33 | height: 0; 34 | border-color: transparent #8451a1 transparent transparent; 35 | border-style: solid; 36 | border-width: 1em 1em 1em 0; 37 | } 38 | 39 | #body .tags a.tag-link:after { 40 | content: ""; 41 | position: absolute; 42 | top: 10px; 43 | left: 1px; 44 | width: 5px; 45 | height: 5px; 46 | -webkit-border-radius: 50%; 47 | border-radius: 100%; 48 | background: #fff; 49 | } 50 | -------------------------------------------------------------------------------- /themes/learn/static/fonts/Inconsolata.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Inconsolata.eot -------------------------------------------------------------------------------- /themes/learn/static/fonts/Inconsolata.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Inconsolata.ttf -------------------------------------------------------------------------------- /themes/learn/static/fonts/Inconsolata.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Inconsolata.woff -------------------------------------------------------------------------------- /themes/learn/static/fonts/Novecentosanswide-Normal-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Novecentosanswide-Normal-webfont.eot -------------------------------------------------------------------------------- /themes/learn/static/fonts/Novecentosanswide-Normal-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Novecentosanswide-Normal-webfont.ttf -------------------------------------------------------------------------------- /themes/learn/static/fonts/Novecentosanswide-Normal-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Novecentosanswide-Normal-webfont.woff -------------------------------------------------------------------------------- /themes/learn/static/fonts/Novecentosanswide-Normal-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Novecentosanswide-Normal-webfont.woff2 -------------------------------------------------------------------------------- /themes/learn/static/fonts/Novecentosanswide-UltraLight-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Novecentosanswide-UltraLight-webfont.eot -------------------------------------------------------------------------------- /themes/learn/static/fonts/Novecentosanswide-UltraLight-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Novecentosanswide-UltraLight-webfont.ttf -------------------------------------------------------------------------------- /themes/learn/static/fonts/Novecentosanswide-UltraLight-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Novecentosanswide-UltraLight-webfont.woff -------------------------------------------------------------------------------- /themes/learn/static/fonts/Novecentosanswide-UltraLight-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Novecentosanswide-UltraLight-webfont.woff2 -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_200.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_200.eot -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_200.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_200.ttf -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_200.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_200.woff -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_200.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_200.woff2 -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_300.eot -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_300.ttf -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_300.woff -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_300.woff2 -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_500.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_500.eot -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_500.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_500.ttf -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_500.woff -------------------------------------------------------------------------------- /themes/learn/static/fonts/Work_Sans_500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/fonts/Work_Sans_500.woff2 -------------------------------------------------------------------------------- /themes/learn/static/images/clippy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /themes/learn/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-social-network-app-workshop/36be5ef09f2d2f0a18a1f7ca0ddad35d7917f363/themes/learn/static/images/logo.png -------------------------------------------------------------------------------- /themes/learn/static/js/hugo-learn.js: -------------------------------------------------------------------------------- 1 | // Get Parameters from some url 2 | var getUrlParameter = function getUrlParameter(sPageURL) { 3 | var url = sPageURL.split('?'); 4 | var obj = {}; 5 | if (url.length == 2) { 6 | var sURLVariables = url[1].split('&'), 7 | sParameterName, 8 | i; 9 | for (i = 0; i < sURLVariables.length; i++) { 10 | sParameterName = sURLVariables[i].split('='); 11 | obj[sParameterName[0]] = sParameterName[1]; 12 | } 13 | return obj; 14 | } else { 15 | return undefined; 16 | } 17 | }; 18 | 19 | // Execute actions on images generated from Markdown pages 20 | var images = $("div#body-inner img").not(".inline"); 21 | // Wrap image inside a featherlight (to get a full size view in a popup) 22 | images.wrap(function(){ 23 | var image =$(this); 24 | if (!image.parent("a").length) { 25 | return ""; 26 | } 27 | }); 28 | 29 | // Change styles, depending on parameters set to the image 30 | images.each(function(index){ 31 | var image = $(this) 32 | var o = getUrlParameter(image[0].src); 33 | if (typeof o !== "undefined") { 34 | var h = o["height"]; 35 | var w = o["width"]; 36 | var c = o["classes"]; 37 | image.css("width", function() { 38 | if (typeof w !== "undefined") { 39 | return w; 40 | } else { 41 | return "auto"; 42 | } 43 | }); 44 | image.css("height", function() { 45 | if (typeof h !== "undefined") { 46 | return h; 47 | } else { 48 | return "auto"; 49 | } 50 | }); 51 | if (typeof c !== "undefined") { 52 | var classes = c.split(','); 53 | for (i = 0; i < classes.length; i++) { 54 | image.addClass(classes[i]); 55 | } 56 | } 57 | } 58 | }); 59 | 60 | // Stick the top to the top of the screen when scrolling 61 | $(document).ready(function(){ 62 | $("#top-bar").sticky({topSpacing:0, zIndex: 1000}); 63 | }); 64 | 65 | 66 | jQuery(document).ready(function() { 67 | // Add link button for every 68 | var text, clip = new ClipboardJS('.anchor'); 69 | $("h1~h2,h1~h3,h1~h4,h1~h5,h1~h6").append(function(index, html){ 70 | var element = $(this); 71 | var url = encodeURI(document.location.origin + document.location.pathname); 72 | var link = url + "#"+element[0].id; 73 | return " " + 74 | "" + 75 | "" 76 | ; 77 | }); 78 | 79 | $(".anchor").on('mouseleave', function(e) { 80 | $(this).attr('aria-label', null).removeClass('tooltipped tooltipped-s tooltipped-w'); 81 | }); 82 | 83 | clip.on('success', function(e) { 84 | e.clearSelection(); 85 | $(e.trigger).attr('aria-label', 'Link copied to clipboard!').addClass('tooltipped tooltipped-s'); 86 | }); 87 | $('code.language-mermaid').each(function(index, element) { 88 | var content = $(element).html().replace(/&/g, '&'); 89 | $(element).parent().replaceWith('
    ' + content + '
    '); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /themes/learn/static/js/search.js: -------------------------------------------------------------------------------- 1 | var lunrIndex, pagesIndex; 2 | 3 | function endsWith(str, suffix) { 4 | return str.indexOf(suffix, str.length - suffix.length) !== -1; 5 | } 6 | 7 | // Initialize lunrjs using our generated index file 8 | function initLunr() { 9 | if (!endsWith(baseurl,"/")){ 10 | baseurl = baseurl+'/' 11 | }; 12 | 13 | // First retrieve the index file 14 | $.getJSON(baseurl +"index.json") 15 | .done(function(index) { 16 | pagesIndex = index; 17 | // Set up lunrjs by declaring the fields we use 18 | // Also provide their boost level for the ranking 19 | lunrIndex = lunr(function() { 20 | this.ref("uri"); 21 | this.field('title', { 22 | boost: 15 23 | }); 24 | this.field('tags', { 25 | boost: 10 26 | }); 27 | this.field("content", { 28 | boost: 5 29 | }); 30 | 31 | this.pipeline.remove(lunr.stemmer); 32 | this.searchPipeline.remove(lunr.stemmer); 33 | 34 | // Feed lunr with each file and let lunr actually index them 35 | pagesIndex.forEach(function(page) { 36 | this.add(page); 37 | }, this); 38 | }) 39 | }) 40 | .fail(function(jqxhr, textStatus, error) { 41 | var err = textStatus + ", " + error; 42 | console.error("Error getting Hugo index file:", err); 43 | }); 44 | } 45 | 46 | /** 47 | * Trigger a search in lunr and transform the result 48 | * 49 | * @param {String} query 50 | * @return {Array} results 51 | */ 52 | function search(queryTerm) { 53 | // Find the item in our index corresponding to the lunr one to have more info 54 | return lunrIndex.search(queryTerm+"^100"+" "+queryTerm+"*^10"+" "+"*"+queryTerm+"^10"+" "+queryTerm+"~2^1").map(function(result) { 55 | return pagesIndex.filter(function(page) { 56 | return page.uri === result.ref; 57 | })[0]; 58 | }); 59 | } 60 | 61 | // Let's get started 62 | initLunr(); 63 | $( document ).ready(function() { 64 | var searchList = new autoComplete({ 65 | /* selector for the search box element */ 66 | selector: $("#search-by").get(0), 67 | /* source is the callback to perform the search */ 68 | source: function(term, response) { 69 | response(search(term)); 70 | }, 71 | /* renderItem displays individual search results */ 72 | renderItem: function(item, term) { 73 | var numContextWords = 2; 74 | var text = item.content.match( 75 | "(?:\\s?(?:[\\w]+)\\s?){0,"+numContextWords+"}" + 76 | term+"(?:\\s?(?:[\\w]+)\\s?){0,"+numContextWords+"}"); 77 | item.context = text; 78 | return '
    ' + 83 | '» ' + item.title + 84 | '
    ' + 85 | (item.context || '') +'
    ' + 86 | '
    '; 87 | }, 88 | /* onSelect callback fires when a search suggestion is chosen */ 89 | onSelect: function(e, term, item) { 90 | location.href = item.getAttribute('data-uri'); 91 | } 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /themes/learn/theme.toml: -------------------------------------------------------------------------------- 1 | # theme.toml template for a Hugo theme 2 | # See https://github.com/spf13/hugoThemes#themetoml for an example 3 | 4 | name = "Learn" 5 | license = "MIT" 6 | licenselink = "https://github.com/matcornic/hugo-theme-learn/blob/master/LICENSE.md" 7 | description = "Documentation theme for Hugo, based on Grav Learn theme" 8 | homepage = "https://github.com/matcornic/hugo-theme-learn/" 9 | repo = "https://github.com/matcornic/hugo-theme-learn" 10 | tags = ["documentation", "grav", "learn", "doc", "search"] 11 | features = ["documentation", "menu", "nested sections", "search", "mermaid"] 12 | min_version = 0.25 13 | 14 | [author] 15 | name = "Mathieu Cornic" 16 | homepage = "https://matcornic.github.io/" 17 | 18 | [original] 19 | name = "Grav Learn" 20 | homepage = "https://learn.getgrav.org/" 21 | repo = "https://github.com/getgrav/grav-learn" 22 | --------------------------------------------------------------------------------