├── .gitignore
├── .graphqlconfig.yml
├── README.md
├── amplify
├── .config
│ └── project-config.json
└── backend
│ ├── analytics
│ └── reactnotes
│ │ ├── parameters.json
│ │ └── pinpoint-cloudformation-template.json
│ ├── api
│ └── reactnotes
│ │ ├── build
│ │ ├── resolvers
│ │ │ ├── Mutation.createNote.request
│ │ │ ├── Mutation.createNote.response
│ │ │ ├── Mutation.deleteNote.request
│ │ │ ├── Mutation.deleteNote.response
│ │ │ ├── Mutation.updateNote.request
│ │ │ ├── Mutation.updateNote.response
│ │ │ ├── Query.getNote.request
│ │ │ ├── Query.getNote.response
│ │ │ ├── Query.listNotes.request
│ │ │ └── Query.listNotes.response
│ │ └── schema.graphql
│ │ ├── cloudformation-template.json
│ │ ├── parameters.json
│ │ └── schema.graphql
│ ├── auth
│ └── reactnotescognitoauth
│ │ ├── parameters.json
│ │ └── reactnotescognitoauth-cloudformation-template.yml
│ ├── awscloudformation
│ └── nested-cloudformation-stack.yml
│ └── backend-config.json
├── auth.jpg
├── hero.jpg
├── notesapp.jpg
├── package.json
├── preview.png
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── src
├── App.css
├── App.js
├── App.test.js
├── components
│ ├── Form.js
│ ├── Note.js
│ └── Notes.js
├── graphql
│ ├── mutations.js
│ ├── queries.js
│ ├── schema.json
│ └── subscriptions.js
├── index.css
├── index.js
├── logo.svg
└── serviceWorker.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
25 | #amplify
26 | amplify/\#current-cloud-backend
27 | amplify/.config/local-*
28 | amplify/backend/amplify-meta.json
29 | aws-exports.js
30 | awsconfiguration.json
--------------------------------------------------------------------------------
/.graphqlconfig.yml:
--------------------------------------------------------------------------------
1 | projects:
2 | reactnotes:
3 | schemaPath: src/graphql/schema.json
4 | includes:
5 | - src/graphql/**/*.js
6 | excludes:
7 | - ./amplify/**
8 | extensions:
9 | amplify:
10 | codeGenTarget: javascript
11 | generatedFileName: ''
12 | docsFilePath: src/graphql
13 | graphQLApiId: v5w4eznaunb6pnuk7ksobutj3m
14 | endpoints:
15 | prod: >-
16 | https://xnxvltjqjzeajlmaq7qc7aas4i.appsync-api.eu-central-1.amazonaws.com/graphql
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Building a ‘Notes’ app with Amplify Framework and React
2 |
3 | ### Overview
4 | In this tutorial, you will create a React 'Notes’ app that connects to a serverless backend via the Amplify Framework. The app will provide user authentication with Amazon Cognito, in-app analytics with Amazon Pinpoint, and it will be connected to a serverless GraphQL backend with AWS AppSync.
5 |
6 | 
7 |
8 | The Amplify Framework enables frontend developers to build apps quickly with a simplified workflow. In this tutorial, you will learn how to create a cloud-enabled React app from scratch using the Amplify Framework. You can deploy this fullstack application to the Amplify Console by clicking the button below.
9 |
10 | [](https://console.aws.amazon.com/amplify/home#/deploy?repo=https://github.com/username/repository)
11 |
12 | #### By completing this tutorial, you will be able to:
13 |
14 | - Bootstrap a React app with Create React App CLI
15 | - Install and configure the Amplify Framework in your app
16 | - Manage your app’s backend with the Amplify CLI
17 | - Add cloud-based authentication to your app
18 | - Create and use a GraphQL backend to store data
19 |
20 | ### Prerequisites
21 | You need to have a basic understanding of JavaScript, Node.js, and NPM to complete this tutorial.
22 |
23 | ### Source Code
24 | You can directly copy and paste code snippets from the tutorial as you are following the steps. Alternatively, if you would like to get right to the source code for the final version of the tutorial’s code, please visit the Github Repo.
25 |
26 | ## Content
27 | Here is the sequence of the tutorial:
28 |
29 | [Part 1: Creating the React App](https://github.com/dabit3/react-notes#part-1-create-a-react-app)
30 | [Part 2: Adding Cloud Features](https://github.com/dabit3/react-notes#part-2-adding-cloud-features)
31 | [Part 3: Enabling GraphQL Backend](https://github.com/dabit3/react-notes#part-3-enabling-graphql-backend)
32 |
33 | # Part 1: Create a React App
34 |
35 | This section introduces React basics. You will learn how to bootstrap a new React app with the Create React App CLI. In subsequent parts of the tutorial, you will gradually add new cloud functionality to your application.
36 |
37 | > If you want to integrate Amplify Framework into an existing React application, you can skip Part 1 and start directly to Part 2.
38 |
39 | ### Install the Create React App CLI and Create a New Project
40 | The easiest way to create an React application is with the Create React App Command Line Interface (CLI). To install the CLI, run the following command in your terminal:
41 |
42 | ```sh
43 | $ npm install -g create-react-app
44 | ```
45 |
46 | After the installation, go to a location where you wish to start your new application project and execute the following command to create a new React app:
47 |
48 | ```sh
49 | $ create-react-app amplify-notes
50 | ```
51 |
52 | ### Install Amplify Framework Libraries
53 |
54 | Now, you have a new React project called react-notes! You can switch to your project’s root folder and install required Amplify Framework libraries.
55 |
56 | ```sh
57 | $ cd react-notes
58 | ```
59 |
60 | Amplify Framework provides npm packages for installation. aws-amplify is the core package and aws-amplify-react includes UI components and React modules that you will use when building you React app.
61 |
62 | Run the following commands to install required packages:
63 |
64 | ```sh
65 | $ npm install aws-amplify aws-amplify-react
66 | ```
67 |
68 | You have installed the required Amplify packages in this step. In Part 2, you will use those packages to cloud-enable your React app.
69 |
70 |
71 | #### Testing your React App
72 |
73 | You can test your React app anytime in a browser by running:
74 |
75 | ```sh
76 | $ npm start
77 | ```
78 |
79 | ## Creating Components
80 |
81 | In this section, we'll create the components that we will use in our React app. These components will at first use local state to manage data. Later on, we'll update our application to work with data from the cloud by adding an AppSync GraphQL API.
82 |
83 | 
84 |
85 | The next thing we'll need to do is install a couple of additional dependencies (Glamor for styling & React Icons for Icons):
86 |
87 | ```sh
88 | npm install react-icons glamor
89 | ```
90 |
91 | Next, we'll need to create a few new files to hold our components.
92 |
93 | In the `src` directory, create a `components` directory with three new components: __Form.js__, __Note.js__, & __Notes.js__:
94 |
95 | ```sh
96 | mkdir components
97 | touch components/Form.js components/Note.js components/Notes.js
98 | ```
99 |
100 | Next, we'll update App.js. App.js will hold most of the logic for creating, updating & deleting items in our app.
101 |
102 | First, let's import everything we'll need for the component:
103 |
104 | ```js
105 | // src/App.js
106 |
107 | import { css } from 'glamor'
108 |
109 | import Form from './components/Form'
110 | import Notes from './components/Notes'
111 | ```
112 |
113 | Next, in our class, we'll define the initial state:
114 |
115 | ```js
116 | state = { notes: [], filter: 'none' }
117 | ```
118 |
119 | - The `notes` array will hold the notes we will be rendering in our app.
120 | - The `filter` value will hold the current filter for the type notes that we are viewing (all notes, completed notes, or pending notes).
121 |
122 | Next, we'll define the methods we'll be needing:
123 |
124 | ```js
125 | createNote = async note => {
126 | const notes = [note, ...this.state.notes]
127 | const newNotes = this.state.notes
128 | this.setState({ notes })
129 | }
130 |
131 | updateNote = async note => {
132 | const updatedNote = {
133 | ...note,
134 | status: note.status === 'new' ? 'completed' : 'new'
135 | }
136 | const index = this.state.notes.findIndex(i => i.id === note.id)
137 | const notes = [...this.state.notes]
138 | notes[index] = updatedNote
139 | this.setState({ notes })
140 | }
141 |
142 | deleteNote = async note => {
143 | const input = { id: note.id }
144 | const notes = this.state.notes.filter(n => n.id !== note.id)
145 | this.setState({ notes })
146 | }
147 |
148 | updateFilter = filter => this.setState({ filter })
149 | ```
150 |
151 | - `createNote` - This method creates a new note in the state.
152 |
153 | - `updateNote` - This method updates the `status` of the note to be either __completed__ or __new__.
154 |
155 | - `deleteNote` - This method deletes the note from the state.
156 |
157 | - `updateFilter` - This method changes the filter type that we are currently viewing.
158 |
159 | Next, we'll update the render method to the following:
160 |
161 | ```js
162 | render() {
163 | let { notes, filter } = this.state
164 | if (filter === 'completed') {
165 | notes = notes.filter(n => n.status === 'completed')
166 | }
167 | if (filter === 'new') {
168 | notes = notes.filter(n => n.status === 'new')
169 | }
170 | return (
171 |
172 |
Notes
173 |
176 |
181 |
182 |
this.updateFilter('none')}
184 | {...css([ styles.menuItem, getStyle('none', filter)])}
185 | >All
186 |
this.updateFilter('completed')}
188 | {...css([styles.menuItem, getStyle('completed', filter)])}
189 | >Completed
190 |
this.updateFilter('new')}
192 | {...css([styles.menuItem, getStyle('new', filter)])}
193 | >Pending
194 |
195 |
196 | );
197 | }
198 | ```
199 |
200 | - We first destructure the `notes` array & the `filter` value from the state.
201 | - Next, we apply the filter on the notes array if there is a filter that matches either `completed` or `new`.
202 | - We return the __Form__ & __Notes__ components as well as some UI to apply the filter.
203 |
204 | Finally, we have a few styles that we create to style our UI:
205 |
206 | ```js
207 | const styles = {
208 | container: {
209 | width: 360,
210 | margin: '0 auto',
211 | borderBottom: '1px solid #ededed',
212 | },
213 | form: {
214 | display: 'flex',
215 | justifyContent: 'center',
216 | alignItems: 'center'
217 | },
218 | input: {
219 | height: 35,
220 | width: '360px',
221 | border: 'none',
222 | outline: 'none',
223 | marginLeft: 10,
224 | fontSize: 20,
225 | padding: 8,
226 | }
227 | }
228 | ```
229 |
230 | Now, let's take a look at the __Note__ component (components/Note.js):
231 |
232 | ```js
233 | // src/components/Note.js
234 |
235 | import React from 'react'
236 | import { css } from 'glamor'
237 | import { FaTimes, FaCircle } from 'react-icons/fa'
238 | import { MdCheckCircle } from 'react-icons/md';
239 |
240 | class Note extends React.Component {
241 | render() {
242 | const { name, status } = this.props.note
243 | return (
244 |
245 | {
246 | status === 'new' && (
247 |
this.props.updateNote(this.props.note)}
252 | />
253 | )
254 | }
255 | {
256 | status === 'completed' && (
257 | this.props.updateNote(this.props.note)}
262 | />
263 | )
264 | }
265 | {name}
266 |
267 | this.props.deleteNote(this.props.note)}
269 | color='red'
270 | size={22}
271 | {...css(styles.times)}
272 | />
273 |
274 |
275 | )
276 | }
277 | }
278 |
279 | const styles = {
280 | container: {
281 | borderBottom: '1px solid rgba(0, 0, 0, .15)',
282 | display: 'flex',
283 | alignItems: 'center'
284 | },
285 | name: {
286 | textAlign: 'left',
287 | fontSize: 18
288 | },
289 | iconContainer: {
290 | display: 'flex',
291 | flex: 1,
292 | justifyContent: 'flex-end',
293 | alignItems: 'center'
294 | },
295 | new: {
296 | marginRight: 10,
297 | cursor: 'pointer',
298 | opacity: .3
299 | },
300 | completed: {
301 | marginRight: 10,
302 | cursor: 'pointer'
303 | },
304 | times: {
305 | cursor: 'pointer',
306 | opacity: 0.7
307 | }
308 | }
309 |
310 | export default Note
311 | ```
312 |
313 | This component is pretty basic: we return the note `name` as well as some UI for updating & deleting the note.
314 |
315 | If the note is `new`, we show an empty circle next to the note.
316 |
317 | If the note is `completed`, we show a circle with a checkmark next to it. If the circle is clicked, we call the `updateNote` method passed down as props & toggle the `completed` state..
318 |
319 | We also have a red __x__ that deletes the note when clicked by calling the `deleteNote` method passed down as props.
320 |
321 | Next, let's take a look at __Notes__ component (Notes.js):
322 |
323 | ```js
324 | // src/components/Notes.js
325 |
326 | import React from 'react'
327 | import { css } from 'glamor'
328 |
329 | import Note from './Note'
330 |
331 | class Notes extends React.Component {
332 | render() {
333 | return (
334 |
335 | {
336 | this.props.notes.map((t, i) => (
337 |
343 | ))
344 | }
345 |
346 | )
347 | }
348 | }
349 |
350 | const styles = {
351 | container: {
352 | width: '360px',
353 | margin: '0 auto',
354 | '@media(max-width: 360px)': {
355 | width: 'calc(100% - 40px)'
356 | }
357 | }
358 | }
359 |
360 | export default Notes
361 | ```
362 |
363 | In this component, we map over all of the notes (passed in as `this.props.notes`), & render a __Note__ component for each item in the array.
364 |
365 | We pass in the `deleteNote` & `updateNote` methods as props to each __Note__ component along with the note.
366 |
367 | Finally, let's take a look at the __Form__ component (components/Form.js):
368 |
369 | ```js
370 | // src/components/Form.js
371 |
372 | import React from 'react'
373 | import { css } from 'glamor'
374 | import { MdAdd } from 'react-icons/md'
375 |
376 | class Form extends React.Component {
377 | state = { name: '' }
378 | onChange = e => {
379 | this.setState({ name: e.target.value })
380 | }
381 | handleKeyPress = (e) => {
382 | if (e.key === 'Enter' && this.state.name !== '') {
383 | const note = {
384 | ...this.state, status: 'new'
385 | }
386 | this.props.createNote(note)
387 | this.setState({ name: '' })
388 | }
389 | }
390 | render() {
391 | return (
392 |
393 |
394 |
395 | this.onChange(e)}
400 | value={this.state.name}
401 | />
402 |
403 |
404 | )
405 | }
406 | }
407 |
408 | const styles = {
409 | container: {
410 | width: 360,
411 | margin: '0 auto',
412 | borderBottom: '1px solid #ededed',
413 | },
414 | form: {
415 | display: 'flex',
416 | justifyContent: 'center',
417 | alignItems: 'center'
418 | },
419 | input: {
420 | height: 35,
421 | width: '360px',
422 | border: 'none',
423 | outline: 'none',
424 | marginLeft: 10,
425 | fontSize: 20,
426 | padding: 8,
427 | }
428 | }
429 |
430 | export default Form
431 | ```
432 |
433 | This component renders a basic form. In the component we listen for an __enter__ `keyPress` event. If the key is the __Enter__ key, we call `this.props.createNote`, passing in the value in the text input.
434 |
435 | ## Testing Your Components
436 |
437 | Now, a working shell for our application if completed & should work with local state. Let's test it out:
438 |
439 | ```sh
440 | npm start
441 | ```
442 |
443 | You should be able to create, update & delete todos in your app. You'll notice that when you refresh, the data goes away. We'll fix this in a later step by storing our data in an AppSync API & fetching it when the app loads.
444 |
445 | # Part 2: Adding Cloud Features
446 |
447 | In this section, you will cloud enable your React app using the Amplify CLI.
448 |
449 | ### Install Amplify CLI
450 |
451 | Amplify CLI is the command line tool that you will use to create and manage the backend for your React app. In the upcoming sections, you will use Amplify CLI to simplify various operations. The CLI enables you to create and configure your backend quickly, even without leaving the command line!
452 |
453 | ### Installing and Configuring the CLI
454 |
455 | To use Amplify CLI with your project, you need to install it to your local development machine and configure it with your AWS credentials. Configuration is a one-time effort; once you configure the CLI, you can use it on multiple projects on your local machine. Because the CLI creates backend resources for you, it needs to utilize an AWS account with appropriate IAM permissions. During the configuration step, a new IAM role will be automatically created on your AWS account.
456 |
457 | To install and configure the Amplify CLI, run the following commands:
458 |
459 | ```sh
460 | $ npm install -g @aws-amplify/cli
461 |
462 | $ npm amplify configure
463 | ```
464 |
465 | > For a video walkthrough of how to configure the Amplify CLI, check out [this](https://www.youtube.com/watch?v=fWbM5DLh25U) video.
466 |
467 | ### Amplify CLI vs. AWS Console
468 |
469 | The backend resources that are created by the CLI is available to you through the AWS Console, e.g., you can access your Amazon Cognito User Pool on the AWS Console after you enable auth with Amplify CLI.
470 |
471 | > To learn about Amplify CLI, visit the CLI developer documentation.
472 |
473 | ### Initialize Your Backend
474 |
475 | To initialize your backend, run the following command in your project’s root folder:
476 |
477 | ```sh
478 | $ amplify init
479 | ```
480 |
481 | The CLI guides you through the options for your project. Select `React` as your framework when prompted:
482 |
483 | ```sh
484 | Please tell us about your project
485 | ? What javascript framework are you using
486 | ❯ react
487 | react-native
488 | angular
489 | ionic
490 | vue
491 | none
492 | ```
493 |
494 | When the CLI successfully configures your backend, the backend configuration files are saved to `/amplify` folder. You don’t need to manually edit the content of this folder as it is maintained by the CLI.
495 |
496 | ### Adding Analytics
497 |
498 | Let’s add our first backend feature to our app, Analytics. Adding Analytics won’t change the user experience though, but it will provide valuable metrics that you can track in Amazon Pinpoint dashboard.
499 |
500 | While enabling Analytics, you will also learn how to use Amplify CLI and configure your app to work with various backend services.
501 |
502 | ### How the Amplify CLI works?
503 |
504 | When you deploy your backend with Amplify CLI, here is what happens under the hood:
505 |
506 | 1. The CLI creates and provisions related resources on your account
507 | 2. The CLI updates your ‘/amplify’ folder, which has all the relevant information about your backend on AWS
508 | 3. The CLI updates the configuration file aws-exports.js with the latest resource credentials
509 |
510 | As a front-end developer, you need to import the auto generated aws-exports.js configuration file in your React app, and configure your app with Amplify.configure() method call.
511 |
512 | So, to enable analytics to your application, run the following commands:
513 |
514 | ```sh
515 | $ amplify add analytics
516 | $ amplify push
517 | ```
518 |
519 | After successfully executing the push command, the CLI updates your configuration file _aws-exports.js_ in your ‘/src’ folder with the references to the newly created resources.
520 |
521 | ## Working with The Configuration File
522 |
523 | The next step is to import _aws-exports.js_ configuration file into your app.
524 |
525 | To configure your app, open __src/App.js__ file and make the following changes in code:
526 |
527 | ```js
528 | import Amplify, { Analytics } from 'aws-amplify';
529 | import aws_exports from './aws-exports';
530 |
531 | Amplify.configure(aws_exports);
532 | ```
533 |
534 | ### Monitoring App Analytics
535 |
536 | Refresh your application a couple of times, and then you will automatically start receiving usage metrics in the Amazon Pinpoint console.
537 |
538 | To view the Amazon Pinpoint console, visit [https://console.aws.amazon.com/pinpoint/home/](https://console.aws.amazon.com/pinpoint/home/) & select the region where you created the service.
539 |
540 | Since your application doesn’t have much functionality at the moment, only ‘session start’ events are displayed in Pinpoint Console. As you add more cloud features to your app - like authentication - Amplify will automatically report related analytics events to Amazon Pinpoint. So, you will know how your users are interacting with your app.
541 |
542 | ### Adding Authentication
543 |
544 | Now that you know how to utilize Amplify CLI to enable backend services, you can continue to add new features to your React app easily.
545 |
546 | User authentication will be the next cloud feature you will enable.
547 |
548 | If you have been following the tutorial from the start and enabled Analytics in the previous step, auth is already enabled for your app (analytics needs secure credentials for reporting metrics). In this case, you just need to run update auth command to create a User Pool that will store your registered users:
549 |
550 | ```sh
551 | $ amplify update auth
552 |
553 | > Do you want to use the default authentication and security configuration? Yes, use the default configuration.
554 |
555 | $ amplify push
556 | ```
557 |
558 | If you have not enabled Analytics earlier, you will be using auth features for the first time. Run the following command:
559 |
560 | ```sh
561 | $ amplify add auth
562 | $ amplify push
563 | ```
564 |
565 | When prompted by the CLI, chose ‘default configuration’:
566 |
567 | ```sh
568 | Do you want to use the default authentication and security configuration?
569 | ❯ Yes, use the default configuration.
570 | ```
571 |
572 | > AWS Amplify’s Authentication category works with Amazon Cognito, a cloud-based authentication and federation service that enables you to manage user access to your applications.
573 |
574 | ## Enabling the UI Components for Auth
575 |
576 | One of the most important benefits of Amplify Framework is that you don’t need to implement standard app features - like user authentication - from scratch. Amplify provides UI components that you can integrate into your app with just a few lines of code.
577 |
578 | ## Rendering the Auth UI
579 |
580 | Now, let’s put the auth UI components in our home page. We'll be using the `withAuthenticator` higher order component from the _aws-amplify-react_ library.
581 |
582 | The `withAuthenticator` component renders a pre-built sign-in and sign-up flow with full-fledged auth functionality like user registration, password reminders, and Multi-factor Authentication.
583 |
584 | Open __src/App.js__. Import the `withAuthenticator` component & replace the default export for the component:
585 |
586 | ```js
587 | import { withAuthenticator } from 'aws-amplify-react'
588 |
589 | export default withAuthenticator(App, { includeGreetings: true })
590 | ```
591 |
592 | ### Test Your Auth Flow
593 |
594 | Now, refresh your app. Once your application loads, you will see login/signup controls.
595 |
596 | 
597 |
598 | ### Where is the user data stored?
599 |
600 | When a new user registers through the `withAuthenticator` component, the user data is stored in your Cognito User Pool. A user pool is a user directory in Amazon Cognito. With a user pool, your users can sign in to your app through Amazon Cognito. You can visit [Amazon Cognito console](https://console.aws.amazon.com/cognito/home), and see the list of registered users by selecting the User Pool that is created for your app.
601 |
602 | # Part 3: Enabling GraphQL Backend
603 |
604 | So far, your app is powered by Amazon Cognito User Pools, but we do not yet have any real yet as far as storing data. In this part, you will integrate your app with a GraphQL API (powered by AWS AppSync) that will store your notes in a NoSQL database (Amazon DynamoDB).
605 |
606 | ### What is GraphQL?
607 |
608 | GraphQL is a modern way of building APIs for your apps and interacting with your backend. It has many benefits over REST – such as using a single endpoint, powerful query syntax, data aggregation from multiple sources and a type system to describe the data – but the overall goal is to make your front-end development experience easy and more productive.
609 |
610 | The Amplify CLI will also help you when creating the GraphQL backend.
611 |
612 | ### Create a GraphQL API
613 |
614 | In the root folder of your app project, run the following command:
615 |
616 | ```sh
617 | amplify add api
618 | ```
619 |
620 | Then, select GraphQL as the service type:
621 |
622 | ```sh
623 | ? Please select from one of the below mentioned services (Use arrow keys)
624 | ❯ GraphQL
625 | REST
626 | ```
627 |
628 | > API category supports GraphQL and REST endpoints. In this tutorial, we will create our backend on GraphQL, which uses AWS AppSync.
629 |
630 | When you select GraphQL as the service type, the CLI offers you options to create a schema. A schema defines the data model for your GraphQL backend.
631 |
632 | Select Single object with fields when prompted What best describes your project?. This option will create a GraphQL backend data model which we can modify and use in our app:
633 |
634 | ```sh
635 | ? Provide API name: reactnotes
636 | ? Choose an authorization type for the API Amazon Cognito User Pool
637 | Use a Cognito user pool configured as a part of this project
638 | ? Do you have an annotated GraphQL schema? No
639 | ? Do you want a guided schema creation? true
640 | ? What best describes your project:
641 | ? What best describes your project: Single object with fields (e.g., “Todo” with ID, name, description)
642 | ? Do you want to edit the schema now? Yes
643 | ```
644 |
645 | This should open a GraphQL schema in your text editor (located at amplify/backend/api/reactnotes/schema.graphql).
646 |
647 | Update the schema to the following & save the file:
648 |
649 | ```graphql
650 | type Note @model {
651 | id: ID!
652 | name: String!
653 | description: String
654 | status: String!
655 | }
656 | ```
657 |
658 | Deploy your GraphQL backend:
659 |
660 | ```sh
661 | $ amplify push
662 | ```
663 |
664 | When prompted, choose code generation language target as JavaScript:
665 |
666 | ```sh
667 | ? Do you want to generate code for your newly created GraphQL API: Yes
668 | ? Choose the code generation language target: javascript
669 | ? Enter the file name pattern of graphql queries, mutations and subscriptions: src/graphql/**/*.js
670 | ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions: Yes
671 | ```
672 |
673 | ### Using GraphQL Queries and Mutations
674 |
675 | When working with a GraphQL API, you pass queries - or mutations - to the GraphQL endpoint. Queries are used for read operations, and mutations perform create, update & delete operations.
676 |
677 | A query/mutation has a simple, JSON-like format, but you don’t need to write it manually. Instead, the Amplify CLI can auto-generate queries and mutations for you.
678 |
679 | ## Auto-Generating Queries/Mutations
680 |
681 | When you updated your GraphQL schema, selecting Do you want to update code for your updated GraphQL API option generated queries and mutations that you need. The CLI also saved your generated queries and mutations under ‘src/graphql’ folder.
682 |
683 | > You can auto-generate queries and mutations anytime by running the `$ amplify codegen` command.
684 |
685 | When you check the `src/graphql` folder, you will see that the CLI has generated a list of queries, mutations, & subscriptions for common data operations. You will use them to perform CRUD (create-read-update-delete) operations on your data:
686 |
687 | | TYPE | OPERATIONS |
688 | | ------------- | ------------- |
689 | | query | getNote , listNotes |
690 | | mutation | createNote , updateNote , deleteNote |
691 |
692 | ## Running Queries/Mutations
693 |
694 | To run a query or mutation, you import it in your app, and use Amplify `API` category to perform the operation:
695 |
696 | ```js
697 | import Amplify, { API, graphqlOperation } from "aws-amplify";
698 | import { listNotes } from './graphql/queries';
699 |
700 | const allNotes = await API.graphql(graphqlOperation(listNotes));
701 | console.log(allNotes);
702 | ```
703 |
704 | Let's take a look at how to do this in our app.
705 |
706 | ## Connecting to the GraphQL Backend & Updating Your App
707 |
708 | Currently, your GraphQL API and related backend resources (an Amazon DynamoDB table that stores the data) have been deployed to the cloud. Now, we can update the functionality to our app by integrating the GraphQL backend.
709 |
710 | To do so, we'll open __App.js__ & do the following:
711 |
712 | 1. Import the components & queries we'll need to interact with the API:
713 |
714 | ```js
715 | // import API & graphqlOperation helpers from AWS Amplify
716 | import { API, graphqlOperation } from 'aws-amplify'
717 |
718 | // import the mutations & queries we'll need to interact with the API
719 | import { createNote, updateNote, deleteNote } from './graphql/mutations'
720 | import { listNotes } from './graphql/queries'
721 | ```
722 |
723 | 2. Add a __ComponentDidMount__ lifecycle method to fetch the data from the AppSync API when the app loads & update the state:
724 |
725 | ```js
726 | async componentDidMount() {
727 | try {
728 | const { data: { listNotes: { items }}} = await API.graphql(graphqlOperation(listNotes))
729 | this.setState({ notes: items })
730 | } catch (err) {
731 | console.log('error fetching notes...', err)
732 | }
733 | }
734 | ```
735 |
736 | Here, we call the API & fetch all of the notes. When the data is returned, we update the notes array to the data that was returned from the API.
737 |
738 | 3. Update the __createNote__ method to create the note in the AppSync API:
739 |
740 | ```js
741 | createNote = async note => {
742 | const notes = [note, ...this.state.notes]
743 | const newNotes = this.state.notes
744 | this.setState({ notes })
745 | try {
746 | const data = await API.graphql(graphqlOperation(createNote, { input: note }))
747 | this.setState({ notes: [data.data.createNote, ...newNotes] })
748 | } catch (err) {
749 | console.log('error creating note..', err)
750 | }
751 | }
752 | ```
753 |
754 | This method calls the API & creates a new note. We still provide an optimistic response as to update the UI as soon as the user creates the note without waiting for the response from the API.
755 |
756 | 4. Update the __updateNote__ method to update the note in the AppSync API:
757 |
758 | ```js
759 | updateNote = async note => {
760 | const updatedNote = {
761 | ...note,
762 | status: note.status === 'new' ? 'completed' : 'new'
763 | }
764 | const index = this.state.notes.findIndex(i => i.id === note.id)
765 | const notes = [...this.state.notes]
766 | notes[index] = updatedNote
767 | this.setState({ notes })
768 |
769 | try {
770 | await API.graphql(graphqlOperation(updateNote, { input: updatedNote }))
771 | } catch (err) {
772 | console.log('error updating note...', err)
773 | }
774 | }
775 | ```
776 |
777 | This method updates the status of the note to be either completed or new. We also provide an optimistic response.
778 |
779 | 5. Update the __deleteNote__ method to delete the note in the AppSync API:
780 |
781 | ```js
782 | deleteNote = async note => {
783 | const input = { id: note.id }
784 | const notes = this.state.notes.filter(n => n.id !== note.id)
785 | this.setState({ notes })
786 | try {
787 | await API.graphql(graphqlOperation(deleteNote, { input }))
788 | } catch (err) {
789 | console.log('error deleting note...', err)
790 | }
791 | }
792 | ```
793 |
794 | This method deletes the note from the API. We also still provide an optimistic response.
795 |
796 | ### Testing it out
797 |
798 | You have just implemented GraphQL queries and mutations in your CRUD functions. Now, you can test your app and verify that the app data is persisted using your GraphQL backend!
799 |
800 | ```sh
801 | npm start
802 | ```
803 |
804 | Inspecting a GraphQL Mutation
805 |
806 | Let’s see what is happening under the hood. Run your app, and add a new item while you monitor the network traffic in your browser’s developer tools. Here is what you will see for a mutation HTTP request:
807 |
808 | 
809 |
810 | When you check the request header, you will notice that the Request Payload has the note item data in JSON format.
811 |
812 | __Congratulations! You now have a cloud-powered React app!__
813 |
814 | You’ve persisted your app’s data using AWS AppSync and Amazon DynamoDB.
815 |
816 | ### What’s next?
817 |
818 | You have completed this tutorial. However, if you like to improve your app even further, here are some ideas you can consider.
819 |
820 | #### Deploy your app using the Amplify Console
821 |
822 | The AWS Amplify Console is a continuous deployment and hosting service for mobile web applications. The AWS Amplify Console makes it easy for you to rapidly release new features, helps you avoid downtime during application deployment, and handles the complexity of simultaneously updating the frontend and backend of your applications. To learn more about the Amplify console, check out [the documentation](https://docs.aws.amazon.com/amplify/latest/userguide/welcome.html).
823 |
824 | #### Work with User Data
825 |
826 | You may have noticed that our Note GraphQL schema doesn’t have a user id field. It means that the notes list is not personalized for your app users. To fix that, you can retrieve the user id after login, and use it when you work with data. When a user is logged in, you may also like to use the user profile information in your app, like displaying the username or profile picture. Learn more about User Attributes here.
827 |
828 | #### Use GraphQL Subscriptions
829 |
830 | In addition to queries and mutations, you can use GraphQL subscriptions with AWS AppSync and enable real-time data in your app. Think of a user experience which you share your notes with your friends and all of you create and edit items at the same time. Learn more about subscriptions here.
831 |
832 | #### Add Search
833 |
834 | You can add search functionality to your app. This will be very easy by adding a @searchable directive in your GraphQL schema. Learn more about here.
835 |
836 | #### Add Images
837 |
838 | You can add an image attachment feature for notes. This can be simply done by enabling complex object types in your GraphQL schema. Learn more about here.
839 |
--------------------------------------------------------------------------------
/amplify/.config/project-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "react-notes",
3 | "version": "1.0",
4 | "frontend": "javascript",
5 | "javascript": {
6 | "framework": "react",
7 | "config": {
8 | "SourceDir": "src",
9 | "DistributionDir": "build",
10 | "BuildCommand": "npm run-script build",
11 | "StartCommand": "npm run-script start"
12 | }
13 | },
14 | "providers": [
15 | "awscloudformation"
16 | ]
17 | }
--------------------------------------------------------------------------------
/amplify/backend/analytics/reactnotes/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": "reactnotes",
3 | "roleName": "pinpointLambdaRoleda8c3f26",
4 | "cloudWatchPolicyName": "cloudWatchPolicyda8c3f26",
5 | "pinpointPolicyName": "pinpointPolicyda8c3f26",
6 | "authPolicyName": "pinpoint_amplify_da8c3f26",
7 | "unauthPolicyName": "pinpoint_amplify_da8c3f26",
8 | "authRoleName": {
9 | "Ref": "AuthRoleName"
10 | },
11 | "unauthRoleName": {
12 | "Ref": "UnauthRoleName"
13 | },
14 | "authRoleArn": {
15 | "Fn::GetAtt": [
16 | "AuthRole",
17 | "Arn"
18 | ]
19 | }
20 | }
--------------------------------------------------------------------------------
/amplify/backend/analytics/reactnotes/pinpoint-cloudformation-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "AWSTemplateFormatVersion": "2010-09-09",
3 | "Description": "Pinpoint resource stack creation using Amplify CLI",
4 | "Parameters": {
5 | "appName": {
6 | "Type": "String"
7 | },
8 | "appId": {
9 | "Type": "String",
10 | "Default": "NONE"
11 | },
12 | "roleName": {
13 | "Type": "String"
14 | },
15 | "cloudWatchPolicyName": {
16 | "Type": "String"
17 | },
18 | "pinpointPolicyName": {
19 | "Type": "String"
20 | },
21 | "authPolicyName": {
22 | "Type": "String"
23 | },
24 | "unauthPolicyName": {
25 | "Type": "String"
26 | },
27 | "authRoleName": {
28 | "Type": "String"
29 | },
30 | "unauthRoleName": {
31 | "Type": "String"
32 | },
33 | "authRoleArn": {
34 | "Type": "String"
35 | },
36 | "env": {
37 | "Type": "String"
38 | }
39 | },
40 | "Metadata": {
41 | "AWS::CloudFormation::Interface": {
42 | "ParameterGroups": [
43 | {
44 | "Label": {
45 | "default": "Creating pinpoint app"
46 | },
47 | "Parameters": [
48 | "appName"
49 | ]
50 | }
51 | ]
52 | }
53 | },
54 | "Conditions": {
55 | "ShouldCreatePinpointApp": {
56 | "Fn::Equals": [
57 | {
58 | "Ref": "appId"
59 | },
60 | "NONE"
61 | ]
62 | },
63 | "ShouldNotCreateEnvResources": {
64 | "Fn::Equals": [
65 | {
66 | "Ref": "env"
67 | },
68 | "NONE"
69 | ]
70 | }
71 | },
72 | "Resources": {
73 | "LambdaExecutionRole": {
74 | "Condition": "ShouldCreatePinpointApp",
75 | "Type": "AWS::IAM::Role",
76 | "Properties": {
77 | "RoleName": {
78 | "Fn::If": [
79 | "ShouldNotCreateEnvResources",
80 | {
81 | "Ref": "roleName"
82 | },
83 | {
84 | "Fn::Join": [
85 | "",
86 | [
87 | {
88 | "Ref": "roleName"
89 | },
90 | "-",
91 | {
92 | "Ref": "env"
93 | }
94 | ]
95 | ]
96 | }
97 | ]
98 | },
99 | "AssumeRolePolicyDocument": {
100 | "Version": "2012-10-17",
101 | "Statement": [
102 | {
103 | "Effect": "Allow",
104 | "Principal": {
105 | "Service": [
106 | "lambda.amazonaws.com"
107 | ]
108 | },
109 | "Action": [
110 | "sts:AssumeRole"
111 | ]
112 | }
113 | ]
114 | },
115 | "Policies": [
116 | {
117 | "PolicyName": {
118 | "Ref": "cloudWatchPolicyName"
119 | },
120 | "PolicyDocument": {
121 | "Version": "2012-10-17",
122 | "Statement": [
123 | {
124 | "Effect": "Allow",
125 | "Action": [
126 | "logs:CreateLogGroup",
127 | "logs:CreateLogStream",
128 | "logs:PutLogEvents"
129 | ],
130 | "Resource": "arn:aws:logs:*:*:*"
131 | }
132 | ]
133 | }
134 | },
135 | {
136 | "PolicyName": {
137 | "Ref": "pinpointPolicyName"
138 | },
139 | "PolicyDocument": {
140 | "Version": "2012-10-17",
141 | "Statement": [
142 | {
143 | "Effect": "Allow",
144 | "Action": [
145 | "mobileanalytics:*",
146 | "mobiletargeting:*"
147 | ],
148 | "Resource": "*"
149 | }
150 | ]
151 | }
152 | }
153 | ]
154 | }
155 | },
156 | "PinpointFunction": {
157 | "Type": "AWS::Lambda::Function",
158 | "Condition": "ShouldCreatePinpointApp",
159 | "Properties": {
160 | "Code": {
161 | "ZipFile": {
162 | "Fn::Join": [
163 | "\n",
164 | [
165 | "const response = require('cfn-response');",
166 | "const aws = require('aws-sdk');",
167 | "exports.handler = function(event, context) {",
168 | " if (event.RequestType == 'Delete') {",
169 | " response.send(event, context, response.SUCCESS);",
170 | " return;",
171 | " }",
172 | " if (event.RequestType == 'Update') {",
173 | " response.send(event, context, response.SUCCESS);",
174 | " return;",
175 | " }",
176 | " if (event.RequestType == 'Create') {",
177 | " const appName = event.ResourceProperties.appName;",
178 | " let responseData = {};",
179 | " const params = {",
180 | " CreateApplicationRequest: {",
181 | " Name: appName",
182 | " }",
183 | " };",
184 | " const pinpoint = new aws.Pinpoint({ apiVersion: '2016-12-01', region: event.ResourceProperties.region });",
185 | " return pinpoint.createApp(params).promise()",
186 | " .then((res) => {",
187 | " responseData = res.ApplicationResponse;",
188 | " response.send(event, context, response.SUCCESS, responseData);",
189 | " }).catch((err) => {",
190 | " console.log(err.stack);",
191 | " responseData = {Error: err};",
192 | " response.send(event, context, response.FAILED, responseData);",
193 | " throw err;",
194 | " });",
195 | " }",
196 | "};"
197 | ]
198 | ]
199 | }
200 | },
201 | "Handler": "index.handler",
202 | "Runtime": "nodejs6.10",
203 | "Timeout": "300",
204 | "Role": {
205 | "Fn::GetAtt": [
206 | "LambdaExecutionRole",
207 | "Arn"
208 | ]
209 | }
210 | }
211 | },
212 | "PinpointFunctionOutputs": {
213 | "Type": "Custom::LambdaCallout",
214 | "Condition": "ShouldCreatePinpointApp",
215 | "Properties": {
216 | "ServiceToken": {
217 | "Fn::GetAtt": [
218 | "PinpointFunction",
219 | "Arn"
220 | ]
221 | },
222 | "region": {
223 | "Fn::FindInMap": [
224 | "RegionMapping",
225 | {
226 | "Ref": "AWS::Region"
227 | },
228 | "pinpointRegion"
229 | ]
230 | },
231 | "appName": {
232 | "Fn::If": [
233 | "ShouldNotCreateEnvResources",
234 | {
235 | "Ref": "appName"
236 | },
237 | {
238 | "Fn::Join": [
239 | "",
240 | [
241 | {
242 | "Ref": "appName"
243 | },
244 | "-",
245 | {
246 | "Ref": "env"
247 | }
248 | ]
249 | ]
250 | }
251 | ]
252 | }
253 | }
254 | },
255 | "CognitoUnauthPolicy": {
256 | "Type": "AWS::IAM::Policy",
257 | "Condition": "ShouldCreatePinpointApp",
258 | "Properties": {
259 | "PolicyName": {
260 | "Ref": "unauthPolicyName"
261 | },
262 | "Roles": [
263 | {
264 | "Ref": "unauthRoleName"
265 | }
266 | ],
267 | "PolicyDocument": {
268 | "Version": "2012-10-17",
269 | "Statement": [
270 | {
271 | "Effect": "Allow",
272 | "Action": [
273 | "mobiletargeting:PutEvents",
274 | "mobiletargeting:UpdateEndpoint"
275 | ],
276 | "Resource": [
277 | {
278 | "Fn::If": [
279 | "ShouldCreatePinpointApp",
280 | {
281 | "Fn::Join": [
282 | "",
283 | [
284 | "arn:aws:mobiletargeting:*:",
285 | {
286 | "Fn::Select": [
287 | "4",
288 | {
289 | "Fn::Split": [
290 | ":",
291 | {
292 | "Ref": "authRoleArn"
293 | }
294 | ]
295 | }
296 | ]
297 | },
298 | ":apps/",
299 | {
300 | "Fn::GetAtt": [
301 | "PinpointFunctionOutputs",
302 | "Id"
303 | ]
304 | },
305 | "*"
306 | ]
307 | ]
308 | },
309 | {
310 | "Fn::Join": [
311 | "",
312 | [
313 | "arn:aws:mobiletargeting:*:",
314 | {
315 | "Fn::Select": [
316 | "4",
317 | {
318 | "Fn::Split": [
319 | ":",
320 | {
321 | "Ref": "authRoleArn"
322 | }
323 | ]
324 | }
325 | ]
326 | },
327 | ":apps/",
328 | {
329 | "Ref": "appId"
330 | },
331 | "*"
332 | ]
333 | ]
334 | }
335 | ]
336 | }
337 | ]
338 | }
339 | ]
340 | }
341 | }
342 | },
343 | "CognitoAuthPolicy": {
344 | "Type": "AWS::IAM::Policy",
345 | "Condition": "ShouldCreatePinpointApp",
346 | "Properties": {
347 | "PolicyName": {
348 | "Ref": "authPolicyName"
349 | },
350 | "Roles": [
351 | {
352 | "Ref": "authRoleName"
353 | }
354 | ],
355 | "PolicyDocument": {
356 | "Version": "2012-10-17",
357 | "Statement": [
358 | {
359 | "Effect": "Allow",
360 | "Action": [
361 | "mobiletargeting:PutEvents",
362 | "mobiletargeting:UpdateEndpoint"
363 | ],
364 | "Resource": [
365 | {
366 | "Fn::If": [
367 | "ShouldCreatePinpointApp",
368 | {
369 | "Fn::Join": [
370 | "",
371 | [
372 | "arn:aws:mobiletargeting:*:",
373 | {
374 | "Fn::Select": [
375 | "4",
376 | {
377 | "Fn::Split": [
378 | ":",
379 | {
380 | "Ref": "authRoleArn"
381 | }
382 | ]
383 | }
384 | ]
385 | },
386 | ":apps/",
387 | {
388 | "Fn::GetAtt": [
389 | "PinpointFunctionOutputs",
390 | "Id"
391 | ]
392 | },
393 | "*"
394 | ]
395 | ]
396 | },
397 | {
398 | "Fn::Join": [
399 | "",
400 | [
401 | "arn:aws:mobiletargeting:*:",
402 | {
403 | "Fn::Select": [
404 | "4",
405 | {
406 | "Fn::Split": [
407 | ":",
408 | {
409 | "Ref": "authRoleArn"
410 | }
411 | ]
412 | }
413 | ]
414 | },
415 | ":apps/",
416 | {
417 | "Ref": "appId"
418 | },
419 | "*"
420 | ]
421 | ]
422 | }
423 | ]
424 | }
425 | ]
426 | }
427 | ]
428 | }
429 | }
430 | }
431 | },
432 | "Outputs": {
433 | "Region": {
434 | "Value": {
435 | "Fn::FindInMap": [
436 | "RegionMapping",
437 | {
438 | "Ref": "AWS::Region"
439 | },
440 | "pinpointRegion"
441 | ]
442 | }
443 | },
444 | "Id": {
445 | "Value": {
446 | "Fn::If": [
447 | "ShouldCreatePinpointApp",
448 | {
449 | "Fn::GetAtt": [
450 | "PinpointFunctionOutputs",
451 | "Id"
452 | ]
453 | },
454 | {
455 | "Ref": "appId"
456 | }
457 | ]
458 | }
459 | },
460 | "appName": {
461 | "Value": {
462 | "Fn::If": [
463 | "ShouldCreatePinpointApp",
464 | {
465 | "Fn::GetAtt": [
466 | "PinpointFunctionOutputs",
467 | "Name"
468 | ]
469 | },
470 | {
471 | "Ref": "appName"
472 | }
473 | ]
474 | }
475 | }
476 | },
477 | "Mappings": {
478 | "RegionMapping": {
479 | "us-east-1": {
480 | "pinpointRegion": "us-east-1"
481 | },
482 | "us-east-2": {
483 | "pinpointRegion": "us-east-1"
484 | },
485 | "sa-east-1": {
486 | "pinpointRegion": "us-east-1"
487 | },
488 | "ca-central-1": {
489 | "pinpointRegion": "us-east-1"
490 | },
491 | "us-west-1": {
492 | "pinpointRegion": "us-west-2"
493 | },
494 | "us-west-2": {
495 | "pinpointRegion": "us-west-2"
496 | },
497 | "cn-north-1": {
498 | "pinpointRegion": "us-west-2"
499 | },
500 | "cn-northwest-1": {
501 | "pinpointRegion": "us-west-2"
502 | },
503 | "ap-south-1": {
504 | "pinpointRegion": "us-west-2"
505 | },
506 | "ap-northeast-3": {
507 | "pinpointRegion": "us-west-2"
508 | },
509 | "ap-northeast-2": {
510 | "pinpointRegion": "us-west-2"
511 | },
512 | "ap-southeast-1": {
513 | "pinpointRegion": "us-west-2"
514 | },
515 | "ap-southeast-2": {
516 | "pinpointRegion": "us-west-2"
517 | },
518 | "ap-northeast-1": {
519 | "pinpointRegion": "us-west-2"
520 | },
521 | "eu-central-1": {
522 | "pinpointRegion": "eu-west-1"
523 | },
524 | "eu-west-1": {
525 | "pinpointRegion": "eu-west-1"
526 | },
527 | "eu-west-2": {
528 | "pinpointRegion": "eu-west-1"
529 | },
530 | "eu-west-3": {
531 | "pinpointRegion": "eu-west-1"
532 | }
533 | }
534 | }
535 | }
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/build/resolvers/Mutation.createNote.request:
--------------------------------------------------------------------------------
1 | ## [Start] Prepare DynamoDB PutItem Request. **
2 | $util.qr($context.args.input.put("createdAt", $util.time.nowISO8601()))
3 | $util.qr($context.args.input.put("updatedAt", $util.time.nowISO8601()))
4 | $util.qr($context.args.input.put("__typename", "Note"))
5 | {
6 | "version": "2017-02-28",
7 | "operation": "PutItem",
8 | "key": {
9 | "id": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.args.input.id, $util.autoId()))
10 | },
11 | "attributeValues": $util.dynamodb.toMapValuesJson($context.args.input),
12 | "condition": {
13 | "expression": "attribute_not_exists(#id)",
14 | "expressionNames": {
15 | "#id": "id"
16 | }
17 | }
18 | }
19 | ## [End] Prepare DynamoDB PutItem Request. **
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/build/resolvers/Mutation.createNote.response:
--------------------------------------------------------------------------------
1 | $util.toJson($context.result)
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/build/resolvers/Mutation.deleteNote.request:
--------------------------------------------------------------------------------
1 | #if( $authCondition )
2 | #set( $condition = $authCondition )
3 | $util.qr($condition.put("expression", "$condition.expression AND attribute_exists(#id)"))
4 | $util.qr($condition.expressionNames.put("#id", "id"))
5 | #else
6 | #set( $condition = {
7 | "expression": "attribute_exists(#id)",
8 | "expressionNames": {
9 | "#id": "id"
10 | }
11 | } )
12 | #end
13 | #if( $versionedCondition )
14 | $util.qr($condition.put("expression", "($condition.expression) AND $versionedCondition.expression"))
15 | $util.qr($condition.expressionNames.putAll($versionedCondition.expressionNames))
16 | #set( $expressionValues = $util.defaultIfNull($condition.expressionValues, {}) )
17 | $util.qr($expressionValues.putAll($versionedCondition.expressionValues))
18 | #set( $condition.expressionValues = $expressionValues )
19 | #end
20 | {
21 | "version": "2017-02-28",
22 | "operation": "DeleteItem",
23 | "key": {
24 | "id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id)
25 | },
26 | "condition": $util.toJson($condition)
27 | }
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/build/resolvers/Mutation.deleteNote.response:
--------------------------------------------------------------------------------
1 | $util.toJson($context.result)
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/build/resolvers/Mutation.updateNote.request:
--------------------------------------------------------------------------------
1 | #if( $authCondition && $authCondition.expression != "" )
2 | #set( $condition = $authCondition )
3 | $util.qr($condition.put("expression", "$condition.expression AND attribute_exists(#id)"))
4 | $util.qr($condition.expressionNames.put("#id", "id"))
5 | #else
6 | #set( $condition = {
7 | "expression": "attribute_exists(#id)",
8 | "expressionNames": {
9 | "#id": "id"
10 | },
11 | "expressionValues": {}
12 | } )
13 | #end
14 | ## Automatically set the updatedAt timestamp. **
15 | $util.qr($context.args.input.put("updatedAt", $util.time.nowISO8601()))
16 | $util.qr($context.args.input.put("__typename", "Note"))
17 | ## Update condition if type is @versioned **
18 | #if( $versionedCondition )
19 | $util.qr($condition.put("expression", "($condition.expression) AND $versionedCondition.expression"))
20 | $util.qr($condition.expressionNames.putAll($versionedCondition.expressionNames))
21 | $util.qr($condition.expressionValues.putAll($versionedCondition.expressionValues))
22 | #end
23 | #set( $expNames = {} )
24 | #set( $expValues = {} )
25 | #set( $expSet = {} )
26 | #set( $expAdd = {} )
27 | #set( $expRemove = [] )
28 | #foreach( $entry in $util.map.copyAndRemoveAllKeys($context.args.input, ["id"]).entrySet() )
29 | #if( $util.isNull($entry.value) )
30 | #set( $discard = $expRemove.add("#$entry.key") )
31 | $util.qr($expNames.put("#$entry.key", "$entry.key"))
32 | #else
33 | $util.qr($expSet.put("#$entry.key", ":$entry.key"))
34 | $util.qr($expNames.put("#$entry.key", "$entry.key"))
35 | $util.qr($expValues.put(":$entry.key", $util.dynamodb.toDynamoDB($entry.value)))
36 | #end
37 | #end
38 | #set( $expression = "" )
39 | #if( !$expSet.isEmpty() )
40 | #set( $expression = "SET" )
41 | #foreach( $entry in $expSet.entrySet() )
42 | #set( $expression = "$expression $entry.key = $entry.value" )
43 | #if( $foreach.hasNext() )
44 | #set( $expression = "$expression," )
45 | #end
46 | #end
47 | #end
48 | #if( !$expAdd.isEmpty() )
49 | #set( $expression = "$expression ADD" )
50 | #foreach( $entry in $expAdd.entrySet() )
51 | #set( $expression = "$expression $entry.key $entry.value" )
52 | #if( $foreach.hasNext() )
53 | #set( $expression = "$expression," )
54 | #end
55 | #end
56 | #end
57 | #if( !$expRemove.isEmpty() )
58 | #set( $expression = "$expression REMOVE" )
59 | #foreach( $entry in $expRemove )
60 | #set( $expression = "$expression $entry" )
61 | #if( $foreach.hasNext() )
62 | #set( $expression = "$expression," )
63 | #end
64 | #end
65 | #end
66 | #set( $update = {} )
67 | $util.qr($update.put("expression", "$expression"))
68 | #if( !$expNames.isEmpty() )
69 | $util.qr($update.put("expressionNames", $expNames))
70 | #end
71 | #if( !$expValues.isEmpty() )
72 | $util.qr($update.put("expressionValues", $expValues))
73 | #end
74 | {
75 | "version": "2017-02-28",
76 | "operation": "UpdateItem",
77 | "key": {
78 | "id": {
79 | "S": "$context.args.input.id"
80 | }
81 | },
82 | "update": $util.toJson($update),
83 | "condition": $util.toJson($condition)
84 | }
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/build/resolvers/Mutation.updateNote.response:
--------------------------------------------------------------------------------
1 | $util.toJson($context.result)
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/build/resolvers/Query.getNote.request:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2017-02-28",
3 | "operation": "GetItem",
4 | "key": {
5 | "id": $util.dynamodb.toDynamoDBJson($ctx.args.id)
6 | }
7 | }
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/build/resolvers/Query.getNote.response:
--------------------------------------------------------------------------------
1 | $util.toJson($context.result)
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/build/resolvers/Query.listNotes.request:
--------------------------------------------------------------------------------
1 | #set( $limit = $util.defaultIfNull($context.args.limit, 10) )
2 | {
3 | "version": "2017-02-28",
4 | "operation": "Scan",
5 | "filter": #if( $context.args.filter )
6 | $util.transform.toDynamoDBFilterExpression($ctx.args.filter)
7 | #else
8 | null
9 | #end,
10 | "limit": $limit,
11 | "nextToken": #if( $context.args.nextToken )
12 | "$context.args.nextToken"
13 | #else
14 | null
15 | #end
16 | }
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/build/resolvers/Query.listNotes.response:
--------------------------------------------------------------------------------
1 | $util.toJson($ctx.result)
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/build/schema.graphql:
--------------------------------------------------------------------------------
1 | type Note {
2 | id: ID!
3 | name: String!
4 | description: String
5 | status: String!
6 | }
7 |
8 | enum ModelSortDirection {
9 | ASC
10 | DESC
11 | }
12 |
13 | type ModelNoteConnection {
14 | items: [Note]
15 | nextToken: String
16 | }
17 |
18 | input ModelStringFilterInput {
19 | ne: String
20 | eq: String
21 | le: String
22 | lt: String
23 | ge: String
24 | gt: String
25 | contains: String
26 | notContains: String
27 | between: [String]
28 | beginsWith: String
29 | }
30 |
31 | input ModelIDFilterInput {
32 | ne: ID
33 | eq: ID
34 | le: ID
35 | lt: ID
36 | ge: ID
37 | gt: ID
38 | contains: ID
39 | notContains: ID
40 | between: [ID]
41 | beginsWith: ID
42 | }
43 |
44 | input ModelIntFilterInput {
45 | ne: Int
46 | eq: Int
47 | le: Int
48 | lt: Int
49 | ge: Int
50 | gt: Int
51 | contains: Int
52 | notContains: Int
53 | between: [Int]
54 | }
55 |
56 | input ModelFloatFilterInput {
57 | ne: Float
58 | eq: Float
59 | le: Float
60 | lt: Float
61 | ge: Float
62 | gt: Float
63 | contains: Float
64 | notContains: Float
65 | between: [Float]
66 | }
67 |
68 | input ModelBooleanFilterInput {
69 | ne: Boolean
70 | eq: Boolean
71 | }
72 |
73 | input ModelNoteFilterInput {
74 | id: ModelIDFilterInput
75 | name: ModelStringFilterInput
76 | description: ModelStringFilterInput
77 | status: ModelStringFilterInput
78 | and: [ModelNoteFilterInput]
79 | or: [ModelNoteFilterInput]
80 | not: ModelNoteFilterInput
81 | }
82 |
83 | type Query {
84 | getNote(id: ID!): Note
85 | listNotes(filter: ModelNoteFilterInput, limit: Int, nextToken: String): ModelNoteConnection
86 | }
87 |
88 | input CreateNoteInput {
89 | id: ID
90 | name: String!
91 | description: String
92 | status: String!
93 | }
94 |
95 | input UpdateNoteInput {
96 | id: ID!
97 | name: String
98 | description: String
99 | status: String
100 | }
101 |
102 | input DeleteNoteInput {
103 | id: ID
104 | }
105 |
106 | type Mutation {
107 | createNote(input: CreateNoteInput!): Note
108 | updateNote(input: UpdateNoteInput!): Note
109 | deleteNote(input: DeleteNoteInput!): Note
110 | }
111 |
112 | type Subscription {
113 | onCreateNote: Note @aws_subscribe(mutations: ["createNote"])
114 | onUpdateNote: Note @aws_subscribe(mutations: ["updateNote"])
115 | onDeleteNote: Note @aws_subscribe(mutations: ["deleteNote"])
116 | }
117 |
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/cloudformation-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "AWSTemplateFormatVersion": "2010-09-09",
3 | "Metadata": {},
4 | "Parameters": {
5 | "AppSyncApiName": {
6 | "Type": "String",
7 | "Description": "The name of the AppSync API",
8 | "Default": "AppSyncSimpleTransform"
9 | },
10 | "DynamoDBModelTableReadIOPS": {
11 | "Type": "Number",
12 | "Description": "The number of read IOPS the table should support.",
13 | "Default": 5
14 | },
15 | "DynamoDBModelTableWriteIOPS": {
16 | "Type": "Number",
17 | "Description": "The number of write IOPS the table should support.",
18 | "Default": 5
19 | },
20 | "AuthCognitoUserPoolId": {
21 | "Type": "String",
22 | "Description": "The id of an existing User Pool to connect. If this is changed, a user pool will not be created for you.",
23 | "Default": "NONE"
24 | },
25 | "AuthCognitoUserPoolName": {
26 | "Type": "String",
27 | "Description": "The name of the user pool.",
28 | "Default": "AppSyncUserPool"
29 | },
30 | "AuthCognitoUserPoolMobileClientName": {
31 | "Type": "String",
32 | "Description": "The name of the native user pool client.",
33 | "Default": "CognitoNativeClient"
34 | },
35 | "AuthCognitoUserPoolJSClientName": {
36 | "Type": "String",
37 | "Description": "The name of the web user pool client.",
38 | "Default": "CognitoJSClient"
39 | },
40 | "AuthCognitoUserPoolRefreshTokenValidity": {
41 | "Type": "Number",
42 | "Description": "The time limit, in days, after which the refresh token is no longer valid.",
43 | "Default": 30
44 | },
45 | "env": {
46 | "Type": "String",
47 | "Description": "The environment name. e.g. Dev, Test, or Production",
48 | "Default": "NONE"
49 | },
50 | "ResolverBucket": {
51 | "Type": "String",
52 | "Description": "The name of the bucket containing the resolver templates"
53 | },
54 | "ResolverRootKey": {
55 | "Type": "String",
56 | "Description": "The s3 key of the folder containing the resolver templates in format {Type}.{Field}.[response|request].{Timestamp}"
57 | },
58 | "DeploymentTimestamp": {
59 | "Type": "String",
60 | "Description": "The timestamp used to identify thie most recent version of the resolver templates in s3."
61 | },
62 | "schemaGraphql": {
63 | "Type": "String",
64 | "Description": "The S3 location for the Schema: schema.graphql"
65 | }
66 | },
67 | "Resources": {
68 | "GraphQLSchema": {
69 | "Type": "AWS::AppSync::GraphQLSchema",
70 | "Properties": {
71 | "ApiId": {
72 | "Fn::GetAtt": [
73 | "GraphQLAPI",
74 | "ApiId"
75 | ]
76 | },
77 | "DefinitionS3Location": {
78 | "Ref": "schemaGraphql"
79 | }
80 | }
81 | },
82 | "GraphQLAPI": {
83 | "Type": "AWS::AppSync::GraphQLApi",
84 | "Properties": {
85 | "Name": {
86 | "Ref": "AppSyncApiName"
87 | },
88 | "AuthenticationType": "AMAZON_COGNITO_USER_POOLS",
89 | "UserPoolConfig": {
90 | "UserPoolId": {
91 | "Fn::If": [
92 | "AuthShouldCreateUserPool",
93 | {
94 | "Ref": "AuthCognitoUserPool"
95 | },
96 | {
97 | "Ref": "AuthCognitoUserPoolId"
98 | }
99 | ]
100 | },
101 | "AwsRegion": {
102 | "Ref": "AWS::Region"
103 | },
104 | "DefaultAction": "ALLOW"
105 | }
106 | }
107 | },
108 | "GraphQLAPIKey": {
109 | "Type": "AWS::AppSync::ApiKey",
110 | "Properties": {
111 | "ApiId": {
112 | "Fn::GetAtt": [
113 | "GraphQLAPI",
114 | "ApiId"
115 | ]
116 | }
117 | }
118 | },
119 | "NoteTable": {
120 | "Type": "AWS::DynamoDB::Table",
121 | "Properties": {
122 | "TableName": {
123 | "Fn::Join": [
124 | "-",
125 | [
126 | "Note",
127 | {
128 | "Fn::GetAtt": [
129 | "GraphQLAPI",
130 | "ApiId"
131 | ]
132 | }
133 | ]
134 | ]
135 | },
136 | "KeySchema": [
137 | {
138 | "AttributeName": "id",
139 | "KeyType": "HASH"
140 | }
141 | ],
142 | "AttributeDefinitions": [
143 | {
144 | "AttributeName": "id",
145 | "AttributeType": "S"
146 | }
147 | ],
148 | "ProvisionedThroughput": {
149 | "ReadCapacityUnits": {
150 | "Ref": "DynamoDBModelTableReadIOPS"
151 | },
152 | "WriteCapacityUnits": {
153 | "Ref": "DynamoDBModelTableWriteIOPS"
154 | }
155 | },
156 | "StreamSpecification": {
157 | "StreamViewType": "NEW_AND_OLD_IMAGES"
158 | }
159 | }
160 | },
161 | "NoteIAMRole": {
162 | "Type": "AWS::IAM::Role",
163 | "Properties": {
164 | "RoleName": {
165 | "Fn::Join": [
166 | "-",
167 | [
168 | "NoteTable",
169 | "role",
170 | {
171 | "Fn::GetAtt": [
172 | "GraphQLAPI",
173 | "ApiId"
174 | ]
175 | }
176 | ]
177 | ]
178 | },
179 | "AssumeRolePolicyDocument": {
180 | "Version": "2012-10-17",
181 | "Statement": [
182 | {
183 | "Effect": "Allow",
184 | "Principal": {
185 | "Service": "appsync.amazonaws.com"
186 | },
187 | "Action": "sts:AssumeRole"
188 | }
189 | ]
190 | },
191 | "Policies": [
192 | {
193 | "PolicyName": "DynamoDBAccess",
194 | "PolicyDocument": {
195 | "Version": "2012-10-17",
196 | "Statement": [
197 | {
198 | "Effect": "Allow",
199 | "Action": [
200 | "dynamodb:BatchGetItem",
201 | "dynamodb:BatchWriteItem",
202 | "dynamodb:PutItem",
203 | "dynamodb:DeleteItem",
204 | "dynamodb:GetItem",
205 | "dynamodb:Scan",
206 | "dynamodb:Query",
207 | "dynamodb:UpdateItem"
208 | ],
209 | "Resource": [
210 | {
211 | "Fn::GetAtt": [
212 | "NoteTable",
213 | "Arn"
214 | ]
215 | },
216 | {
217 | "Fn::Join": [
218 | "/",
219 | [
220 | {
221 | "Fn::GetAtt": [
222 | "NoteTable",
223 | "Arn"
224 | ]
225 | },
226 | "*"
227 | ]
228 | ]
229 | }
230 | ]
231 | }
232 | ]
233 | }
234 | }
235 | ]
236 | }
237 | },
238 | "NoteDataSource": {
239 | "Type": "AWS::AppSync::DataSource",
240 | "Properties": {
241 | "ApiId": {
242 | "Fn::GetAtt": [
243 | "GraphQLAPI",
244 | "ApiId"
245 | ]
246 | },
247 | "Name": "NoteTable",
248 | "Type": "AMAZON_DYNAMODB",
249 | "ServiceRoleArn": {
250 | "Fn::GetAtt": [
251 | "NoteIAMRole",
252 | "Arn"
253 | ]
254 | },
255 | "DynamoDBConfig": {
256 | "AwsRegion": {
257 | "Fn::Select": [
258 | 3,
259 | {
260 | "Fn::Split": [
261 | ":",
262 | {
263 | "Fn::GetAtt": [
264 | "NoteTable",
265 | "Arn"
266 | ]
267 | }
268 | ]
269 | }
270 | ]
271 | },
272 | "TableName": {
273 | "Ref": "NoteTable"
274 | }
275 | }
276 | },
277 | "DependsOn": [
278 | "NoteTable",
279 | "NoteIAMRole"
280 | ]
281 | },
282 | "GetNoteResolver": {
283 | "Type": "AWS::AppSync::Resolver",
284 | "Properties": {
285 | "ApiId": {
286 | "Fn::GetAtt": [
287 | "GraphQLAPI",
288 | "ApiId"
289 | ]
290 | },
291 | "DataSourceName": {
292 | "Fn::GetAtt": [
293 | "NoteDataSource",
294 | "Name"
295 | ]
296 | },
297 | "FieldName": "getNote",
298 | "TypeName": "Query",
299 | "RequestMappingTemplateS3Location": {
300 | "Fn::Join": [
301 | "",
302 | [
303 | "s3://",
304 | {
305 | "Fn::Join": [
306 | "/",
307 | [
308 | {
309 | "Ref": "ResolverBucket"
310 | },
311 | {
312 | "Ref": "ResolverRootKey"
313 | },
314 | {
315 | "Fn::Join": [
316 | ".",
317 | [
318 | "Query",
319 | "getNote",
320 | "request",
321 | {
322 | "Ref": "DeploymentTimestamp"
323 | }
324 | ]
325 | ]
326 | }
327 | ]
328 | ]
329 | }
330 | ]
331 | ]
332 | },
333 | "ResponseMappingTemplateS3Location": {
334 | "Fn::Join": [
335 | "",
336 | [
337 | "s3://",
338 | {
339 | "Fn::Join": [
340 | "/",
341 | [
342 | {
343 | "Ref": "ResolverBucket"
344 | },
345 | {
346 | "Ref": "ResolverRootKey"
347 | },
348 | {
349 | "Fn::Join": [
350 | ".",
351 | [
352 | "Query",
353 | "getNote",
354 | "response",
355 | {
356 | "Ref": "DeploymentTimestamp"
357 | }
358 | ]
359 | ]
360 | }
361 | ]
362 | ]
363 | }
364 | ]
365 | ]
366 | }
367 | },
368 | "DependsOn": "GraphQLSchema"
369 | },
370 | "ListNoteResolver": {
371 | "Type": "AWS::AppSync::Resolver",
372 | "Properties": {
373 | "ApiId": {
374 | "Fn::GetAtt": [
375 | "GraphQLAPI",
376 | "ApiId"
377 | ]
378 | },
379 | "DataSourceName": {
380 | "Fn::GetAtt": [
381 | "NoteDataSource",
382 | "Name"
383 | ]
384 | },
385 | "FieldName": "listNotes",
386 | "TypeName": "Query",
387 | "RequestMappingTemplateS3Location": {
388 | "Fn::Join": [
389 | "",
390 | [
391 | "s3://",
392 | {
393 | "Fn::Join": [
394 | "/",
395 | [
396 | {
397 | "Ref": "ResolverBucket"
398 | },
399 | {
400 | "Ref": "ResolverRootKey"
401 | },
402 | {
403 | "Fn::Join": [
404 | ".",
405 | [
406 | "Query",
407 | "listNotes",
408 | "request",
409 | {
410 | "Ref": "DeploymentTimestamp"
411 | }
412 | ]
413 | ]
414 | }
415 | ]
416 | ]
417 | }
418 | ]
419 | ]
420 | },
421 | "ResponseMappingTemplateS3Location": {
422 | "Fn::Join": [
423 | "",
424 | [
425 | "s3://",
426 | {
427 | "Fn::Join": [
428 | "/",
429 | [
430 | {
431 | "Ref": "ResolverBucket"
432 | },
433 | {
434 | "Ref": "ResolverRootKey"
435 | },
436 | {
437 | "Fn::Join": [
438 | ".",
439 | [
440 | "Query",
441 | "listNotes",
442 | "response",
443 | {
444 | "Ref": "DeploymentTimestamp"
445 | }
446 | ]
447 | ]
448 | }
449 | ]
450 | ]
451 | }
452 | ]
453 | ]
454 | }
455 | },
456 | "DependsOn": "GraphQLSchema"
457 | },
458 | "CreateNoteResolver": {
459 | "Type": "AWS::AppSync::Resolver",
460 | "Properties": {
461 | "ApiId": {
462 | "Fn::GetAtt": [
463 | "GraphQLAPI",
464 | "ApiId"
465 | ]
466 | },
467 | "DataSourceName": {
468 | "Fn::GetAtt": [
469 | "NoteDataSource",
470 | "Name"
471 | ]
472 | },
473 | "FieldName": "createNote",
474 | "TypeName": "Mutation",
475 | "RequestMappingTemplateS3Location": {
476 | "Fn::Join": [
477 | "",
478 | [
479 | "s3://",
480 | {
481 | "Fn::Join": [
482 | "/",
483 | [
484 | {
485 | "Ref": "ResolverBucket"
486 | },
487 | {
488 | "Ref": "ResolverRootKey"
489 | },
490 | {
491 | "Fn::Join": [
492 | ".",
493 | [
494 | "Mutation",
495 | "createNote",
496 | "request",
497 | {
498 | "Ref": "DeploymentTimestamp"
499 | }
500 | ]
501 | ]
502 | }
503 | ]
504 | ]
505 | }
506 | ]
507 | ]
508 | },
509 | "ResponseMappingTemplateS3Location": {
510 | "Fn::Join": [
511 | "",
512 | [
513 | "s3://",
514 | {
515 | "Fn::Join": [
516 | "/",
517 | [
518 | {
519 | "Ref": "ResolverBucket"
520 | },
521 | {
522 | "Ref": "ResolverRootKey"
523 | },
524 | {
525 | "Fn::Join": [
526 | ".",
527 | [
528 | "Mutation",
529 | "createNote",
530 | "response",
531 | {
532 | "Ref": "DeploymentTimestamp"
533 | }
534 | ]
535 | ]
536 | }
537 | ]
538 | ]
539 | }
540 | ]
541 | ]
542 | }
543 | },
544 | "DependsOn": "GraphQLSchema"
545 | },
546 | "UpdateNoteResolver": {
547 | "Type": "AWS::AppSync::Resolver",
548 | "Properties": {
549 | "ApiId": {
550 | "Fn::GetAtt": [
551 | "GraphQLAPI",
552 | "ApiId"
553 | ]
554 | },
555 | "DataSourceName": {
556 | "Fn::GetAtt": [
557 | "NoteDataSource",
558 | "Name"
559 | ]
560 | },
561 | "FieldName": "updateNote",
562 | "TypeName": "Mutation",
563 | "RequestMappingTemplateS3Location": {
564 | "Fn::Join": [
565 | "",
566 | [
567 | "s3://",
568 | {
569 | "Fn::Join": [
570 | "/",
571 | [
572 | {
573 | "Ref": "ResolverBucket"
574 | },
575 | {
576 | "Ref": "ResolverRootKey"
577 | },
578 | {
579 | "Fn::Join": [
580 | ".",
581 | [
582 | "Mutation",
583 | "updateNote",
584 | "request",
585 | {
586 | "Ref": "DeploymentTimestamp"
587 | }
588 | ]
589 | ]
590 | }
591 | ]
592 | ]
593 | }
594 | ]
595 | ]
596 | },
597 | "ResponseMappingTemplateS3Location": {
598 | "Fn::Join": [
599 | "",
600 | [
601 | "s3://",
602 | {
603 | "Fn::Join": [
604 | "/",
605 | [
606 | {
607 | "Ref": "ResolverBucket"
608 | },
609 | {
610 | "Ref": "ResolverRootKey"
611 | },
612 | {
613 | "Fn::Join": [
614 | ".",
615 | [
616 | "Mutation",
617 | "updateNote",
618 | "response",
619 | {
620 | "Ref": "DeploymentTimestamp"
621 | }
622 | ]
623 | ]
624 | }
625 | ]
626 | ]
627 | }
628 | ]
629 | ]
630 | }
631 | },
632 | "DependsOn": "GraphQLSchema"
633 | },
634 | "DeleteNoteResolver": {
635 | "Type": "AWS::AppSync::Resolver",
636 | "Properties": {
637 | "ApiId": {
638 | "Fn::GetAtt": [
639 | "GraphQLAPI",
640 | "ApiId"
641 | ]
642 | },
643 | "DataSourceName": {
644 | "Fn::GetAtt": [
645 | "NoteDataSource",
646 | "Name"
647 | ]
648 | },
649 | "FieldName": "deleteNote",
650 | "TypeName": "Mutation",
651 | "RequestMappingTemplateS3Location": {
652 | "Fn::Join": [
653 | "",
654 | [
655 | "s3://",
656 | {
657 | "Fn::Join": [
658 | "/",
659 | [
660 | {
661 | "Ref": "ResolverBucket"
662 | },
663 | {
664 | "Ref": "ResolverRootKey"
665 | },
666 | {
667 | "Fn::Join": [
668 | ".",
669 | [
670 | "Mutation",
671 | "deleteNote",
672 | "request",
673 | {
674 | "Ref": "DeploymentTimestamp"
675 | }
676 | ]
677 | ]
678 | }
679 | ]
680 | ]
681 | }
682 | ]
683 | ]
684 | },
685 | "ResponseMappingTemplateS3Location": {
686 | "Fn::Join": [
687 | "",
688 | [
689 | "s3://",
690 | {
691 | "Fn::Join": [
692 | "/",
693 | [
694 | {
695 | "Ref": "ResolverBucket"
696 | },
697 | {
698 | "Ref": "ResolverRootKey"
699 | },
700 | {
701 | "Fn::Join": [
702 | ".",
703 | [
704 | "Mutation",
705 | "deleteNote",
706 | "response",
707 | {
708 | "Ref": "DeploymentTimestamp"
709 | }
710 | ]
711 | ]
712 | }
713 | ]
714 | ]
715 | }
716 | ]
717 | ]
718 | }
719 | },
720 | "DependsOn": "GraphQLSchema"
721 | },
722 | "AuthCognitoUserPool": {
723 | "Type": "AWS::Cognito::UserPool",
724 | "Properties": {
725 | "UserPoolName": {
726 | "Ref": "AuthCognitoUserPoolName"
727 | },
728 | "Policies": {
729 | "PasswordPolicy": {
730 | "MinimumLength": 8,
731 | "RequireLowercase": true,
732 | "RequireNumbers": true,
733 | "RequireSymbols": true,
734 | "RequireUppercase": true
735 | }
736 | },
737 | "Schema": [
738 | {
739 | "Name": "email",
740 | "Required": true,
741 | "Mutable": true
742 | }
743 | ],
744 | "AutoVerifiedAttributes": [
745 | "email"
746 | ]
747 | },
748 | "Condition": "AuthShouldCreateUserPool"
749 | },
750 | "AuthCognitoUserPoolNativeClient": {
751 | "Type": "AWS::Cognito::UserPoolClient",
752 | "Properties": {
753 | "ClientName": {
754 | "Ref": "AuthCognitoUserPoolMobileClientName"
755 | },
756 | "UserPoolId": {
757 | "Fn::If": [
758 | "AuthShouldCreateUserPool",
759 | {
760 | "Ref": "AuthCognitoUserPool"
761 | },
762 | {
763 | "Ref": "AuthCognitoUserPoolId"
764 | }
765 | ]
766 | },
767 | "GenerateSecret": true,
768 | "RefreshTokenValidity": {
769 | "Ref": "AuthCognitoUserPoolRefreshTokenValidity"
770 | },
771 | "ReadAttributes": [],
772 | "WriteAttributes": []
773 | },
774 | "Condition": "AuthShouldCreateUserPool"
775 | },
776 | "AuthCognitoUserPoolJSClient": {
777 | "Type": "AWS::Cognito::UserPoolClient",
778 | "Properties": {
779 | "ClientName": {
780 | "Ref": "AuthCognitoUserPoolJSClientName"
781 | },
782 | "UserPoolId": {
783 | "Fn::If": [
784 | "AuthShouldCreateUserPool",
785 | {
786 | "Ref": "AuthCognitoUserPool"
787 | },
788 | {
789 | "Ref": "AuthCognitoUserPoolId"
790 | }
791 | ]
792 | },
793 | "GenerateSecret": false,
794 | "RefreshTokenValidity": {
795 | "Ref": "AuthCognitoUserPoolRefreshTokenValidity"
796 | },
797 | "ReadAttributes": [],
798 | "WriteAttributes": []
799 | },
800 | "Condition": "AuthShouldCreateUserPool"
801 | }
802 | },
803 | "Outputs": {
804 | "GraphQLAPIIdOutput": {
805 | "Description": "Your GraphQL API ID.",
806 | "Value": {
807 | "Fn::GetAtt": [
808 | "GraphQLAPI",
809 | "ApiId"
810 | ]
811 | },
812 | "Export": {
813 | "Name": {
814 | "Fn::Join": [
815 | ":",
816 | [
817 | {
818 | "Ref": "AWS::StackName"
819 | },
820 | "GraphQLApiId"
821 | ]
822 | ]
823 | }
824 | }
825 | },
826 | "GraphQLAPIEndpointOutput": {
827 | "Description": "Your GraphQL API endpoint.",
828 | "Value": {
829 | "Fn::GetAtt": [
830 | "GraphQLAPI",
831 | "GraphQLUrl"
832 | ]
833 | },
834 | "Export": {
835 | "Name": {
836 | "Fn::Join": [
837 | ":",
838 | [
839 | {
840 | "Ref": "AWS::StackName"
841 | },
842 | "GraphQLApiEndpoint"
843 | ]
844 | ]
845 | }
846 | }
847 | },
848 | "GraphQLAPIKeyOutput": {
849 | "Description": "Your GraphQL API key. Provide via 'x-api-key' header.",
850 | "Value": {
851 | "Fn::GetAtt": [
852 | "GraphQLAPIKey",
853 | "ApiKey"
854 | ]
855 | },
856 | "Export": {
857 | "Name": {
858 | "Fn::Join": [
859 | ":",
860 | [
861 | {
862 | "Ref": "AWS::StackName"
863 | },
864 | "GraphQLApiKey"
865 | ]
866 | ]
867 | }
868 | }
869 | },
870 | "AuthCognitoUserPoolNativeClientId": {
871 | "Description": "Amazon Cognito UserPools native client ID",
872 | "Value": {
873 | "Fn::If": [
874 | "AuthShouldCreateUserPool",
875 | {
876 | "Ref": "AuthCognitoUserPoolNativeClient"
877 | },
878 | {
879 | "Fn::Join": [
880 | " ",
881 | [
882 | "See UserPool:",
883 | {
884 | "Ref": "AuthCognitoUserPoolId"
885 | }
886 | ]
887 | ]
888 | }
889 | ]
890 | },
891 | "Export": {
892 | "Name": {
893 | "Fn::Join": [
894 | ":",
895 | [
896 | {
897 | "Ref": "AWS::StackName"
898 | },
899 | "CognitoNativeClient"
900 | ]
901 | ]
902 | }
903 | }
904 | },
905 | "AuthCognitoUserPoolJSClientId": {
906 | "Description": "Amazon Cognito UserPools JS client ID",
907 | "Value": {
908 | "Fn::If": [
909 | "AuthShouldCreateUserPool",
910 | {
911 | "Ref": "AuthCognitoUserPoolJSClient"
912 | },
913 | {
914 | "Fn::Join": [
915 | " ",
916 | [
917 | "See UserPool:",
918 | {
919 | "Ref": "AuthCognitoUserPoolId"
920 | }
921 | ]
922 | ]
923 | }
924 | ]
925 | },
926 | "Export": {
927 | "Name": {
928 | "Fn::Join": [
929 | ":",
930 | [
931 | {
932 | "Ref": "AWS::StackName"
933 | },
934 | "CognitoJSClient"
935 | ]
936 | ]
937 | }
938 | }
939 | },
940 | "AuthCognitoUserPoolIdOutput": {
941 | "Description": "Amazon Cognito UserPool id",
942 | "Value": {
943 | "Fn::If": [
944 | "AuthShouldCreateUserPool",
945 | {
946 | "Ref": "AuthCognitoUserPool"
947 | },
948 | {
949 | "Ref": "AuthCognitoUserPoolId"
950 | }
951 | ]
952 | },
953 | "Export": {
954 | "Name": {
955 | "Fn::Join": [
956 | ":",
957 | [
958 | {
959 | "Ref": "AWS::StackName"
960 | },
961 | "CognitoUserPoolId"
962 | ]
963 | ]
964 | }
965 | }
966 | }
967 | },
968 | "Conditions": {
969 | "HasEnvironmentParameter": {
970 | "Fn::Not": [
971 | {
972 | "Fn::Equals": [
973 | {
974 | "Ref": "env"
975 | },
976 | "NONE"
977 | ]
978 | }
979 | ]
980 | },
981 | "AuthShouldCreateUserPool": {
982 | "Fn::Equals": [
983 | {
984 | "Ref": "AuthCognitoUserPoolId"
985 | },
986 | "NONE"
987 | ]
988 | }
989 | }
990 | }
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "AppSyncApiName": "reactnotes",
3 | "AuthCognitoUserPoolId": {
4 | "Fn::GetAtt": [
5 | "authreactnotescognitoauth",
6 | "Outputs.UserPoolId"
7 | ]
8 | },
9 | "schemaGraphql": "s3://react-notes-20181228114606-deployment/amplify-appsync-files/schema.graphql.1546457354441",
10 | "ResolverBucket": "react-notes-20181228114606-deployment",
11 | "ResolverRootKey": "amplify-appsync-files",
12 | "DeploymentTimestamp": "1546457354441"
13 | }
--------------------------------------------------------------------------------
/amplify/backend/api/reactnotes/schema.graphql:
--------------------------------------------------------------------------------
1 | type Note @model {
2 | id: ID!
3 | name: String!
4 | description: String
5 | status: String!
6 | }
--------------------------------------------------------------------------------
/amplify/backend/auth/reactnotescognitoauth/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "identityPoolName": "react_notes_id_pool",
3 | "allowUnauthenticatedIdentities": true,
4 | "thirdPartyAuth": false,
5 | "lambdaLogPolicy": "reactnotescognitoauth_lambda_log_policy",
6 | "openIdLambdaRoleName": "reactnotescognitoauth_openid_lambda_role",
7 | "openIdRolePolicy": "reactnotescognitoauth_openid_pass_role_policy",
8 | "openIdLambdaIAMPolicy": "reactnotescognitoauth_openid_lambda_iam_policy",
9 | "openIdLogPolicy": "reactnotescognitoauth_openid_lambda_log_policy",
10 | "userPoolName": "react_notes_user_pool",
11 | "autoVerifiedAttributes": [
12 | "email"
13 | ],
14 | "mfaConfiguration": "OFF",
15 | "mfaTypes": [
16 | "SMS Text Message"
17 | ],
18 | "roleName": "reactnotescognitoauth_sns-role",
19 | "roleExternalId": "reactnotescognitoauth_role_external_id",
20 | "policyName": "reactnotescognitoauth-sns-policy",
21 | "smsAuthenticationMessage": "Your authentication code is {####}",
22 | "smsVerificationMessage": "Your verification code is {####}",
23 | "emailVerificationSubject": "Your verification code",
24 | "emailVerificationMessage": "Your verification code is {####}",
25 | "defaultPasswordPolicy": false,
26 | "passwordPolicyMinLength": 8,
27 | "passwordPolicyCharacters": [
28 | "Requires Lowercase",
29 | "Requires Uppercase",
30 | "Requires Numbers",
31 | "Requires Symbols"
32 | ],
33 | "requiredAttributes": [
34 | "email"
35 | ],
36 | "userpoolClientName": "reactnotescognitoauth_app_client",
37 | "userpoolClientGenerateSecret": true,
38 | "userpoolClientRefreshTokenValidity": 30,
39 | "userpoolClientReadAttributes": [
40 | "email"
41 | ],
42 | "mfaLambdaRole": "reactnotescognitoauth_totp_lambda_role",
43 | "mfaLambdaLogPolicy": "reactnotescognitoauth_totp_lambda_log_policy",
44 | "mfaPassRolePolicy": "reactnotescognitoauth_totp_pass_role_policy",
45 | "mfaLambdaIAMPolicy": "reactnotescognitoauth_totp_lambda_iam_policy",
46 | "userpoolClientLambdaRole": "reactnotescognitoauth_userpoolclient_lambda_role",
47 | "userpoolClientLogPolicy": "reactnotescognitoauth_userpoolclient_lambda_log_policy",
48 | "userpoolClientLambdaPolicy": "reactnotescognitoauth_userpoolclient_lambda_iam_policy",
49 | "userpoolClientSetAttributes": false,
50 | "useDefault": "manual",
51 | "authSelections": "identityPoolAndUserPool",
52 | "resourceName": "reactnotescognitoauth",
53 | "authRoleName": {
54 | "Ref": "AuthRoleName"
55 | },
56 | "unauthRoleName": {
57 | "Ref": "UnauthRoleName"
58 | },
59 | "authRoleArn": {
60 | "Fn::GetAtt": [
61 | "AuthRole",
62 | "Arn"
63 | ]
64 | },
65 | "unauthRoleArn": {
66 | "Fn::GetAtt": [
67 | "UnauthRole",
68 | "Arn"
69 | ]
70 | }
71 | }
--------------------------------------------------------------------------------
/amplify/backend/auth/reactnotescognitoauth/reactnotescognitoauth-cloudformation-template.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 |
3 | Parameters:
4 | env:
5 | Type: String
6 | authRoleName:
7 | Type: String
8 | unauthRoleName:
9 | Type: String
10 | authRoleArn:
11 | Type: String
12 | unauthRoleArn:
13 | Type: String
14 |
15 |
16 | identityPoolName:
17 | Type: String
18 |
19 | allowUnauthenticatedIdentities:
20 | Type: String
21 |
22 | thirdPartyAuth:
23 | Type: String
24 |
25 | lambdaLogPolicy:
26 | Type: String
27 |
28 | openIdLambdaRoleName:
29 | Type: String
30 |
31 | openIdRolePolicy:
32 | Type: String
33 |
34 | openIdLambdaIAMPolicy:
35 | Type: String
36 |
37 | openIdLogPolicy:
38 | Type: String
39 |
40 | userPoolName:
41 | Type: String
42 |
43 | autoVerifiedAttributes:
44 | Type: CommaDelimitedList
45 |
46 | mfaConfiguration:
47 | Type: String
48 |
49 | mfaTypes:
50 | Type: CommaDelimitedList
51 |
52 | roleName:
53 | Type: String
54 |
55 | roleExternalId:
56 | Type: String
57 |
58 | policyName:
59 | Type: String
60 |
61 | smsAuthenticationMessage:
62 | Type: String
63 |
64 | smsVerificationMessage:
65 | Type: String
66 |
67 | emailVerificationSubject:
68 | Type: String
69 |
70 | emailVerificationMessage:
71 | Type: String
72 |
73 | defaultPasswordPolicy:
74 | Type: String
75 |
76 | passwordPolicyMinLength:
77 | Type: Number
78 |
79 | passwordPolicyCharacters:
80 | Type: CommaDelimitedList
81 |
82 | requiredAttributes:
83 | Type: CommaDelimitedList
84 |
85 | userpoolClientName:
86 | Type: String
87 |
88 | userpoolClientGenerateSecret:
89 | Type: String
90 |
91 | userpoolClientRefreshTokenValidity:
92 | Type: Number
93 |
94 | userpoolClientReadAttributes:
95 | Type: CommaDelimitedList
96 |
97 | mfaLambdaRole:
98 | Type: String
99 |
100 | mfaLambdaLogPolicy:
101 | Type: String
102 |
103 | mfaPassRolePolicy:
104 | Type: String
105 |
106 | mfaLambdaIAMPolicy:
107 | Type: String
108 |
109 | userpoolClientLambdaRole:
110 | Type: String
111 |
112 | userpoolClientLogPolicy:
113 | Type: String
114 |
115 | userpoolClientLambdaPolicy:
116 | Type: String
117 |
118 | userpoolClientSetAttributes:
119 | Type: String
120 |
121 | useDefault:
122 | Type: String
123 |
124 | authSelections:
125 | Type: String
126 |
127 | resourceName:
128 | Type: String
129 |
130 | Conditions:
131 | ShouldNotCreateEnvResources: !Equals [ !Ref env, NONE ]
132 |
133 | Resources:
134 |
135 | # BEGIN SNS ROLE RESOURCE
136 | SNSRole:
137 | # Created to allow the UserPool SMS Config to publish via the Simple Notification Service during MFA Process
138 | Type: AWS::IAM::Role
139 | Properties:
140 | RoleName: !If [ShouldNotCreateEnvResources, !Ref roleName, !Join ['',[!Ref roleName, '-', !Ref env]]]
141 | AssumeRolePolicyDocument:
142 | Version: "2012-10-17"
143 | Statement:
144 | - Sid: ""
145 | Effect: "Allow"
146 | Principal:
147 | Service: "cognito-idp.amazonaws.com"
148 | Action:
149 | - "sts:AssumeRole"
150 | Condition:
151 | StringEquals:
152 | sts:ExternalId: !Ref roleExternalId
153 | Policies:
154 | -
155 | PolicyName: !Ref policyName
156 | PolicyDocument:
157 | Version: "2012-10-17"
158 | Statement:
159 | -
160 | Effect: "Allow"
161 | Action:
162 | - "sns:Publish"
163 | Resource: "*"
164 | # BEGIN USER POOL RESOURCES
165 | UserPool:
166 | # Created upon user selection
167 | # Depends on SNS Role for Arn if MFA is enabled
168 | Type: AWS::Cognito::UserPool
169 | Properties:
170 | UserPoolName: !If [ShouldNotCreateEnvResources, !Ref userPoolName, !Join ['',[!Ref userPoolName, '-', !Ref env]]]
171 | Schema:
172 |
173 | -
174 | Name: email
175 | Required: true
176 | Mutable: true
177 |
178 |
179 | AutoVerifiedAttributes: !Ref autoVerifiedAttributes
180 |
181 |
182 | EmailVerificationMessage: !Ref emailVerificationMessage
183 | EmailVerificationSubject: !Ref emailVerificationSubject
184 |
185 | Policies:
186 | PasswordPolicy:
187 | MinimumLength: !Ref passwordPolicyMinLength
188 | RequireLowercase: true
189 | RequireNumbers: true
190 | RequireSymbols: true
191 | RequireUppercase: true
192 | MfaConfiguration: !Ref mfaConfiguration
193 | SmsVerificationMessage: !Ref smsVerificationMessage
194 | SmsConfiguration:
195 | SnsCallerArn: !GetAtt SNSRole.Arn
196 | ExternalId: !Ref roleExternalId
197 |
198 | UserPoolClientWeb:
199 | # Created provide application access to user pool
200 | # Depends on UserPool for ID reference
201 | Type: "AWS::Cognito::UserPoolClient"
202 | Properties:
203 | ClientName: reactnotescognitoauth_app_clientWeb
204 |
205 | RefreshTokenValidity: !Ref userpoolClientRefreshTokenValidity
206 | UserPoolId: !Ref UserPool
207 | DependsOn: UserPool
208 | UserPoolClient:
209 | # Created provide application access to user pool
210 | # Depends on UserPool for ID reference
211 | Type: "AWS::Cognito::UserPoolClient"
212 | Properties:
213 | ClientName: !Ref userpoolClientName
214 |
215 | GenerateSecret: !Ref userpoolClientGenerateSecret
216 | RefreshTokenValidity: !Ref userpoolClientRefreshTokenValidity
217 | UserPoolId: !Ref UserPool
218 | DependsOn: UserPool
219 | # BEGIN USER POOL LAMBDA RESOURCES
220 | UserPoolClientRole:
221 | # Created to execute Lambda which gets userpool app client config values
222 | Type: 'AWS::IAM::Role'
223 | Properties:
224 | RoleName: !If [ShouldNotCreateEnvResources, !Ref userpoolClientLambdaRole, !Join ['',[!Ref userpoolClientLambdaRole, '-', !Ref env]]]
225 | AssumeRolePolicyDocument:
226 | Version: '2012-10-17'
227 | Statement:
228 | - Effect: Allow
229 | Principal:
230 | Service:
231 | - lambda.amazonaws.com
232 | Action:
233 | - 'sts:AssumeRole'
234 | DependsOn: UserPoolClient
235 | UserPoolClientLambda:
236 | # Lambda which gets userpool app client config values
237 | # Depends on UserPool for id
238 | # Depends on UserPoolClientRole for role ARN
239 | Type: 'AWS::Lambda::Function'
240 | Properties:
241 | Code:
242 | ZipFile: !Join
243 | - |+
244 | - - 'const response = require(''cfn-response'');'
245 | - 'const aws = require(''aws-sdk'');'
246 | - 'const identity = new aws.CognitoIdentityServiceProvider();'
247 | - 'exports.handler = (event, context, callback) => {'
248 | - ' if (event.RequestType == ''Delete'') { '
249 | - ' response.send(event, context, response.SUCCESS, {})'
250 | - ' }'
251 | - ' if (event.RequestType == ''Update'' || event.RequestType == ''Create'') {'
252 | - ' const params = {'
253 | - ' ClientId: event.ResourceProperties.clientId,'
254 | - ' UserPoolId: event.ResourceProperties.userpoolId'
255 | - ' };'
256 | - ' identity.describeUserPoolClient(params).promise()'
257 | - ' .then((res) => {'
258 | - ' response.send(event, context, response.SUCCESS, {''appSecret'': res.UserPoolClient.ClientSecret});'
259 | - ' })'
260 | - ' .catch((err) => {'
261 | - ' response.send(event, context, response.FAILURE, {err});'
262 | - ' });'
263 | - ' }'
264 | - '};'
265 | Handler: index.handler
266 | Runtime: nodejs8.10
267 | Timeout: '300'
268 | Role: !GetAtt
269 | - UserPoolClientRole
270 | - Arn
271 | DependsOn: UserPoolClientRole
272 | UserPoolClientLambdaPolicy:
273 | # Sets userpool policy for the role that executes the Userpool Client Lambda
274 | # Depends on UserPool for Arn
275 | # Marked as depending on UserPoolClientRole for easier to understand CFN sequencing
276 | Type: 'AWS::IAM::Policy'
277 | Properties:
278 | PolicyName: !Ref userpoolClientLambdaPolicy
279 | Roles:
280 | - !If [ShouldNotCreateEnvResources, !Ref userpoolClientLambdaRole, !Join ['',[!Ref userpoolClientLambdaRole, '-', !Ref env]]]
281 | PolicyDocument:
282 | Version: '2012-10-17'
283 | Statement:
284 | - Effect: Allow
285 | Action:
286 | - 'cognito-idp:DescribeUserPoolClient'
287 | Resource: !GetAtt UserPool.Arn
288 | DependsOn: UserPoolClientLambda
289 | UserPoolClientLogPolicy:
290 | # Sets log policy for the role that executes the Userpool Client Lambda
291 | # Depends on UserPool for Arn
292 | # Marked as depending on UserPoolClientLambdaPolicy for easier to understand CFN sequencing
293 | Type: 'AWS::IAM::Policy'
294 | Properties:
295 | PolicyName: !Ref userpoolClientLogPolicy
296 | Roles:
297 | - !If [ShouldNotCreateEnvResources, !Ref userpoolClientLambdaRole, !Join ['',[!Ref userpoolClientLambdaRole, '-', !Ref env]]]
298 | PolicyDocument:
299 | Version: 2012-10-17
300 | Statement:
301 | - Effect: Allow
302 | Action:
303 | - 'logs:CreateLogGroup'
304 | - 'logs:CreateLogStream'
305 | - 'logs:PutLogEvents'
306 | Resource: !Sub
307 | - arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*
308 | - { region: !Ref "AWS::Region", account: !Ref "AWS::AccountId", lambda: !Ref UserPoolClientLambda}
309 | DependsOn: UserPoolClientLambdaPolicy
310 | UserPoolClientInputs:
311 | # Values passed to Userpool client Lambda
312 | # Depends on UserPool for Id
313 | # Depends on UserPoolClient for Id
314 | # Marked as depending on UserPoolClientLambdaPolicy for easier to understand CFN sequencing
315 | Type: 'Custom::LambdaCallout'
316 | Properties:
317 | ServiceToken: !GetAtt UserPoolClientLambda.Arn
318 | clientId: !Ref UserPoolClient
319 | userpoolId: !Ref UserPool
320 | DependsOn: UserPoolClientLogPolicy
321 |
322 |
323 | # BEGIN IDENTITY POOL RESOURCES
324 |
325 |
326 | IdentityPool:
327 | # Always created
328 | Type: AWS::Cognito::IdentityPool
329 | Properties:
330 | IdentityPoolName: !If [ShouldNotCreateEnvResources, 'react_notes_id_pool', !Join ['',['react_notes_id_pool', '__', !Ref env]]]
331 |
332 | CognitoIdentityProviders:
333 | - ClientId: !Ref UserPoolClient
334 | ProviderName: !Sub
335 | - cognito-idp.${region}.amazonaws.com/${client}
336 | - { region: !Ref "AWS::Region", client: !Ref UserPool}
337 | - ClientId: !Ref UserPoolClientWeb
338 | ProviderName: !Sub
339 | - cognito-idp.${region}.amazonaws.com/${client}
340 | - { region: !Ref "AWS::Region", client: !Ref UserPool}
341 |
342 | AllowUnauthenticatedIdentities: !Ref allowUnauthenticatedIdentities
343 |
344 |
345 | DependsOn: UserPoolClientInputs
346 |
347 |
348 | IdentityPoolRoleMap:
349 | # Created to map Auth and Unauth roles to the identity pool
350 | # Depends on Identity Pool for ID ref
351 | Type: AWS::Cognito::IdentityPoolRoleAttachment
352 | Properties:
353 | IdentityPoolId: !Ref IdentityPool
354 | Roles:
355 | unauthenticated: !Ref unauthRoleArn
356 | authenticated: !Ref authRoleArn
357 | DependsOn: IdentityPool
358 |
359 |
360 | Outputs :
361 |
362 | IdentityPoolId:
363 | Value: !Ref 'IdentityPool'
364 | Description: Id for the identity pool
365 | IdentityPoolName:
366 | Value: !GetAtt IdentityPool.Name
367 |
368 |
369 | UserPoolId:
370 | Value: !Ref 'UserPool'
371 | Description: Id for the user pool
372 | UserPoolName:
373 | Value: !Ref userPoolName
374 | AppClientIDWeb:
375 | Value: !Ref 'UserPoolClientWeb'
376 | Description: The user pool app client id for web
377 | AppClientID:
378 | Value: !Ref 'UserPoolClient'
379 | Description: The user pool app client id
380 | AppClientSecret:
381 | Value: !GetAtt UserPoolClientInputs.appSecret
382 |
383 |
384 |
385 |
386 |
387 |
388 |
--------------------------------------------------------------------------------
/amplify/backend/awscloudformation/nested-cloudformation-stack.yml:
--------------------------------------------------------------------------------
1 | {
2 | "AWSTemplateFormatVersion": "2010-09-09",
3 | "Description": "Root stack for the Amplify AWS CloudFormation provider",
4 | "Parameters": {
5 | "DeploymentBucketName": {
6 | "Description": "Name of the common deployment bucket provided by the parent stack",
7 | "Type": "String",
8 | "Default": "DeploymentBucket"
9 | },
10 | "AuthRoleName": {
11 | "Type": "String",
12 | "Default": "AuthRoleName"
13 | },
14 | "UnauthRoleName": {
15 | "Type": "String",
16 | "Default": "UnauthRoleName"
17 | }
18 | },
19 | "Resources": {
20 | "DeploymentBucket": {
21 | "Type": "AWS::S3::Bucket",
22 | "DeletionPolicy": "Retain",
23 | "Properties": {
24 | "BucketName": {
25 | "Ref": "DeploymentBucketName"
26 | }
27 | }
28 | },
29 | "AuthRole": {
30 | "Type": "AWS::IAM::Role",
31 | "Properties": {
32 | "RoleName": {
33 | "Ref": "AuthRoleName"
34 | },
35 | "AssumeRolePolicyDocument": {
36 | "Version": "2012-10-17",
37 | "Statement": [
38 | {
39 | "Sid": "",
40 | "Effect": "Allow",
41 | "Principal": {
42 | "Federated": "cognito-identity.amazonaws.com"
43 | },
44 | "Action": "sts:AssumeRoleWithWebIdentity",
45 | "Condition": {
46 | "ForAnyValue:StringLike": {
47 | "cognito-identity.amazonaws.com:amr": "authenticated"
48 | }
49 | }
50 | }
51 | ]
52 | }
53 | }
54 | },
55 | "UnauthRole": {
56 | "Type": "AWS::IAM::Role",
57 | "Properties": {
58 | "RoleName": {
59 | "Ref": "UnauthRoleName"
60 | },
61 | "AssumeRolePolicyDocument": {
62 | "Version": "2012-10-17",
63 | "Statement": [
64 | {
65 | "Sid": "",
66 | "Effect": "Allow",
67 | "Principal": {
68 | "Federated": "cognito-identity.amazonaws.com"
69 | },
70 | "Action": "sts:AssumeRoleWithWebIdentity",
71 | "Condition": {
72 | "ForAnyValue:StringLike": {
73 | "cognito-identity.amazonaws.com:amr": "unauthenticated"
74 | }
75 | }
76 | }
77 | ]
78 | }
79 | }
80 | },
81 | "authreactnotescognitoauth": {
82 | "Type": "AWS::CloudFormation::Stack",
83 | "Properties": {
84 | "TemplateURL": "https://s3.amazonaws.com/react-notes-20181228114606-deployment/amplify-cfn-templates/auth/reactnotescognitoauth-cloudformation-template.yml",
85 | "Parameters": {
86 | "identityPoolName": "react_notes_id_pool",
87 | "allowUnauthenticatedIdentities": true,
88 | "thirdPartyAuth": false,
89 | "lambdaLogPolicy": "reactnotescognitoauth_lambda_log_policy",
90 | "openIdLambdaRoleName": "reactnotescognitoauth_openid_lambda_role",
91 | "openIdRolePolicy": "reactnotescognitoauth_openid_pass_role_policy",
92 | "openIdLambdaIAMPolicy": "reactnotescognitoauth_openid_lambda_iam_policy",
93 | "openIdLogPolicy": "reactnotescognitoauth_openid_lambda_log_policy",
94 | "userPoolName": "react_notes_user_pool",
95 | "autoVerifiedAttributes": "email",
96 | "mfaConfiguration": "OFF",
97 | "mfaTypes": "SMS Text Message",
98 | "roleName": "reactnotescognitoauth_sns-role",
99 | "roleExternalId": "reactnotescognitoauth_role_external_id",
100 | "policyName": "reactnotescognitoauth-sns-policy",
101 | "smsAuthenticationMessage": "Your authentication code is {####}",
102 | "smsVerificationMessage": "Your verification code is {####}",
103 | "emailVerificationSubject": "Your verification code",
104 | "emailVerificationMessage": "Your verification code is {####}",
105 | "defaultPasswordPolicy": false,
106 | "passwordPolicyMinLength": 8,
107 | "passwordPolicyCharacters": "Requires Lowercase,Requires Uppercase,Requires Numbers,Requires Symbols",
108 | "requiredAttributes": "email",
109 | "userpoolClientName": "reactnotescognitoauth_app_client",
110 | "userpoolClientGenerateSecret": true,
111 | "userpoolClientRefreshTokenValidity": 30,
112 | "userpoolClientReadAttributes": "email",
113 | "mfaLambdaRole": "reactnotescognitoauth_totp_lambda_role",
114 | "mfaLambdaLogPolicy": "reactnotescognitoauth_totp_lambda_log_policy",
115 | "mfaPassRolePolicy": "reactnotescognitoauth_totp_pass_role_policy",
116 | "mfaLambdaIAMPolicy": "reactnotescognitoauth_totp_lambda_iam_policy",
117 | "userpoolClientLambdaRole": "reactnotescognitoauth_userpoolclient_lambda_role",
118 | "userpoolClientLogPolicy": "reactnotescognitoauth_userpoolclient_lambda_log_policy",
119 | "userpoolClientLambdaPolicy": "reactnotescognitoauth_userpoolclient_lambda_iam_policy",
120 | "userpoolClientSetAttributes": false,
121 | "useDefault": "manual",
122 | "authSelections": "identityPoolAndUserPool",
123 | "resourceName": "reactnotescognitoauth",
124 | "authRoleName": {
125 | "Ref": "AuthRoleName"
126 | },
127 | "unauthRoleName": {
128 | "Ref": "UnauthRoleName"
129 | },
130 | "authRoleArn": {
131 | "Fn::GetAtt": [
132 | "AuthRole",
133 | "Arn"
134 | ]
135 | },
136 | "unauthRoleArn": {
137 | "Fn::GetAtt": [
138 | "UnauthRole",
139 | "Arn"
140 | ]
141 | },
142 | "env": "dev"
143 | }
144 | }
145 | },
146 | "apireactnotes": {
147 | "Type": "AWS::CloudFormation::Stack",
148 | "Properties": {
149 | "TemplateURL": "https://s3.amazonaws.com/react-notes-20181228114606-deployment/amplify-cfn-templates/api/cloudformation-template.json",
150 | "Parameters": {
151 | "AppSyncApiName": "reactnotes",
152 | "AuthCognitoUserPoolId": {
153 | "Fn::GetAtt": [
154 | "authreactnotescognitoauth",
155 | "Outputs.UserPoolId"
156 | ]
157 | },
158 | "schemaGraphql": "s3://react-notes-20181228114606-deployment/amplify-appsync-files/schema.graphql.1546457354441",
159 | "ResolverBucket": "react-notes-20181228114606-deployment",
160 | "ResolverRootKey": "amplify-appsync-files",
161 | "DeploymentTimestamp": "1546457354441",
162 | "env": "dev"
163 | }
164 | }
165 | },
166 | "analyticsreactnotes": {
167 | "Type": "AWS::CloudFormation::Stack",
168 | "Properties": {
169 | "TemplateURL": "https://s3.amazonaws.com/react-notes-20181228114606-deployment/amplify-cfn-templates/analytics/pinpoint-cloudformation-template.json",
170 | "Parameters": {
171 | "appName": "reactnotes",
172 | "roleName": "pinpointLambdaRoleda8c3f26",
173 | "cloudWatchPolicyName": "cloudWatchPolicyda8c3f26",
174 | "pinpointPolicyName": "pinpointPolicyda8c3f26",
175 | "authPolicyName": "pinpoint_amplify_da8c3f26",
176 | "unauthPolicyName": "pinpoint_amplify_da8c3f26",
177 | "authRoleName": {
178 | "Ref": "AuthRoleName"
179 | },
180 | "unauthRoleName": {
181 | "Ref": "UnauthRoleName"
182 | },
183 | "authRoleArn": {
184 | "Fn::GetAtt": [
185 | "AuthRole",
186 | "Arn"
187 | ]
188 | },
189 | "env": "dev"
190 | }
191 | }
192 | }
193 | },
194 | "Outputs": {
195 | "Region": {
196 | "Description": "CloudFormation provider root stack Region",
197 | "Value": {
198 | "Ref": "AWS::Region"
199 | },
200 | "Export": {
201 | "Name": {
202 | "Fn::Sub": "${AWS::StackName}-Region"
203 | }
204 | }
205 | },
206 | "StackName": {
207 | "Description": "CloudFormation provider root stack ID",
208 | "Value": {
209 | "Ref": "AWS::StackName"
210 | },
211 | "Export": {
212 | "Name": {
213 | "Fn::Sub": "${AWS::StackName}-StackName"
214 | }
215 | }
216 | },
217 | "StackId": {
218 | "Description": "CloudFormation provider root stack name",
219 | "Value": {
220 | "Ref": "AWS::StackId"
221 | },
222 | "Export": {
223 | "Name": {
224 | "Fn::Sub": "${AWS::StackName}-StackId"
225 | }
226 | }
227 | },
228 | "DeploymentBucketName": {
229 | "Description": "CloudFormation provider root stack deployment bucket name",
230 | "Value": {
231 | "Ref": "DeploymentBucketName"
232 | },
233 | "Export": {
234 | "Name": {
235 | "Fn::Sub": "${AWS::StackName}-DeploymentBucketName"
236 | }
237 | }
238 | },
239 | "AuthRoleArn": {
240 | "Value": {
241 | "Fn::GetAtt": [
242 | "AuthRole",
243 | "Arn"
244 | ]
245 | }
246 | },
247 | "UnauthRoleArn": {
248 | "Value": {
249 | "Fn::GetAtt": [
250 | "UnauthRole",
251 | "Arn"
252 | ]
253 | }
254 | },
255 | "AuthRoleName": {
256 | "Value": {
257 | "Ref": "AuthRole"
258 | }
259 | },
260 | "UnauthRoleName": {
261 | "Value": {
262 | "Ref": "UnauthRole"
263 | }
264 | }
265 | }
266 | }
--------------------------------------------------------------------------------
/amplify/backend/backend-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "auth": {
3 | "reactnotescognitoauth": {
4 | "service": "Cognito",
5 | "providerPlugin": "awscloudformation"
6 | }
7 | },
8 | "api": {
9 | "reactnotes": {
10 | "service": "AppSync",
11 | "providerPlugin": "awscloudformation",
12 | "output": {
13 | "securityType": "AMAZON_COGNITO_USER_POOLS"
14 | }
15 | }
16 | },
17 | "analytics": {
18 | "reactnotes": {
19 | "service": "Pinpoint",
20 | "providerPlugin": "awscloudformation"
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/auth.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dabit3/react-notes/2dca8e54f768dfb431cd90f66e7d3e6c3c9c0a7a/auth.jpg
--------------------------------------------------------------------------------
/hero.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dabit3/react-notes/2dca8e54f768dfb431cd90f66e7d3e6c3c9c0a7a/hero.jpg
--------------------------------------------------------------------------------
/notesapp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dabit3/react-notes/2dca8e54f768dfb431cd90f66e7d3e6c3c9c0a7a/notesapp.jpg
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-notes",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "aws-amplify": "^1.1.18",
7 | "aws-amplify-react": "^2.2.5",
8 | "glamor": "^2.20.40",
9 | "react": "^16.7.0",
10 | "react-dom": "^16.7.0",
11 | "react-icons": "^3.2.2",
12 | "react-scripts": "2.1.2"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": "react-app"
22 | },
23 | "browserslist": [
24 | ">0.2%",
25 | "not dead",
26 | "not ie <= 11",
27 | "not op_mini all"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dabit3/react-notes/2dca8e54f768dfb431cd90f66e7d3e6c3c9c0a7a/preview.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dabit3/react-notes/2dca8e54f768dfb431cd90f66e7d3e6c3c9c0a7a/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
15 |
16 |
25 | React App
26 |
27 |
28 | You need to enable JavaScript to run this app.
29 |
30 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/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": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 40vmin;
8 | }
9 |
10 | .App-header {
11 | background-color: #282c34;
12 | min-height: 100vh;
13 | display: flex;
14 | flex-direction: column;
15 | align-items: center;
16 | justify-content: center;
17 | font-size: calc(10px + 2vmin);
18 | color: white;
19 | }
20 |
21 | .App-link {
22 | color: #61dafb;
23 | }
24 |
25 | @keyframes App-logo-spin {
26 | from {
27 | transform: rotate(0deg);
28 | }
29 | to {
30 | transform: rotate(360deg);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { API, graphqlOperation } from 'aws-amplify'
3 | import { withAuthenticator } from 'aws-amplify-react'
4 | import { css } from 'glamor'
5 |
6 | import Form from './components/Form'
7 | import Notes from './components/Notes'
8 | import { createNote, updateNote, deleteNote } from './graphql/mutations'
9 | import { listNotes } from './graphql/queries'
10 |
11 | class App extends Component {
12 | state = { notes: [], filter: 'none' }
13 | async componentDidMount() {
14 | try {
15 | const { data: { listNotes: { items }}} = await API.graphql(graphqlOperation(listNotes))
16 | this.setState({ notes: items })
17 | } catch (err) {
18 | console.log('error fetching notes...', err)
19 | }
20 | }
21 | createNote = async note => {
22 | const notes = [note, ...this.state.notes]
23 | const newNotes = this.state.notes
24 | this.setState({ notes })
25 | try {
26 | const data = await API.graphql(graphqlOperation(createNote, { input: note }))
27 | this.setState({ notes: [data.data.createNote, ...newNotes] })
28 | } catch (err) {
29 | console.log('error creating note..', err)
30 | }
31 | }
32 | updateNote = async note => {
33 | const updatedNote = {
34 | ...note,
35 | status: note.status === 'new' ? 'completed' : 'new'
36 | }
37 | const index = this.state.notes.findIndex(i => i.id === note.id)
38 | const notes = [...this.state.notes]
39 | notes[index] = updatedNote
40 | this.setState({ notes })
41 |
42 | try {
43 | await API.graphql(graphqlOperation(updateNote, { input: updatedNote }))
44 | } catch (err) {
45 | console.log('error updating note...', err)
46 | }
47 | }
48 | deleteNote = async note => {
49 | const input = { id: note.id }
50 | const notes = this.state.notes.filter(n => n.id !== note.id)
51 | this.setState({ notes })
52 | try {
53 | await API.graphql(graphqlOperation(deleteNote, { input }))
54 | } catch (err) {
55 | console.log('error deleting note...', err)
56 | }
57 | }
58 | updateFilter = filter => this.setState({ filter })
59 | render() {
60 | let { notes, filter } = this.state
61 | if (filter === 'completed') {
62 | notes = notes.filter(n => n.status === 'completed')
63 | }
64 | if (filter === 'new') {
65 | notes = notes.filter(n => n.status === 'new')
66 | }
67 | return (
68 |
69 |
Notes
70 |
73 |
78 |
79 |
this.updateFilter('none')}
81 | {...css([ styles.menuItem, getStyle('none', filter)])}
82 | >All
83 |
this.updateFilter('completed')}
85 | {...css([styles.menuItem, getStyle('completed', filter)])}
86 | >Completed
87 |
this.updateFilter('new')}
89 | {...css([styles.menuItem, getStyle('new', filter)])}
90 | >Pending
91 |
92 |
93 | );
94 | }
95 | }
96 |
97 | function getStyle(type, filter) {
98 | if (type === filter) {
99 | return {
100 | fontWeight: 'bold'
101 | }
102 | }
103 | }
104 |
105 | const styles = {
106 | bottomMenu: {
107 | display: 'flex',
108 | marginTop: 10,
109 | justifyContent: 'center'
110 | },
111 | menuItem: {
112 | cursor: 'pointer',
113 | marginRight: 20
114 | },
115 | title: {
116 | fontSize: 44,
117 | margin: '10px 0px'
118 | },
119 | container: {
120 | textAlign: 'center'
121 | }
122 | }
123 |
124 | export default withAuthenticator(App, { includeGreetings: true })
125 |
--------------------------------------------------------------------------------
/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 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/components/Form.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { css } from 'glamor'
3 | import { MdAdd } from 'react-icons/md'
4 |
5 | class Form extends React.Component {
6 | state = { name: '' }
7 | onChange = e => {
8 | this.setState({ name: e.target.value })
9 | }
10 | handleKeyPress = (e) => {
11 | if (e.key === 'Enter' && this.state.name !== '') {
12 | const note = {
13 | ...this.state, status: 'new'
14 | }
15 | this.props.createNote(note)
16 | this.setState({ name: '' })
17 | }
18 | }
19 | render() {
20 | return (
21 |
22 |
23 |
24 | this.onChange(e)}
29 | value={this.state.name}
30 | />
31 |
32 |
33 | )
34 | }
35 | }
36 |
37 | const styles = {
38 | container: {
39 | width: 360,
40 | margin: '0 auto',
41 | borderBottom: '1px solid #ededed',
42 | },
43 | form: {
44 | display: 'flex',
45 | justifyContent: 'center',
46 | alignItems: 'center'
47 | },
48 | input: {
49 | height: 35,
50 | width: '360px',
51 | border: 'none',
52 | outline: 'none',
53 | marginLeft: 10,
54 | fontSize: 20,
55 | padding: 8,
56 | }
57 | }
58 |
59 | export default Form
--------------------------------------------------------------------------------
/src/components/Note.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { css } from 'glamor'
3 | import { FaTimes, FaCircle } from 'react-icons/fa'
4 | import { MdCheckCircle } from 'react-icons/md';
5 |
6 | class Note extends React.Component {
7 | render() {
8 | const { name, status } = this.props.note
9 | return (
10 |
11 | {
12 | status === 'new' && (
13 |
this.props.updateNote(this.props.note)}
18 | />
19 | )
20 | }
21 | {
22 | status === 'completed' && (
23 | this.props.updateNote(this.props.note)}
28 | />
29 | )
30 | }
31 | {name}
32 |
33 | this.props.deleteNote(this.props.note)}
35 | color='red'
36 | size={22}
37 | {...css(styles.times)}
38 | />
39 |
40 |
41 | )
42 | }
43 | }
44 |
45 | const styles = {
46 | container: {
47 | borderBottom: '1px solid rgba(0, 0, 0, .15)',
48 | display: 'flex',
49 | alignItems: 'center'
50 | },
51 | name: {
52 | textAlign: 'left',
53 | fontSize: 18
54 | },
55 | iconContainer: {
56 | display: 'flex',
57 | flex: 1,
58 | justifyContent: 'flex-end',
59 | alignItems: 'center'
60 | },
61 | new: {
62 | marginRight: 10,
63 | cursor: 'pointer',
64 | opacity: .3
65 | },
66 | completed: {
67 | marginRight: 10,
68 | cursor: 'pointer'
69 | },
70 | times: {
71 | cursor: 'pointer',
72 | opacity: 0.7
73 | }
74 | }
75 |
76 | export default Note
--------------------------------------------------------------------------------
/src/components/Notes.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { css } from 'glamor'
3 |
4 | import Note from './Note'
5 |
6 | class Notes extends React.Component {
7 | render() {
8 | return (
9 |
10 | {
11 | this.props.notes.map((t, i) => (
12 |
18 | ))
19 | }
20 |
21 | )
22 | }
23 | }
24 |
25 | const styles = {
26 | container: {
27 | width: '360px',
28 | margin: '0 auto',
29 | '@media(max-width: 360px)': {
30 | width: 'calc(100% - 40px)'
31 | }
32 | }
33 | }
34 |
35 | export default Notes
--------------------------------------------------------------------------------
/src/graphql/mutations.js:
--------------------------------------------------------------------------------
1 | // eslint-disable
2 | // this is an auto generated file. This will be overwritten
3 |
4 | export const createNote = `mutation CreateNote($input: CreateNoteInput!) {
5 | createNote(input: $input) {
6 | id
7 | name
8 | description
9 | status
10 | }
11 | }
12 | `;
13 | export const updateNote = `mutation UpdateNote($input: UpdateNoteInput!) {
14 | updateNote(input: $input) {
15 | id
16 | name
17 | description
18 | status
19 | }
20 | }
21 | `;
22 | export const deleteNote = `mutation DeleteNote($input: DeleteNoteInput!) {
23 | deleteNote(input: $input) {
24 | id
25 | name
26 | description
27 | status
28 | }
29 | }
30 | `;
31 |
--------------------------------------------------------------------------------
/src/graphql/queries.js:
--------------------------------------------------------------------------------
1 | // eslint-disable
2 | // this is an auto generated file. This will be overwritten
3 |
4 | export const getNote = `query GetNote($id: ID!) {
5 | getNote(id: $id) {
6 | id
7 | name
8 | description
9 | status
10 | }
11 | }
12 | `;
13 | export const listNotes = `query ListNotes(
14 | $filter: ModelNoteFilterInput
15 | $limit: Int
16 | $nextToken: String
17 | ) {
18 | listNotes(filter: $filter, limit: $limit, nextToken: $nextToken) {
19 | items {
20 | id
21 | name
22 | description
23 | status
24 | }
25 | nextToken
26 | }
27 | }
28 | `;
29 |
--------------------------------------------------------------------------------
/src/graphql/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "data" : {
3 | "__schema" : {
4 | "queryType" : {
5 | "name" : "Query"
6 | },
7 | "mutationType" : {
8 | "name" : "Mutation"
9 | },
10 | "subscriptionType" : {
11 | "name" : "Subscription"
12 | },
13 | "types" : [ {
14 | "kind" : "OBJECT",
15 | "name" : "Query",
16 | "description" : null,
17 | "fields" : [ {
18 | "name" : "getNote",
19 | "description" : null,
20 | "args" : [ {
21 | "name" : "id",
22 | "description" : null,
23 | "type" : {
24 | "kind" : "NON_NULL",
25 | "name" : null,
26 | "ofType" : {
27 | "kind" : "SCALAR",
28 | "name" : "ID",
29 | "ofType" : null
30 | }
31 | },
32 | "defaultValue" : null
33 | } ],
34 | "type" : {
35 | "kind" : "OBJECT",
36 | "name" : "Note",
37 | "ofType" : null
38 | },
39 | "isDeprecated" : false,
40 | "deprecationReason" : null
41 | }, {
42 | "name" : "listNotes",
43 | "description" : null,
44 | "args" : [ {
45 | "name" : "filter",
46 | "description" : null,
47 | "type" : {
48 | "kind" : "INPUT_OBJECT",
49 | "name" : "ModelNoteFilterInput",
50 | "ofType" : null
51 | },
52 | "defaultValue" : null
53 | }, {
54 | "name" : "limit",
55 | "description" : null,
56 | "type" : {
57 | "kind" : "SCALAR",
58 | "name" : "Int",
59 | "ofType" : null
60 | },
61 | "defaultValue" : null
62 | }, {
63 | "name" : "nextToken",
64 | "description" : null,
65 | "type" : {
66 | "kind" : "SCALAR",
67 | "name" : "String",
68 | "ofType" : null
69 | },
70 | "defaultValue" : null
71 | } ],
72 | "type" : {
73 | "kind" : "OBJECT",
74 | "name" : "ModelNoteConnection",
75 | "ofType" : null
76 | },
77 | "isDeprecated" : false,
78 | "deprecationReason" : null
79 | } ],
80 | "inputFields" : null,
81 | "interfaces" : [ ],
82 | "enumValues" : null,
83 | "possibleTypes" : null
84 | }, {
85 | "kind" : "OBJECT",
86 | "name" : "Note",
87 | "description" : null,
88 | "fields" : [ {
89 | "name" : "id",
90 | "description" : null,
91 | "args" : [ ],
92 | "type" : {
93 | "kind" : "NON_NULL",
94 | "name" : null,
95 | "ofType" : {
96 | "kind" : "SCALAR",
97 | "name" : "ID",
98 | "ofType" : null
99 | }
100 | },
101 | "isDeprecated" : false,
102 | "deprecationReason" : null
103 | }, {
104 | "name" : "name",
105 | "description" : null,
106 | "args" : [ ],
107 | "type" : {
108 | "kind" : "NON_NULL",
109 | "name" : null,
110 | "ofType" : {
111 | "kind" : "SCALAR",
112 | "name" : "String",
113 | "ofType" : null
114 | }
115 | },
116 | "isDeprecated" : false,
117 | "deprecationReason" : null
118 | }, {
119 | "name" : "description",
120 | "description" : null,
121 | "args" : [ ],
122 | "type" : {
123 | "kind" : "SCALAR",
124 | "name" : "String",
125 | "ofType" : null
126 | },
127 | "isDeprecated" : false,
128 | "deprecationReason" : null
129 | }, {
130 | "name" : "status",
131 | "description" : null,
132 | "args" : [ ],
133 | "type" : {
134 | "kind" : "NON_NULL",
135 | "name" : null,
136 | "ofType" : {
137 | "kind" : "SCALAR",
138 | "name" : "String",
139 | "ofType" : null
140 | }
141 | },
142 | "isDeprecated" : false,
143 | "deprecationReason" : null
144 | } ],
145 | "inputFields" : null,
146 | "interfaces" : [ ],
147 | "enumValues" : null,
148 | "possibleTypes" : null
149 | }, {
150 | "kind" : "SCALAR",
151 | "name" : "ID",
152 | "description" : "Built-in ID",
153 | "fields" : null,
154 | "inputFields" : null,
155 | "interfaces" : null,
156 | "enumValues" : null,
157 | "possibleTypes" : null
158 | }, {
159 | "kind" : "SCALAR",
160 | "name" : "String",
161 | "description" : "Built-in String",
162 | "fields" : null,
163 | "inputFields" : null,
164 | "interfaces" : null,
165 | "enumValues" : null,
166 | "possibleTypes" : null
167 | }, {
168 | "kind" : "OBJECT",
169 | "name" : "ModelNoteConnection",
170 | "description" : null,
171 | "fields" : [ {
172 | "name" : "items",
173 | "description" : null,
174 | "args" : [ ],
175 | "type" : {
176 | "kind" : "LIST",
177 | "name" : null,
178 | "ofType" : {
179 | "kind" : "OBJECT",
180 | "name" : "Note",
181 | "ofType" : null
182 | }
183 | },
184 | "isDeprecated" : false,
185 | "deprecationReason" : null
186 | }, {
187 | "name" : "nextToken",
188 | "description" : null,
189 | "args" : [ ],
190 | "type" : {
191 | "kind" : "SCALAR",
192 | "name" : "String",
193 | "ofType" : null
194 | },
195 | "isDeprecated" : false,
196 | "deprecationReason" : null
197 | } ],
198 | "inputFields" : null,
199 | "interfaces" : [ ],
200 | "enumValues" : null,
201 | "possibleTypes" : null
202 | }, {
203 | "kind" : "INPUT_OBJECT",
204 | "name" : "ModelNoteFilterInput",
205 | "description" : null,
206 | "fields" : null,
207 | "inputFields" : [ {
208 | "name" : "id",
209 | "description" : null,
210 | "type" : {
211 | "kind" : "INPUT_OBJECT",
212 | "name" : "ModelIDFilterInput",
213 | "ofType" : null
214 | },
215 | "defaultValue" : null
216 | }, {
217 | "name" : "name",
218 | "description" : null,
219 | "type" : {
220 | "kind" : "INPUT_OBJECT",
221 | "name" : "ModelStringFilterInput",
222 | "ofType" : null
223 | },
224 | "defaultValue" : null
225 | }, {
226 | "name" : "description",
227 | "description" : null,
228 | "type" : {
229 | "kind" : "INPUT_OBJECT",
230 | "name" : "ModelStringFilterInput",
231 | "ofType" : null
232 | },
233 | "defaultValue" : null
234 | }, {
235 | "name" : "status",
236 | "description" : null,
237 | "type" : {
238 | "kind" : "INPUT_OBJECT",
239 | "name" : "ModelStringFilterInput",
240 | "ofType" : null
241 | },
242 | "defaultValue" : null
243 | }, {
244 | "name" : "and",
245 | "description" : null,
246 | "type" : {
247 | "kind" : "LIST",
248 | "name" : null,
249 | "ofType" : {
250 | "kind" : "INPUT_OBJECT",
251 | "name" : "ModelNoteFilterInput",
252 | "ofType" : null
253 | }
254 | },
255 | "defaultValue" : null
256 | }, {
257 | "name" : "or",
258 | "description" : null,
259 | "type" : {
260 | "kind" : "LIST",
261 | "name" : null,
262 | "ofType" : {
263 | "kind" : "INPUT_OBJECT",
264 | "name" : "ModelNoteFilterInput",
265 | "ofType" : null
266 | }
267 | },
268 | "defaultValue" : null
269 | }, {
270 | "name" : "not",
271 | "description" : null,
272 | "type" : {
273 | "kind" : "INPUT_OBJECT",
274 | "name" : "ModelNoteFilterInput",
275 | "ofType" : null
276 | },
277 | "defaultValue" : null
278 | } ],
279 | "interfaces" : null,
280 | "enumValues" : null,
281 | "possibleTypes" : null
282 | }, {
283 | "kind" : "INPUT_OBJECT",
284 | "name" : "ModelIDFilterInput",
285 | "description" : null,
286 | "fields" : null,
287 | "inputFields" : [ {
288 | "name" : "ne",
289 | "description" : null,
290 | "type" : {
291 | "kind" : "SCALAR",
292 | "name" : "ID",
293 | "ofType" : null
294 | },
295 | "defaultValue" : null
296 | }, {
297 | "name" : "eq",
298 | "description" : null,
299 | "type" : {
300 | "kind" : "SCALAR",
301 | "name" : "ID",
302 | "ofType" : null
303 | },
304 | "defaultValue" : null
305 | }, {
306 | "name" : "le",
307 | "description" : null,
308 | "type" : {
309 | "kind" : "SCALAR",
310 | "name" : "ID",
311 | "ofType" : null
312 | },
313 | "defaultValue" : null
314 | }, {
315 | "name" : "lt",
316 | "description" : null,
317 | "type" : {
318 | "kind" : "SCALAR",
319 | "name" : "ID",
320 | "ofType" : null
321 | },
322 | "defaultValue" : null
323 | }, {
324 | "name" : "ge",
325 | "description" : null,
326 | "type" : {
327 | "kind" : "SCALAR",
328 | "name" : "ID",
329 | "ofType" : null
330 | },
331 | "defaultValue" : null
332 | }, {
333 | "name" : "gt",
334 | "description" : null,
335 | "type" : {
336 | "kind" : "SCALAR",
337 | "name" : "ID",
338 | "ofType" : null
339 | },
340 | "defaultValue" : null
341 | }, {
342 | "name" : "contains",
343 | "description" : null,
344 | "type" : {
345 | "kind" : "SCALAR",
346 | "name" : "ID",
347 | "ofType" : null
348 | },
349 | "defaultValue" : null
350 | }, {
351 | "name" : "notContains",
352 | "description" : null,
353 | "type" : {
354 | "kind" : "SCALAR",
355 | "name" : "ID",
356 | "ofType" : null
357 | },
358 | "defaultValue" : null
359 | }, {
360 | "name" : "between",
361 | "description" : null,
362 | "type" : {
363 | "kind" : "LIST",
364 | "name" : null,
365 | "ofType" : {
366 | "kind" : "SCALAR",
367 | "name" : "ID",
368 | "ofType" : null
369 | }
370 | },
371 | "defaultValue" : null
372 | }, {
373 | "name" : "beginsWith",
374 | "description" : null,
375 | "type" : {
376 | "kind" : "SCALAR",
377 | "name" : "ID",
378 | "ofType" : null
379 | },
380 | "defaultValue" : null
381 | } ],
382 | "interfaces" : null,
383 | "enumValues" : null,
384 | "possibleTypes" : null
385 | }, {
386 | "kind" : "INPUT_OBJECT",
387 | "name" : "ModelStringFilterInput",
388 | "description" : null,
389 | "fields" : null,
390 | "inputFields" : [ {
391 | "name" : "ne",
392 | "description" : null,
393 | "type" : {
394 | "kind" : "SCALAR",
395 | "name" : "String",
396 | "ofType" : null
397 | },
398 | "defaultValue" : null
399 | }, {
400 | "name" : "eq",
401 | "description" : null,
402 | "type" : {
403 | "kind" : "SCALAR",
404 | "name" : "String",
405 | "ofType" : null
406 | },
407 | "defaultValue" : null
408 | }, {
409 | "name" : "le",
410 | "description" : null,
411 | "type" : {
412 | "kind" : "SCALAR",
413 | "name" : "String",
414 | "ofType" : null
415 | },
416 | "defaultValue" : null
417 | }, {
418 | "name" : "lt",
419 | "description" : null,
420 | "type" : {
421 | "kind" : "SCALAR",
422 | "name" : "String",
423 | "ofType" : null
424 | },
425 | "defaultValue" : null
426 | }, {
427 | "name" : "ge",
428 | "description" : null,
429 | "type" : {
430 | "kind" : "SCALAR",
431 | "name" : "String",
432 | "ofType" : null
433 | },
434 | "defaultValue" : null
435 | }, {
436 | "name" : "gt",
437 | "description" : null,
438 | "type" : {
439 | "kind" : "SCALAR",
440 | "name" : "String",
441 | "ofType" : null
442 | },
443 | "defaultValue" : null
444 | }, {
445 | "name" : "contains",
446 | "description" : null,
447 | "type" : {
448 | "kind" : "SCALAR",
449 | "name" : "String",
450 | "ofType" : null
451 | },
452 | "defaultValue" : null
453 | }, {
454 | "name" : "notContains",
455 | "description" : null,
456 | "type" : {
457 | "kind" : "SCALAR",
458 | "name" : "String",
459 | "ofType" : null
460 | },
461 | "defaultValue" : null
462 | }, {
463 | "name" : "between",
464 | "description" : null,
465 | "type" : {
466 | "kind" : "LIST",
467 | "name" : null,
468 | "ofType" : {
469 | "kind" : "SCALAR",
470 | "name" : "String",
471 | "ofType" : null
472 | }
473 | },
474 | "defaultValue" : null
475 | }, {
476 | "name" : "beginsWith",
477 | "description" : null,
478 | "type" : {
479 | "kind" : "SCALAR",
480 | "name" : "String",
481 | "ofType" : null
482 | },
483 | "defaultValue" : null
484 | } ],
485 | "interfaces" : null,
486 | "enumValues" : null,
487 | "possibleTypes" : null
488 | }, {
489 | "kind" : "SCALAR",
490 | "name" : "Int",
491 | "description" : "Built-in Int",
492 | "fields" : null,
493 | "inputFields" : null,
494 | "interfaces" : null,
495 | "enumValues" : null,
496 | "possibleTypes" : null
497 | }, {
498 | "kind" : "OBJECT",
499 | "name" : "Mutation",
500 | "description" : null,
501 | "fields" : [ {
502 | "name" : "createNote",
503 | "description" : null,
504 | "args" : [ {
505 | "name" : "input",
506 | "description" : null,
507 | "type" : {
508 | "kind" : "NON_NULL",
509 | "name" : null,
510 | "ofType" : {
511 | "kind" : "INPUT_OBJECT",
512 | "name" : "CreateNoteInput",
513 | "ofType" : null
514 | }
515 | },
516 | "defaultValue" : null
517 | } ],
518 | "type" : {
519 | "kind" : "OBJECT",
520 | "name" : "Note",
521 | "ofType" : null
522 | },
523 | "isDeprecated" : false,
524 | "deprecationReason" : null
525 | }, {
526 | "name" : "updateNote",
527 | "description" : null,
528 | "args" : [ {
529 | "name" : "input",
530 | "description" : null,
531 | "type" : {
532 | "kind" : "NON_NULL",
533 | "name" : null,
534 | "ofType" : {
535 | "kind" : "INPUT_OBJECT",
536 | "name" : "UpdateNoteInput",
537 | "ofType" : null
538 | }
539 | },
540 | "defaultValue" : null
541 | } ],
542 | "type" : {
543 | "kind" : "OBJECT",
544 | "name" : "Note",
545 | "ofType" : null
546 | },
547 | "isDeprecated" : false,
548 | "deprecationReason" : null
549 | }, {
550 | "name" : "deleteNote",
551 | "description" : null,
552 | "args" : [ {
553 | "name" : "input",
554 | "description" : null,
555 | "type" : {
556 | "kind" : "NON_NULL",
557 | "name" : null,
558 | "ofType" : {
559 | "kind" : "INPUT_OBJECT",
560 | "name" : "DeleteNoteInput",
561 | "ofType" : null
562 | }
563 | },
564 | "defaultValue" : null
565 | } ],
566 | "type" : {
567 | "kind" : "OBJECT",
568 | "name" : "Note",
569 | "ofType" : null
570 | },
571 | "isDeprecated" : false,
572 | "deprecationReason" : null
573 | } ],
574 | "inputFields" : null,
575 | "interfaces" : [ ],
576 | "enumValues" : null,
577 | "possibleTypes" : null
578 | }, {
579 | "kind" : "INPUT_OBJECT",
580 | "name" : "CreateNoteInput",
581 | "description" : null,
582 | "fields" : null,
583 | "inputFields" : [ {
584 | "name" : "id",
585 | "description" : null,
586 | "type" : {
587 | "kind" : "SCALAR",
588 | "name" : "ID",
589 | "ofType" : null
590 | },
591 | "defaultValue" : null
592 | }, {
593 | "name" : "name",
594 | "description" : null,
595 | "type" : {
596 | "kind" : "NON_NULL",
597 | "name" : null,
598 | "ofType" : {
599 | "kind" : "SCALAR",
600 | "name" : "String",
601 | "ofType" : null
602 | }
603 | },
604 | "defaultValue" : null
605 | }, {
606 | "name" : "description",
607 | "description" : null,
608 | "type" : {
609 | "kind" : "SCALAR",
610 | "name" : "String",
611 | "ofType" : null
612 | },
613 | "defaultValue" : null
614 | }, {
615 | "name" : "status",
616 | "description" : null,
617 | "type" : {
618 | "kind" : "NON_NULL",
619 | "name" : null,
620 | "ofType" : {
621 | "kind" : "SCALAR",
622 | "name" : "String",
623 | "ofType" : null
624 | }
625 | },
626 | "defaultValue" : null
627 | } ],
628 | "interfaces" : null,
629 | "enumValues" : null,
630 | "possibleTypes" : null
631 | }, {
632 | "kind" : "INPUT_OBJECT",
633 | "name" : "UpdateNoteInput",
634 | "description" : null,
635 | "fields" : null,
636 | "inputFields" : [ {
637 | "name" : "id",
638 | "description" : null,
639 | "type" : {
640 | "kind" : "NON_NULL",
641 | "name" : null,
642 | "ofType" : {
643 | "kind" : "SCALAR",
644 | "name" : "ID",
645 | "ofType" : null
646 | }
647 | },
648 | "defaultValue" : null
649 | }, {
650 | "name" : "name",
651 | "description" : null,
652 | "type" : {
653 | "kind" : "SCALAR",
654 | "name" : "String",
655 | "ofType" : null
656 | },
657 | "defaultValue" : null
658 | }, {
659 | "name" : "description",
660 | "description" : null,
661 | "type" : {
662 | "kind" : "SCALAR",
663 | "name" : "String",
664 | "ofType" : null
665 | },
666 | "defaultValue" : null
667 | }, {
668 | "name" : "status",
669 | "description" : null,
670 | "type" : {
671 | "kind" : "SCALAR",
672 | "name" : "String",
673 | "ofType" : null
674 | },
675 | "defaultValue" : null
676 | } ],
677 | "interfaces" : null,
678 | "enumValues" : null,
679 | "possibleTypes" : null
680 | }, {
681 | "kind" : "INPUT_OBJECT",
682 | "name" : "DeleteNoteInput",
683 | "description" : null,
684 | "fields" : null,
685 | "inputFields" : [ {
686 | "name" : "id",
687 | "description" : null,
688 | "type" : {
689 | "kind" : "SCALAR",
690 | "name" : "ID",
691 | "ofType" : null
692 | },
693 | "defaultValue" : null
694 | } ],
695 | "interfaces" : null,
696 | "enumValues" : null,
697 | "possibleTypes" : null
698 | }, {
699 | "kind" : "OBJECT",
700 | "name" : "Subscription",
701 | "description" : null,
702 | "fields" : [ {
703 | "name" : "onCreateNote",
704 | "description" : null,
705 | "args" : [ ],
706 | "type" : {
707 | "kind" : "OBJECT",
708 | "name" : "Note",
709 | "ofType" : null
710 | },
711 | "isDeprecated" : false,
712 | "deprecationReason" : null
713 | }, {
714 | "name" : "onUpdateNote",
715 | "description" : null,
716 | "args" : [ ],
717 | "type" : {
718 | "kind" : "OBJECT",
719 | "name" : "Note",
720 | "ofType" : null
721 | },
722 | "isDeprecated" : false,
723 | "deprecationReason" : null
724 | }, {
725 | "name" : "onDeleteNote",
726 | "description" : null,
727 | "args" : [ ],
728 | "type" : {
729 | "kind" : "OBJECT",
730 | "name" : "Note",
731 | "ofType" : null
732 | },
733 | "isDeprecated" : false,
734 | "deprecationReason" : null
735 | } ],
736 | "inputFields" : null,
737 | "interfaces" : [ ],
738 | "enumValues" : null,
739 | "possibleTypes" : null
740 | }, {
741 | "kind" : "INPUT_OBJECT",
742 | "name" : "ModelIntFilterInput",
743 | "description" : null,
744 | "fields" : null,
745 | "inputFields" : [ {
746 | "name" : "ne",
747 | "description" : null,
748 | "type" : {
749 | "kind" : "SCALAR",
750 | "name" : "Int",
751 | "ofType" : null
752 | },
753 | "defaultValue" : null
754 | }, {
755 | "name" : "eq",
756 | "description" : null,
757 | "type" : {
758 | "kind" : "SCALAR",
759 | "name" : "Int",
760 | "ofType" : null
761 | },
762 | "defaultValue" : null
763 | }, {
764 | "name" : "le",
765 | "description" : null,
766 | "type" : {
767 | "kind" : "SCALAR",
768 | "name" : "Int",
769 | "ofType" : null
770 | },
771 | "defaultValue" : null
772 | }, {
773 | "name" : "lt",
774 | "description" : null,
775 | "type" : {
776 | "kind" : "SCALAR",
777 | "name" : "Int",
778 | "ofType" : null
779 | },
780 | "defaultValue" : null
781 | }, {
782 | "name" : "ge",
783 | "description" : null,
784 | "type" : {
785 | "kind" : "SCALAR",
786 | "name" : "Int",
787 | "ofType" : null
788 | },
789 | "defaultValue" : null
790 | }, {
791 | "name" : "gt",
792 | "description" : null,
793 | "type" : {
794 | "kind" : "SCALAR",
795 | "name" : "Int",
796 | "ofType" : null
797 | },
798 | "defaultValue" : null
799 | }, {
800 | "name" : "contains",
801 | "description" : null,
802 | "type" : {
803 | "kind" : "SCALAR",
804 | "name" : "Int",
805 | "ofType" : null
806 | },
807 | "defaultValue" : null
808 | }, {
809 | "name" : "notContains",
810 | "description" : null,
811 | "type" : {
812 | "kind" : "SCALAR",
813 | "name" : "Int",
814 | "ofType" : null
815 | },
816 | "defaultValue" : null
817 | }, {
818 | "name" : "between",
819 | "description" : null,
820 | "type" : {
821 | "kind" : "LIST",
822 | "name" : null,
823 | "ofType" : {
824 | "kind" : "SCALAR",
825 | "name" : "Int",
826 | "ofType" : null
827 | }
828 | },
829 | "defaultValue" : null
830 | } ],
831 | "interfaces" : null,
832 | "enumValues" : null,
833 | "possibleTypes" : null
834 | }, {
835 | "kind" : "INPUT_OBJECT",
836 | "name" : "ModelFloatFilterInput",
837 | "description" : null,
838 | "fields" : null,
839 | "inputFields" : [ {
840 | "name" : "ne",
841 | "description" : null,
842 | "type" : {
843 | "kind" : "SCALAR",
844 | "name" : "Float",
845 | "ofType" : null
846 | },
847 | "defaultValue" : null
848 | }, {
849 | "name" : "eq",
850 | "description" : null,
851 | "type" : {
852 | "kind" : "SCALAR",
853 | "name" : "Float",
854 | "ofType" : null
855 | },
856 | "defaultValue" : null
857 | }, {
858 | "name" : "le",
859 | "description" : null,
860 | "type" : {
861 | "kind" : "SCALAR",
862 | "name" : "Float",
863 | "ofType" : null
864 | },
865 | "defaultValue" : null
866 | }, {
867 | "name" : "lt",
868 | "description" : null,
869 | "type" : {
870 | "kind" : "SCALAR",
871 | "name" : "Float",
872 | "ofType" : null
873 | },
874 | "defaultValue" : null
875 | }, {
876 | "name" : "ge",
877 | "description" : null,
878 | "type" : {
879 | "kind" : "SCALAR",
880 | "name" : "Float",
881 | "ofType" : null
882 | },
883 | "defaultValue" : null
884 | }, {
885 | "name" : "gt",
886 | "description" : null,
887 | "type" : {
888 | "kind" : "SCALAR",
889 | "name" : "Float",
890 | "ofType" : null
891 | },
892 | "defaultValue" : null
893 | }, {
894 | "name" : "contains",
895 | "description" : null,
896 | "type" : {
897 | "kind" : "SCALAR",
898 | "name" : "Float",
899 | "ofType" : null
900 | },
901 | "defaultValue" : null
902 | }, {
903 | "name" : "notContains",
904 | "description" : null,
905 | "type" : {
906 | "kind" : "SCALAR",
907 | "name" : "Float",
908 | "ofType" : null
909 | },
910 | "defaultValue" : null
911 | }, {
912 | "name" : "between",
913 | "description" : null,
914 | "type" : {
915 | "kind" : "LIST",
916 | "name" : null,
917 | "ofType" : {
918 | "kind" : "SCALAR",
919 | "name" : "Float",
920 | "ofType" : null
921 | }
922 | },
923 | "defaultValue" : null
924 | } ],
925 | "interfaces" : null,
926 | "enumValues" : null,
927 | "possibleTypes" : null
928 | }, {
929 | "kind" : "SCALAR",
930 | "name" : "Float",
931 | "description" : "Built-in Float",
932 | "fields" : null,
933 | "inputFields" : null,
934 | "interfaces" : null,
935 | "enumValues" : null,
936 | "possibleTypes" : null
937 | }, {
938 | "kind" : "ENUM",
939 | "name" : "ModelSortDirection",
940 | "description" : null,
941 | "fields" : null,
942 | "inputFields" : null,
943 | "interfaces" : null,
944 | "enumValues" : [ {
945 | "name" : "ASC",
946 | "description" : null,
947 | "isDeprecated" : false,
948 | "deprecationReason" : null
949 | }, {
950 | "name" : "DESC",
951 | "description" : null,
952 | "isDeprecated" : false,
953 | "deprecationReason" : null
954 | } ],
955 | "possibleTypes" : null
956 | }, {
957 | "kind" : "INPUT_OBJECT",
958 | "name" : "ModelBooleanFilterInput",
959 | "description" : null,
960 | "fields" : null,
961 | "inputFields" : [ {
962 | "name" : "ne",
963 | "description" : null,
964 | "type" : {
965 | "kind" : "SCALAR",
966 | "name" : "Boolean",
967 | "ofType" : null
968 | },
969 | "defaultValue" : null
970 | }, {
971 | "name" : "eq",
972 | "description" : null,
973 | "type" : {
974 | "kind" : "SCALAR",
975 | "name" : "Boolean",
976 | "ofType" : null
977 | },
978 | "defaultValue" : null
979 | } ],
980 | "interfaces" : null,
981 | "enumValues" : null,
982 | "possibleTypes" : null
983 | }, {
984 | "kind" : "SCALAR",
985 | "name" : "Boolean",
986 | "description" : "Built-in Boolean",
987 | "fields" : null,
988 | "inputFields" : null,
989 | "interfaces" : null,
990 | "enumValues" : null,
991 | "possibleTypes" : null
992 | }, {
993 | "kind" : "OBJECT",
994 | "name" : "__Schema",
995 | "description" : "A GraphQL Introspection defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, the entry points for query, mutation, and subscription operations.",
996 | "fields" : [ {
997 | "name" : "types",
998 | "description" : "A list of all types supported by this server.",
999 | "args" : [ ],
1000 | "type" : {
1001 | "kind" : "NON_NULL",
1002 | "name" : null,
1003 | "ofType" : {
1004 | "kind" : "LIST",
1005 | "name" : null,
1006 | "ofType" : {
1007 | "kind" : "NON_NULL",
1008 | "name" : null,
1009 | "ofType" : {
1010 | "kind" : "OBJECT",
1011 | "name" : "__Type",
1012 | "ofType" : null
1013 | }
1014 | }
1015 | }
1016 | },
1017 | "isDeprecated" : false,
1018 | "deprecationReason" : null
1019 | }, {
1020 | "name" : "queryType",
1021 | "description" : "The type that query operations will be rooted at.",
1022 | "args" : [ ],
1023 | "type" : {
1024 | "kind" : "NON_NULL",
1025 | "name" : null,
1026 | "ofType" : {
1027 | "kind" : "OBJECT",
1028 | "name" : "__Type",
1029 | "ofType" : null
1030 | }
1031 | },
1032 | "isDeprecated" : false,
1033 | "deprecationReason" : null
1034 | }, {
1035 | "name" : "mutationType",
1036 | "description" : "If this server supports mutation, the type that mutation operations will be rooted at.",
1037 | "args" : [ ],
1038 | "type" : {
1039 | "kind" : "OBJECT",
1040 | "name" : "__Type",
1041 | "ofType" : null
1042 | },
1043 | "isDeprecated" : false,
1044 | "deprecationReason" : null
1045 | }, {
1046 | "name" : "directives",
1047 | "description" : "'A list of all directives supported by this server.",
1048 | "args" : [ ],
1049 | "type" : {
1050 | "kind" : "NON_NULL",
1051 | "name" : null,
1052 | "ofType" : {
1053 | "kind" : "LIST",
1054 | "name" : null,
1055 | "ofType" : {
1056 | "kind" : "NON_NULL",
1057 | "name" : null,
1058 | "ofType" : {
1059 | "kind" : "OBJECT",
1060 | "name" : "__Directive",
1061 | "ofType" : null
1062 | }
1063 | }
1064 | }
1065 | },
1066 | "isDeprecated" : false,
1067 | "deprecationReason" : null
1068 | }, {
1069 | "name" : "subscriptionType",
1070 | "description" : "'If this server support subscription, the type that subscription operations will be rooted at.",
1071 | "args" : [ ],
1072 | "type" : {
1073 | "kind" : "OBJECT",
1074 | "name" : "__Type",
1075 | "ofType" : null
1076 | },
1077 | "isDeprecated" : false,
1078 | "deprecationReason" : null
1079 | } ],
1080 | "inputFields" : null,
1081 | "interfaces" : [ ],
1082 | "enumValues" : null,
1083 | "possibleTypes" : null
1084 | }, {
1085 | "kind" : "OBJECT",
1086 | "name" : "__Type",
1087 | "description" : null,
1088 | "fields" : [ {
1089 | "name" : "kind",
1090 | "description" : null,
1091 | "args" : [ ],
1092 | "type" : {
1093 | "kind" : "NON_NULL",
1094 | "name" : null,
1095 | "ofType" : {
1096 | "kind" : "ENUM",
1097 | "name" : "__TypeKind",
1098 | "ofType" : null
1099 | }
1100 | },
1101 | "isDeprecated" : false,
1102 | "deprecationReason" : null
1103 | }, {
1104 | "name" : "name",
1105 | "description" : null,
1106 | "args" : [ ],
1107 | "type" : {
1108 | "kind" : "SCALAR",
1109 | "name" : "String",
1110 | "ofType" : null
1111 | },
1112 | "isDeprecated" : false,
1113 | "deprecationReason" : null
1114 | }, {
1115 | "name" : "description",
1116 | "description" : null,
1117 | "args" : [ ],
1118 | "type" : {
1119 | "kind" : "SCALAR",
1120 | "name" : "String",
1121 | "ofType" : null
1122 | },
1123 | "isDeprecated" : false,
1124 | "deprecationReason" : null
1125 | }, {
1126 | "name" : "fields",
1127 | "description" : null,
1128 | "args" : [ {
1129 | "name" : "includeDeprecated",
1130 | "description" : null,
1131 | "type" : {
1132 | "kind" : "SCALAR",
1133 | "name" : "Boolean",
1134 | "ofType" : null
1135 | },
1136 | "defaultValue" : "false"
1137 | } ],
1138 | "type" : {
1139 | "kind" : "LIST",
1140 | "name" : null,
1141 | "ofType" : {
1142 | "kind" : "NON_NULL",
1143 | "name" : null,
1144 | "ofType" : {
1145 | "kind" : "OBJECT",
1146 | "name" : "__Field",
1147 | "ofType" : null
1148 | }
1149 | }
1150 | },
1151 | "isDeprecated" : false,
1152 | "deprecationReason" : null
1153 | }, {
1154 | "name" : "interfaces",
1155 | "description" : null,
1156 | "args" : [ ],
1157 | "type" : {
1158 | "kind" : "LIST",
1159 | "name" : null,
1160 | "ofType" : {
1161 | "kind" : "NON_NULL",
1162 | "name" : null,
1163 | "ofType" : {
1164 | "kind" : "OBJECT",
1165 | "name" : "__Type",
1166 | "ofType" : null
1167 | }
1168 | }
1169 | },
1170 | "isDeprecated" : false,
1171 | "deprecationReason" : null
1172 | }, {
1173 | "name" : "possibleTypes",
1174 | "description" : null,
1175 | "args" : [ ],
1176 | "type" : {
1177 | "kind" : "LIST",
1178 | "name" : null,
1179 | "ofType" : {
1180 | "kind" : "NON_NULL",
1181 | "name" : null,
1182 | "ofType" : {
1183 | "kind" : "OBJECT",
1184 | "name" : "__Type",
1185 | "ofType" : null
1186 | }
1187 | }
1188 | },
1189 | "isDeprecated" : false,
1190 | "deprecationReason" : null
1191 | }, {
1192 | "name" : "enumValues",
1193 | "description" : null,
1194 | "args" : [ {
1195 | "name" : "includeDeprecated",
1196 | "description" : null,
1197 | "type" : {
1198 | "kind" : "SCALAR",
1199 | "name" : "Boolean",
1200 | "ofType" : null
1201 | },
1202 | "defaultValue" : "false"
1203 | } ],
1204 | "type" : {
1205 | "kind" : "LIST",
1206 | "name" : null,
1207 | "ofType" : {
1208 | "kind" : "NON_NULL",
1209 | "name" : null,
1210 | "ofType" : {
1211 | "kind" : "OBJECT",
1212 | "name" : "__EnumValue",
1213 | "ofType" : null
1214 | }
1215 | }
1216 | },
1217 | "isDeprecated" : false,
1218 | "deprecationReason" : null
1219 | }, {
1220 | "name" : "inputFields",
1221 | "description" : null,
1222 | "args" : [ ],
1223 | "type" : {
1224 | "kind" : "LIST",
1225 | "name" : null,
1226 | "ofType" : {
1227 | "kind" : "NON_NULL",
1228 | "name" : null,
1229 | "ofType" : {
1230 | "kind" : "OBJECT",
1231 | "name" : "__InputValue",
1232 | "ofType" : null
1233 | }
1234 | }
1235 | },
1236 | "isDeprecated" : false,
1237 | "deprecationReason" : null
1238 | }, {
1239 | "name" : "ofType",
1240 | "description" : null,
1241 | "args" : [ ],
1242 | "type" : {
1243 | "kind" : "OBJECT",
1244 | "name" : "__Type",
1245 | "ofType" : null
1246 | },
1247 | "isDeprecated" : false,
1248 | "deprecationReason" : null
1249 | } ],
1250 | "inputFields" : null,
1251 | "interfaces" : [ ],
1252 | "enumValues" : null,
1253 | "possibleTypes" : null
1254 | }, {
1255 | "kind" : "ENUM",
1256 | "name" : "__TypeKind",
1257 | "description" : "An enum describing what kind of type a given __Type is",
1258 | "fields" : null,
1259 | "inputFields" : null,
1260 | "interfaces" : null,
1261 | "enumValues" : [ {
1262 | "name" : "SCALAR",
1263 | "description" : "Indicates this type is a scalar.",
1264 | "isDeprecated" : false,
1265 | "deprecationReason" : null
1266 | }, {
1267 | "name" : "OBJECT",
1268 | "description" : "Indicates this type is an object. `fields` and `interfaces` are valid fields.",
1269 | "isDeprecated" : false,
1270 | "deprecationReason" : null
1271 | }, {
1272 | "name" : "INTERFACE",
1273 | "description" : "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.",
1274 | "isDeprecated" : false,
1275 | "deprecationReason" : null
1276 | }, {
1277 | "name" : "UNION",
1278 | "description" : "Indicates this type is a union. `possibleTypes` is a valid field.",
1279 | "isDeprecated" : false,
1280 | "deprecationReason" : null
1281 | }, {
1282 | "name" : "ENUM",
1283 | "description" : "Indicates this type is an enum. `enumValues` is a valid field.",
1284 | "isDeprecated" : false,
1285 | "deprecationReason" : null
1286 | }, {
1287 | "name" : "INPUT_OBJECT",
1288 | "description" : "Indicates this type is an input object. `inputFields` is a valid field.",
1289 | "isDeprecated" : false,
1290 | "deprecationReason" : null
1291 | }, {
1292 | "name" : "LIST",
1293 | "description" : "Indicates this type is a list. `ofType` is a valid field.",
1294 | "isDeprecated" : false,
1295 | "deprecationReason" : null
1296 | }, {
1297 | "name" : "NON_NULL",
1298 | "description" : "Indicates this type is a non-null. `ofType` is a valid field.",
1299 | "isDeprecated" : false,
1300 | "deprecationReason" : null
1301 | } ],
1302 | "possibleTypes" : null
1303 | }, {
1304 | "kind" : "OBJECT",
1305 | "name" : "__Field",
1306 | "description" : null,
1307 | "fields" : [ {
1308 | "name" : "name",
1309 | "description" : null,
1310 | "args" : [ ],
1311 | "type" : {
1312 | "kind" : "NON_NULL",
1313 | "name" : null,
1314 | "ofType" : {
1315 | "kind" : "SCALAR",
1316 | "name" : "String",
1317 | "ofType" : null
1318 | }
1319 | },
1320 | "isDeprecated" : false,
1321 | "deprecationReason" : null
1322 | }, {
1323 | "name" : "description",
1324 | "description" : null,
1325 | "args" : [ ],
1326 | "type" : {
1327 | "kind" : "SCALAR",
1328 | "name" : "String",
1329 | "ofType" : null
1330 | },
1331 | "isDeprecated" : false,
1332 | "deprecationReason" : null
1333 | }, {
1334 | "name" : "args",
1335 | "description" : null,
1336 | "args" : [ ],
1337 | "type" : {
1338 | "kind" : "NON_NULL",
1339 | "name" : null,
1340 | "ofType" : {
1341 | "kind" : "LIST",
1342 | "name" : null,
1343 | "ofType" : {
1344 | "kind" : "NON_NULL",
1345 | "name" : null,
1346 | "ofType" : {
1347 | "kind" : "OBJECT",
1348 | "name" : "__InputValue",
1349 | "ofType" : null
1350 | }
1351 | }
1352 | }
1353 | },
1354 | "isDeprecated" : false,
1355 | "deprecationReason" : null
1356 | }, {
1357 | "name" : "type",
1358 | "description" : null,
1359 | "args" : [ ],
1360 | "type" : {
1361 | "kind" : "NON_NULL",
1362 | "name" : null,
1363 | "ofType" : {
1364 | "kind" : "OBJECT",
1365 | "name" : "__Type",
1366 | "ofType" : null
1367 | }
1368 | },
1369 | "isDeprecated" : false,
1370 | "deprecationReason" : null
1371 | }, {
1372 | "name" : "isDeprecated",
1373 | "description" : null,
1374 | "args" : [ ],
1375 | "type" : {
1376 | "kind" : "NON_NULL",
1377 | "name" : null,
1378 | "ofType" : {
1379 | "kind" : "SCALAR",
1380 | "name" : "Boolean",
1381 | "ofType" : null
1382 | }
1383 | },
1384 | "isDeprecated" : false,
1385 | "deprecationReason" : null
1386 | }, {
1387 | "name" : "deprecationReason",
1388 | "description" : null,
1389 | "args" : [ ],
1390 | "type" : {
1391 | "kind" : "SCALAR",
1392 | "name" : "String",
1393 | "ofType" : null
1394 | },
1395 | "isDeprecated" : false,
1396 | "deprecationReason" : null
1397 | } ],
1398 | "inputFields" : null,
1399 | "interfaces" : [ ],
1400 | "enumValues" : null,
1401 | "possibleTypes" : null
1402 | }, {
1403 | "kind" : "OBJECT",
1404 | "name" : "__InputValue",
1405 | "description" : null,
1406 | "fields" : [ {
1407 | "name" : "name",
1408 | "description" : null,
1409 | "args" : [ ],
1410 | "type" : {
1411 | "kind" : "NON_NULL",
1412 | "name" : null,
1413 | "ofType" : {
1414 | "kind" : "SCALAR",
1415 | "name" : "String",
1416 | "ofType" : null
1417 | }
1418 | },
1419 | "isDeprecated" : false,
1420 | "deprecationReason" : null
1421 | }, {
1422 | "name" : "description",
1423 | "description" : null,
1424 | "args" : [ ],
1425 | "type" : {
1426 | "kind" : "SCALAR",
1427 | "name" : "String",
1428 | "ofType" : null
1429 | },
1430 | "isDeprecated" : false,
1431 | "deprecationReason" : null
1432 | }, {
1433 | "name" : "type",
1434 | "description" : null,
1435 | "args" : [ ],
1436 | "type" : {
1437 | "kind" : "NON_NULL",
1438 | "name" : null,
1439 | "ofType" : {
1440 | "kind" : "OBJECT",
1441 | "name" : "__Type",
1442 | "ofType" : null
1443 | }
1444 | },
1445 | "isDeprecated" : false,
1446 | "deprecationReason" : null
1447 | }, {
1448 | "name" : "defaultValue",
1449 | "description" : null,
1450 | "args" : [ ],
1451 | "type" : {
1452 | "kind" : "SCALAR",
1453 | "name" : "String",
1454 | "ofType" : null
1455 | },
1456 | "isDeprecated" : false,
1457 | "deprecationReason" : null
1458 | } ],
1459 | "inputFields" : null,
1460 | "interfaces" : [ ],
1461 | "enumValues" : null,
1462 | "possibleTypes" : null
1463 | }, {
1464 | "kind" : "OBJECT",
1465 | "name" : "__EnumValue",
1466 | "description" : null,
1467 | "fields" : [ {
1468 | "name" : "name",
1469 | "description" : null,
1470 | "args" : [ ],
1471 | "type" : {
1472 | "kind" : "NON_NULL",
1473 | "name" : null,
1474 | "ofType" : {
1475 | "kind" : "SCALAR",
1476 | "name" : "String",
1477 | "ofType" : null
1478 | }
1479 | },
1480 | "isDeprecated" : false,
1481 | "deprecationReason" : null
1482 | }, {
1483 | "name" : "description",
1484 | "description" : null,
1485 | "args" : [ ],
1486 | "type" : {
1487 | "kind" : "SCALAR",
1488 | "name" : "String",
1489 | "ofType" : null
1490 | },
1491 | "isDeprecated" : false,
1492 | "deprecationReason" : null
1493 | }, {
1494 | "name" : "isDeprecated",
1495 | "description" : null,
1496 | "args" : [ ],
1497 | "type" : {
1498 | "kind" : "NON_NULL",
1499 | "name" : null,
1500 | "ofType" : {
1501 | "kind" : "SCALAR",
1502 | "name" : "Boolean",
1503 | "ofType" : null
1504 | }
1505 | },
1506 | "isDeprecated" : false,
1507 | "deprecationReason" : null
1508 | }, {
1509 | "name" : "deprecationReason",
1510 | "description" : null,
1511 | "args" : [ ],
1512 | "type" : {
1513 | "kind" : "SCALAR",
1514 | "name" : "String",
1515 | "ofType" : null
1516 | },
1517 | "isDeprecated" : false,
1518 | "deprecationReason" : null
1519 | } ],
1520 | "inputFields" : null,
1521 | "interfaces" : [ ],
1522 | "enumValues" : null,
1523 | "possibleTypes" : null
1524 | }, {
1525 | "kind" : "OBJECT",
1526 | "name" : "__Directive",
1527 | "description" : null,
1528 | "fields" : [ {
1529 | "name" : "name",
1530 | "description" : null,
1531 | "args" : [ ],
1532 | "type" : {
1533 | "kind" : "SCALAR",
1534 | "name" : "String",
1535 | "ofType" : null
1536 | },
1537 | "isDeprecated" : false,
1538 | "deprecationReason" : null
1539 | }, {
1540 | "name" : "description",
1541 | "description" : null,
1542 | "args" : [ ],
1543 | "type" : {
1544 | "kind" : "SCALAR",
1545 | "name" : "String",
1546 | "ofType" : null
1547 | },
1548 | "isDeprecated" : false,
1549 | "deprecationReason" : null
1550 | }, {
1551 | "name" : "locations",
1552 | "description" : null,
1553 | "args" : [ ],
1554 | "type" : {
1555 | "kind" : "LIST",
1556 | "name" : null,
1557 | "ofType" : {
1558 | "kind" : "NON_NULL",
1559 | "name" : null,
1560 | "ofType" : {
1561 | "kind" : "ENUM",
1562 | "name" : "__DirectiveLocation",
1563 | "ofType" : null
1564 | }
1565 | }
1566 | },
1567 | "isDeprecated" : false,
1568 | "deprecationReason" : null
1569 | }, {
1570 | "name" : "args",
1571 | "description" : null,
1572 | "args" : [ ],
1573 | "type" : {
1574 | "kind" : "NON_NULL",
1575 | "name" : null,
1576 | "ofType" : {
1577 | "kind" : "LIST",
1578 | "name" : null,
1579 | "ofType" : {
1580 | "kind" : "NON_NULL",
1581 | "name" : null,
1582 | "ofType" : {
1583 | "kind" : "OBJECT",
1584 | "name" : "__InputValue",
1585 | "ofType" : null
1586 | }
1587 | }
1588 | }
1589 | },
1590 | "isDeprecated" : false,
1591 | "deprecationReason" : null
1592 | }, {
1593 | "name" : "onOperation",
1594 | "description" : null,
1595 | "args" : [ ],
1596 | "type" : {
1597 | "kind" : "SCALAR",
1598 | "name" : "Boolean",
1599 | "ofType" : null
1600 | },
1601 | "isDeprecated" : true,
1602 | "deprecationReason" : "Use `locations`."
1603 | }, {
1604 | "name" : "onFragment",
1605 | "description" : null,
1606 | "args" : [ ],
1607 | "type" : {
1608 | "kind" : "SCALAR",
1609 | "name" : "Boolean",
1610 | "ofType" : null
1611 | },
1612 | "isDeprecated" : true,
1613 | "deprecationReason" : "Use `locations`."
1614 | }, {
1615 | "name" : "onField",
1616 | "description" : null,
1617 | "args" : [ ],
1618 | "type" : {
1619 | "kind" : "SCALAR",
1620 | "name" : "Boolean",
1621 | "ofType" : null
1622 | },
1623 | "isDeprecated" : true,
1624 | "deprecationReason" : "Use `locations`."
1625 | } ],
1626 | "inputFields" : null,
1627 | "interfaces" : [ ],
1628 | "enumValues" : null,
1629 | "possibleTypes" : null
1630 | }, {
1631 | "kind" : "ENUM",
1632 | "name" : "__DirectiveLocation",
1633 | "description" : "An enum describing valid locations where a directive can be placed",
1634 | "fields" : null,
1635 | "inputFields" : null,
1636 | "interfaces" : null,
1637 | "enumValues" : [ {
1638 | "name" : "QUERY",
1639 | "description" : "Indicates the directive is valid on queries.",
1640 | "isDeprecated" : false,
1641 | "deprecationReason" : null
1642 | }, {
1643 | "name" : "MUTATION",
1644 | "description" : "Indicates the directive is valid on mutations.",
1645 | "isDeprecated" : false,
1646 | "deprecationReason" : null
1647 | }, {
1648 | "name" : "FIELD",
1649 | "description" : "Indicates the directive is valid on fields.",
1650 | "isDeprecated" : false,
1651 | "deprecationReason" : null
1652 | }, {
1653 | "name" : "FRAGMENT_DEFINITION",
1654 | "description" : "Indicates the directive is valid on fragment definitions.",
1655 | "isDeprecated" : false,
1656 | "deprecationReason" : null
1657 | }, {
1658 | "name" : "FRAGMENT_SPREAD",
1659 | "description" : "Indicates the directive is valid on fragment spreads.",
1660 | "isDeprecated" : false,
1661 | "deprecationReason" : null
1662 | }, {
1663 | "name" : "INLINE_FRAGMENT",
1664 | "description" : "Indicates the directive is valid on inline fragments.",
1665 | "isDeprecated" : false,
1666 | "deprecationReason" : null
1667 | }, {
1668 | "name" : "SCHEMA",
1669 | "description" : "Indicates the directive is valid on a schema SDL definition.",
1670 | "isDeprecated" : false,
1671 | "deprecationReason" : null
1672 | }, {
1673 | "name" : "SCALAR",
1674 | "description" : "Indicates the directive is valid on a scalar SDL definition.",
1675 | "isDeprecated" : false,
1676 | "deprecationReason" : null
1677 | }, {
1678 | "name" : "OBJECT",
1679 | "description" : "Indicates the directive is valid on an object SDL definition.",
1680 | "isDeprecated" : false,
1681 | "deprecationReason" : null
1682 | }, {
1683 | "name" : "FIELD_DEFINITION",
1684 | "description" : "Indicates the directive is valid on a field SDL definition.",
1685 | "isDeprecated" : false,
1686 | "deprecationReason" : null
1687 | }, {
1688 | "name" : "ARGUMENT_DEFINITION",
1689 | "description" : "Indicates the directive is valid on a field argument SDL definition.",
1690 | "isDeprecated" : false,
1691 | "deprecationReason" : null
1692 | }, {
1693 | "name" : "INTERFACE",
1694 | "description" : "Indicates the directive is valid on an interface SDL definition.",
1695 | "isDeprecated" : false,
1696 | "deprecationReason" : null
1697 | }, {
1698 | "name" : "UNION",
1699 | "description" : "Indicates the directive is valid on an union SDL definition.",
1700 | "isDeprecated" : false,
1701 | "deprecationReason" : null
1702 | }, {
1703 | "name" : "ENUM",
1704 | "description" : "Indicates the directive is valid on an enum SDL definition.",
1705 | "isDeprecated" : false,
1706 | "deprecationReason" : null
1707 | }, {
1708 | "name" : "ENUM_VALUE",
1709 | "description" : "Indicates the directive is valid on an enum value SDL definition.",
1710 | "isDeprecated" : false,
1711 | "deprecationReason" : null
1712 | }, {
1713 | "name" : "INPUT_OBJECT",
1714 | "description" : "Indicates the directive is valid on an input object SDL definition.",
1715 | "isDeprecated" : false,
1716 | "deprecationReason" : null
1717 | }, {
1718 | "name" : "INPUT_FIELD_DEFINITION",
1719 | "description" : "Indicates the directive is valid on an input object field SDL definition.",
1720 | "isDeprecated" : false,
1721 | "deprecationReason" : null
1722 | } ],
1723 | "possibleTypes" : null
1724 | } ],
1725 | "directives" : [ {
1726 | "name" : "include",
1727 | "description" : "Directs the executor to include this field or fragment only when the `if` argument is true",
1728 | "locations" : [ "FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT" ],
1729 | "args" : [ {
1730 | "name" : "if",
1731 | "description" : "Included when true.",
1732 | "type" : {
1733 | "kind" : "NON_NULL",
1734 | "name" : null,
1735 | "ofType" : {
1736 | "kind" : "SCALAR",
1737 | "name" : "Boolean",
1738 | "ofType" : null
1739 | }
1740 | },
1741 | "defaultValue" : null
1742 | } ],
1743 | "onOperation" : false,
1744 | "onFragment" : true,
1745 | "onField" : true
1746 | }, {
1747 | "name" : "skip",
1748 | "description" : "Directs the executor to skip this field or fragment when the `if`'argument is true.",
1749 | "locations" : [ "FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT" ],
1750 | "args" : [ {
1751 | "name" : "if",
1752 | "description" : "Skipped when true.",
1753 | "type" : {
1754 | "kind" : "NON_NULL",
1755 | "name" : null,
1756 | "ofType" : {
1757 | "kind" : "SCALAR",
1758 | "name" : "Boolean",
1759 | "ofType" : null
1760 | }
1761 | },
1762 | "defaultValue" : null
1763 | } ],
1764 | "onOperation" : false,
1765 | "onFragment" : true,
1766 | "onField" : true
1767 | }, {
1768 | "name" : "defer",
1769 | "description" : "This directive allows results to be deferred during execution",
1770 | "locations" : [ "FIELD" ],
1771 | "args" : [ ],
1772 | "onOperation" : false,
1773 | "onFragment" : false,
1774 | "onField" : true
1775 | }, {
1776 | "name" : "aws_subscribe",
1777 | "description" : "Tells the service which mutation triggers this subscription.",
1778 | "locations" : [ "FIELD_DEFINITION" ],
1779 | "args" : [ {
1780 | "name" : "mutations",
1781 | "description" : "List of mutations which will trigger this subscription when they are called.",
1782 | "type" : {
1783 | "kind" : "LIST",
1784 | "name" : null,
1785 | "ofType" : {
1786 | "kind" : "SCALAR",
1787 | "name" : "String",
1788 | "ofType" : null
1789 | }
1790 | },
1791 | "defaultValue" : null
1792 | } ],
1793 | "onOperation" : false,
1794 | "onFragment" : false,
1795 | "onField" : false
1796 | }, {
1797 | "name" : "aws_auth",
1798 | "description" : "Directs the schema to enforce authorization on a field",
1799 | "locations" : [ "FIELD_DEFINITION" ],
1800 | "args" : [ {
1801 | "name" : "cognito_groups",
1802 | "description" : "List of cognito user pool groups which have access on this field",
1803 | "type" : {
1804 | "kind" : "LIST",
1805 | "name" : null,
1806 | "ofType" : {
1807 | "kind" : "SCALAR",
1808 | "name" : "String",
1809 | "ofType" : null
1810 | }
1811 | },
1812 | "defaultValue" : null
1813 | } ],
1814 | "onOperation" : false,
1815 | "onFragment" : false,
1816 | "onField" : false
1817 | }, {
1818 | "name" : "aws_publish",
1819 | "description" : "Tells the service which subscriptions will be published to when this mutation is called. This directive is deprecated use @aws_susbscribe directive instead.",
1820 | "locations" : [ "FIELD_DEFINITION" ],
1821 | "args" : [ {
1822 | "name" : "subscriptions",
1823 | "description" : "List of subscriptions which will be published to when this mutation is called.",
1824 | "type" : {
1825 | "kind" : "LIST",
1826 | "name" : null,
1827 | "ofType" : {
1828 | "kind" : "SCALAR",
1829 | "name" : "String",
1830 | "ofType" : null
1831 | }
1832 | },
1833 | "defaultValue" : null
1834 | } ],
1835 | "onOperation" : false,
1836 | "onFragment" : false,
1837 | "onField" : false
1838 | }, {
1839 | "name" : "deprecated",
1840 | "description" : null,
1841 | "locations" : [ "FIELD_DEFINITION", "ENUM_VALUE" ],
1842 | "args" : [ {
1843 | "name" : "reason",
1844 | "description" : null,
1845 | "type" : {
1846 | "kind" : "SCALAR",
1847 | "name" : "String",
1848 | "ofType" : null
1849 | },
1850 | "defaultValue" : "\"No longer supported\""
1851 | } ],
1852 | "onOperation" : false,
1853 | "onFragment" : false,
1854 | "onField" : false
1855 | } ]
1856 | }
1857 | }
1858 | }
--------------------------------------------------------------------------------
/src/graphql/subscriptions.js:
--------------------------------------------------------------------------------
1 | // eslint-disable
2 | // this is an auto generated file. This will be overwritten
3 |
4 | export const onCreateNote = `subscription OnCreateNote {
5 | onCreateNote {
6 | id
7 | name
8 | description
9 | status
10 | }
11 | }
12 | `;
13 | export const onUpdateNote = `subscription OnUpdateNote {
14 | onUpdateNote {
15 | id
16 | name
17 | description
18 | status
19 | }
20 | }
21 | `;
22 | export const onDeleteNote = `subscription OnDeleteNote {
23 | onDeleteNote {
24 | id
25 | name
26 | description
27 | status
28 | }
29 | }
30 | `;
31 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/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 * as serviceWorker from './serviceWorker';
6 |
7 | import Amplify from 'aws-amplify'
8 | import config from './aws-exports'
9 | Amplify.configure(config)
10 |
11 | ReactDOM.render( , document.getElementById('root'));
12 |
13 | // If you want your app to work offline and load faster, you can change
14 | // unregister() to register() below. Note this comes with some pitfalls.
15 | // Learn more about service workers: http://bit.ly/CRA-PWA
16 | serviceWorker.unregister();
17 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read http://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit http://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------