├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── package.json ├── public ├── amplify-add-api.png ├── amplify-add-auth.png ├── amplify-add-storage.png ├── amplify-init.png ├── architecture_diagram.png ├── demo.gif ├── favicon.ico ├── index.html └── manifest.json ├── schema.graphql └── src ├── App.css ├── App.js ├── App.test.js ├── Components ├── AddPhoto.js └── AllPhotos.js ├── GraphQL ├── MutationCreatePicture.js ├── QueryListPictures.js └── index.js ├── index.css ├── index.js ├── logo.png └── registerServiceWorker.js /.gitignore: -------------------------------------------------------------------------------- 1 | #awsmobilejs 2 | aws-info.json 3 | project-info.json 4 | aws-exports.js 5 | amplify 6 | .amplifyrc 7 | 8 | 9 | # Created by https://www.gitignore.io/api/node 10 | 11 | ### Node ### 12 | # Logs 13 | logs 14 | *.log 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | # Runtime data 20 | pids 21 | *.pid 22 | *.seed 23 | *.pid.lock 24 | 25 | # Directory for instrumented libs generated by jscoverage/JSCover 26 | lib-cov 27 | 28 | # Coverage directory used by tools like istanbul 29 | coverage 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (https://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | jspm_packages/ 49 | 50 | # TypeScript v1 declaration files 51 | typings/ 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variables file 69 | .env 70 | 71 | # parcel-bundler cache (https://parceljs.org/) 72 | .cache 73 | 74 | # next.js build output 75 | .next 76 | 77 | # nuxt.js build output 78 | .nuxt 79 | 80 | # vuepress build output 81 | .vuepress/dist 82 | 83 | # Serverless directories 84 | .serverless 85 | 86 | 87 | # End of https://www.gitignore.io/api/node 88 | -------------------------------------------------------------------------------- /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. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/aws-samples/aws-amplify-graphql/issues), or [recently closed](https://github.com/aws-samples/aws-amplify-graphql/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws-samples/aws-amplify-graphql/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/aws-samples/aws-amplify-graphql/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS AppSync GraphQL Photo Sample 2 | 3 | **Please submit issues to the [appsync-sdk-js](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues) repository.** 4 | 5 | ![Demo](public/demo.gif) 6 | 7 | This sample application shows how to use GraphQL to build an application that a user can login to the system, then upload and download photos which are private to them. The sample is written in React and uses AWS AppSync, Amazon Cognito, Amazon DynamoDB and Amazon S3 as well as the Amplify CLI. 8 | 9 | ## Architecture Overview 10 | 11 | ![Architecture](public/architecture_diagram.png) 12 | 13 | ## Prerequisites 14 | + [AWS Account](https://aws.amazon.com/mobile/details/) 15 | 16 | + [NodeJS](https://nodejs.org/en/download/) with [NPM](https://docs.npmjs.com/getting-started/installing-node) 17 | 18 | + [AWS Ampify CLI](https://aws-amplify.github.io/) 19 | - `npm install -g @aws-amplify/cli` 20 | - `amplify configure` 21 | 22 | ## Getting Started 23 | 24 | 1. Clone this repo locally. 25 | 26 | ``` 27 | git clone https://github.com/aws-samples/aws-amplify-graphql.git 28 | cd aws-amplify-graphql 29 | ``` 30 | 31 | 2. Initialize the amplify project. 32 | 33 | ``` 34 | amplify init 35 | ``` 36 | 37 | 3. Configure an Amazon Cognito User Pool to manage user credentials. 38 | 39 | ``` 40 | amplify add auth 41 | ``` 42 | 43 | ![Architecture](public/amplify-add-auth.png) 44 | 45 | 4. Configure an Amazon S3 bucket to store files. 46 | 47 | ``` 48 | amplify add storage 49 | ``` 50 | 51 | ![Architecture](public/amplify-add-storage.png) 52 | 53 | 5. Configure an AWS AppSync API to interact with my backend data sources such as Amazon DynamoDB, Amazon Elasticsearch, AWS Lambda, and self hosted HTTP services. 54 | 55 | ``` 56 | amplify add api 57 | 58 | # When prompted for a schema.graphql provide the value "schema.graphql" 59 | # to point to the file checked in to the root of the project directory. 60 | ``` 61 | 62 | ![Architecture](public/amplify-add-api.png) 63 | 64 | > After running this command, you edit the schema.graphql located at `amplify/backend/api/<-projectname->/schema.graphql`. You may delete the one at the root of the project directory as it will no longer be used. 65 | 66 | 6. Deploy your project. 67 | 68 | ``` 69 | amplify push 70 | 71 | # When asked if you would like to generate client code, you can 72 | # say no since we are using plain JavaScript. 73 | ``` 74 | 75 | 7. Install client dependencies. 76 | 77 | ``` 78 | npm install 79 | 80 | # or 81 | yarn 82 | ``` 83 | 84 | 8. Run the react application 85 | 86 | ``` 87 | npm run start 88 | 89 | # or 90 | yarn start 91 | ``` 92 | 93 | The AWS Amplify CLI will create an Amazon Cognito User Pool and Identity Pool, an Amazon S3 bucket with private directories to store each user's photo and an AWS AppSync API that uses Amazon DynamoDB to store data. 94 | 95 | The sample uses [AWS Amplify](https://github.com/aws/aws-amplify) to perform the Sign-Up and Sign-In flows with a Higher Order Component. 96 | 97 | If the application runs successfully you should be able to enter the name of a photo, choose a file and then press **Add photo**. This will make a GraphQL call to enter the record into the database and simultaneously upload the object to S3. An immediate fetch of the record will then be at the bottom of the screen. 98 | 99 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "photo-client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "apollo-client": "^2.4.2", 7 | "aws-amplify": "^1.1.6", 8 | "aws-amplify-react": "^2.0.7", 9 | "aws-appsync": "^1.4.0", 10 | "aws-appsync-react": "^1.2.0", 11 | "graphql-tag": "^2.10.0", 12 | "react": "^16.5.2", 13 | "react-apollo": "^2.2.4", 14 | "react-dom": "^16.5.2", 15 | "react-scripts": "^3.4.0", 16 | "semantic-ui-css": "^2.4.1", 17 | "semantic-ui-react": "^0.82.5", 18 | "uuid": "^3.3.2" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test --env=jsdom", 24 | "eject": "react-scripts eject" 25 | }, 26 | "browserslist": [ 27 | ">0.2%", 28 | "not dead", 29 | "not ie <= 11", 30 | "not op_mini all" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /public/amplify-add-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-graphql/b6fb8a5e16d955b4fd832442853181a3c0f1a8ef/public/amplify-add-api.png -------------------------------------------------------------------------------- /public/amplify-add-auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-graphql/b6fb8a5e16d955b4fd832442853181a3c0f1a8ef/public/amplify-add-auth.png -------------------------------------------------------------------------------- /public/amplify-add-storage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-graphql/b6fb8a5e16d955b4fd832442853181a3c0f1a8ef/public/amplify-add-storage.png -------------------------------------------------------------------------------- /public/amplify-init.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-graphql/b6fb8a5e16d955b4fd832442853181a3c0f1a8ef/public/amplify-init.png -------------------------------------------------------------------------------- /public/architecture_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-graphql/b6fb8a5e16d955b4fd832442853181a3c0f1a8ef/public/architecture_diagram.png -------------------------------------------------------------------------------- /public/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-graphql/b6fb8a5e16d955b4fd832442853181a3c0f1a8ef/public/demo.gif -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-graphql/b6fb8a5e16d955b4fd832442853181a3c0f1a8ef/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /schema.graphql: -------------------------------------------------------------------------------- 1 | type Picture @model @auth(rules: [{allow: owner}]) { 2 | id: ID! 3 | name: String 4 | owner: String 5 | visibility: Visibility 6 | file: S3Object 7 | createdAt: String 8 | } 9 | 10 | type S3Object { 11 | bucket: String! 12 | region: String! 13 | key: String! 14 | } 15 | 16 | enum Visibility { 17 | public 18 | private 19 | } 20 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | width: 300px; 7 | } 8 | 9 | .App-header { 10 | background-color: #222; 11 | padding: 20px; 12 | margin-bottom: 20px; 13 | color: white; 14 | } 15 | 16 | .App-title { 17 | font-size: 1.5em; 18 | } 19 | 20 | .App-intro { 21 | font-size: large; 22 | } 23 | 24 | .App-content { 25 | text-align: left; 26 | } 27 | 28 | @keyframes App-logo-spin { 29 | from { transform: rotate(0deg); } 30 | to { transform: rotate(360deg); } 31 | } 32 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.png'; 3 | import './App.css'; 4 | import 'semantic-ui-css/semantic.min.css' 5 | 6 | //AppSync and Apollo libraries 7 | import AWSAppSyncClient from "aws-appsync"; 8 | import { Rehydrated } from 'aws-appsync-react'; 9 | import { ApolloProvider } from 'react-apollo'; 10 | 11 | //Amplify 12 | import Amplify, { Auth } from 'aws-amplify'; 13 | import { withAuthenticator } from 'aws-amplify-react'; 14 | 15 | // Components 16 | import AllPhotos from "./Components/AllPhotos"; 17 | import AddPhoto from "./Components/AddPhoto"; 18 | 19 | import awsconfig from './aws-exports'; 20 | 21 | // Amplify init 22 | Amplify.configure(awsconfig); 23 | 24 | const GRAPHQL_API_REGION = awsconfig.aws_appsync_region 25 | const GRAPHQL_API_ENDPOINT_URL = awsconfig.aws_appsync_graphqlEndpoint 26 | const S3_BUCKET_REGION = awsconfig.aws_user_files_s3_bucket_region 27 | const S3_BUCKET_NAME = awsconfig.aws_user_files_s3_bucket 28 | const AUTH_TYPE = awsconfig.aws_appsync_authenticationType 29 | 30 | // AppSync client instantiation 31 | const client = new AWSAppSyncClient({ 32 | url: GRAPHQL_API_ENDPOINT_URL, 33 | region: GRAPHQL_API_REGION, 34 | auth: { 35 | type: AUTH_TYPE, 36 | // Get the currently logged in users credential. 37 | jwtToken: async () => (await Auth.currentSession()).getAccessToken().getJwtToken(), 38 | }, 39 | // Amplify uses Amazon IAM to authorize calls to Amazon S3. This provides the relevant IAM credentials. 40 | complexObjectsCredentials: () => Auth.currentCredentials() 41 | }); 42 | 43 | class App extends Component { 44 | 45 | render() { 46 | return ( 47 |
48 |
49 | logo 50 |

AWS Amplify with AWS AppSync Sample using Complex Objects

51 |
52 |
53 | 54 | 55 |
56 |
57 | ); 58 | } 59 | } 60 | 61 | const AppWithAuth = withAuthenticator(App, true); 62 | 63 | export default () => ( 64 | 65 | 66 | 67 | 68 | 69 | ); 70 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /src/Components/AddPhoto.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { graphql } from 'react-apollo'; 3 | import { MutationCreatePicture, QueryListPictures } from "../GraphQL"; 4 | import { v4 as uuid } from 'uuid'; 5 | 6 | import { Form, Icon } from 'semantic-ui-react' 7 | 8 | import { Auth } from "aws-amplify"; 9 | 10 | class AddPhoto extends Component { 11 | 12 | constructor(props) { 13 | super(props); 14 | 15 | this.state = this.getInitialState(); 16 | this.fileInput = {}; 17 | 18 | this.handleSubmit = this.handleSubmit.bind(this); 19 | this.handleChange = this.handleChange.bind(this); 20 | } 21 | 22 | getInitialState = () => ({ 23 | name: '', 24 | file: undefined, 25 | lastUpdate: new Date().toISOString() 26 | }); 27 | 28 | handleChange(field, event) { 29 | const { target: { value, files } } = event; 30 | const [file,] = files || []; 31 | this.setState({ 32 | [field]: file || value 33 | }); 34 | } 35 | 36 | async handleSubmit(e) { 37 | e.preventDefault(); 38 | 39 | const { bucket, region } = this.props.options; 40 | const visibility = 'private'; 41 | 42 | const { name, file: selectedFile } = this.state; 43 | const { identityId } = await Auth.currentCredentials(); 44 | const { username: owner } = await Auth.currentUserInfo(); 45 | 46 | let file; 47 | 48 | if (selectedFile) { 49 | const { name: fileName, type: mimeType } = selectedFile; 50 | const [, , , extension] = /([^.]+)(\.(\w+))?$/.exec(fileName); 51 | 52 | const key = `${visibility}/${identityId}/${uuid()}${extension && '.'}${extension}`; 53 | 54 | file = { 55 | bucket, 56 | key, 57 | region, 58 | mimeType, 59 | localUri: selectedFile, 60 | }; 61 | } 62 | 63 | this.setState(this.getInitialState(), () => { 64 | this.fileInput.value = ""; 65 | this.props.createPicture({ name, owner, visibility, file }); 66 | }); 67 | } 68 | 69 | render() { 70 | const isSubmitEnabled = this.state.name !== '' && this.state.file !== undefined; 71 | return ( 72 |
73 |
74 | 75 | 76 | 77 | Add Photo 78 | 79 |
80 |
81 | ); 82 | } 83 | } 84 | 85 | export default graphql( 86 | MutationCreatePicture, 87 | { 88 | options: { 89 | update: (proxy, { data: { createPicture } }) => { 90 | const query = QueryListPictures; 91 | const data = proxy.readQuery({ query }); 92 | data.listPictures.items = [ 93 | ...data.listPictures.items.filter((photo) => photo.id !== createPicture.id), 94 | createPicture 95 | ]; 96 | proxy.writeQuery({ query, data }); 97 | } 98 | }, 99 | props: ({ ownProps, mutate }) => ({ 100 | createPicture: photo => mutate({ 101 | variables: { input: photo }, 102 | optimisticResponse: () => ({ 103 | createPicture: { 104 | ...photo, 105 | id: uuid(), 106 | createdAt: new Date().toISOString(), 107 | __typename: 'Picture', 108 | file: { ...photo.file, __typename: 'S3Object' } 109 | } 110 | }), 111 | }), 112 | }), 113 | } 114 | )(AddPhoto); 115 | -------------------------------------------------------------------------------- /src/Components/AllPhotos.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { graphql } from 'react-apollo'; 3 | import { QueryListPictures } from "../GraphQL"; 4 | 5 | import { Icon, Table, Button, Loader } from 'semantic-ui-react' 6 | 7 | import { Storage } from 'aws-amplify'; 8 | 9 | class AllPhotos extends Component { 10 | 11 | async handleDownload({ visibility: level, file }) { 12 | try { 13 | const { bucket, region, key } = file; 14 | const [, , keyWithoutPrefix] = /([^/]+\/){2}(.*)$/.exec(key) || key; 15 | 16 | const url = await Storage.get(keyWithoutPrefix, { bucket, region, level }); 17 | 18 | window.open(url); 19 | } catch (err) { 20 | console.error(err); 21 | } 22 | } 23 | 24 | render() { 25 | return ( 26 | 27 | 28 | 29 | 30 | PhotoId 31 | Friendly name 32 | Visibility 33 | Owner 34 | Created at 35 |  Download 36 | 37 | 38 | 39 | {this.props.photos && this.props.photos.items && [].concat(this.props.photos.items).sort((a, b) => b.createdAt.localeCompare(a.createdAt)).map(photo => ( 40 | 41 | {photo.file && photo.id} 42 | {photo.name} 43 | {photo.visibility} 44 | {photo.owner} 45 | {photo.file && photo.createdAt} 46 | 47 | {photo.file? : } 48 | 49 | 50 | ))} 51 | 52 |
53 |
54 | ); 55 | } 56 | 57 | } 58 | 59 | export default graphql( 60 | QueryListPictures, 61 | { 62 | options: { 63 | fetchPolicy: 'cache-and-network', 64 | }, 65 | props: ({ data: { listPictures: photos } }) => ({ 66 | photos, 67 | }) 68 | } 69 | )(AllPhotos); 70 | -------------------------------------------------------------------------------- /src/GraphQL/MutationCreatePicture.js: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | 3 | export default gql` 4 | mutation ($input: CreatePictureInput!) { 5 | createPicture(input: $input) { 6 | id 7 | name 8 | visibility 9 | owner 10 | createdAt 11 | file { 12 | region 13 | bucket 14 | key 15 | } 16 | } 17 | }`; 18 | -------------------------------------------------------------------------------- /src/GraphQL/QueryListPictures.js: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | 3 | export default gql` 4 | query { 5 | listPictures(limit: 100) { 6 | items { 7 | id 8 | name 9 | visibility 10 | owner 11 | createdAt 12 | file { 13 | bucket 14 | region 15 | key 16 | } 17 | } 18 | } 19 | }`; 20 | -------------------------------------------------------------------------------- /src/GraphQL/index.js: -------------------------------------------------------------------------------- 1 | export { default as QueryListPictures } from "./QueryListPictures"; 2 | export { default as MutationCreatePicture } from "./MutationCreatePicture"; 3 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-amplify-graphql/b6fb8a5e16d955b4fd832442853181a3c0f1a8ef/src/logo.png -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | } else { 39 | // Is not local host. Just register service worker 40 | registerValidSW(swUrl); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | function registerValidSW(swUrl) { 47 | navigator.serviceWorker 48 | .register(swUrl) 49 | .then(registration => { 50 | registration.onupdatefound = () => { 51 | const installingWorker = registration.installing; 52 | installingWorker.onstatechange = () => { 53 | if (installingWorker.state === 'installed') { 54 | if (navigator.serviceWorker.controller) { 55 | // At this point, the old content will have been purged and 56 | // the fresh content will have been added to the cache. 57 | // It's the perfect time to display a "New content is 58 | // available; please refresh." message in your web app. 59 | console.log('New content is available; please refresh.'); 60 | } else { 61 | // At this point, everything has been precached. 62 | // It's the perfect time to display a 63 | // "Content is cached for offline use." message. 64 | console.log('Content is cached for offline use.'); 65 | } 66 | } 67 | }; 68 | }; 69 | }) 70 | .catch(error => { 71 | console.error('Error during service worker registration:', error); 72 | }); 73 | } 74 | 75 | function checkValidServiceWorker(swUrl) { 76 | // Check if the service worker can be found. If it can't reload the page. 77 | fetch(swUrl) 78 | .then(response => { 79 | // Ensure service worker exists, and that we really are getting a JS file. 80 | if ( 81 | response.status === 404 || 82 | response.headers.get('content-type').indexOf('javascript') === -1 83 | ) { 84 | // No service worker found. Probably a different app. Reload the page. 85 | navigator.serviceWorker.ready.then(registration => { 86 | registration.unregister().then(() => { 87 | window.location.reload(); 88 | }); 89 | }); 90 | } else { 91 | // Service worker found. Proceed as normal. 92 | registerValidSW(swUrl); 93 | } 94 | }) 95 | .catch(() => { 96 | console.log( 97 | 'No internet connection found. App is running in offline mode.' 98 | ); 99 | }); 100 | } 101 | 102 | export function unregister() { 103 | if ('serviceWorker' in navigator) { 104 | navigator.serviceWorker.ready.then(registration => { 105 | registration.unregister(); 106 | }); 107 | } 108 | } 109 | --------------------------------------------------------------------------------