Skip to content

Commit

Permalink
cut over to use GraphQL API instead of a local data store
Browse files Browse the repository at this point in the history
  • Loading branch information
kesslerpillar committed May 26, 2021
1 parent baed317 commit f6ee8a2
Show file tree
Hide file tree
Showing 18 changed files with 2,544 additions and 61 deletions.
12 changes: 12 additions & 0 deletions .graphqlconfig.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
projects:
tddamplifyreact:
schemaPath: src/graphql/schema.json
includes:
- src/graphql/**/*.js
excludes:
- ./amplify/**
extensions:
amplify:
codeGenTarget: javascript
generatedFileName: ''
docsFilePath: src/graphql
11 changes: 11 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"files.exclude": {
"amplify/.config": true,
"amplify/**/*-parameters.json": true,
"amplify/**/amplify.state": true,
"amplify/**/transform.conf.json": true,
"amplify/#current-cloud-backend": true,
"amplify/backend/amplify-meta.json": true,
"amplify/backend/awscloudformation": true
}
}
106 changes: 102 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1417,10 +1417,8 @@ How do you want users to be able to sign in? Username
Do you want to configure advanced settings? No, I am done.
```

- Run `amplify push`
```
Are you sure you want to continue? (Y/n): Y
```
- Run `amplify push --y`

- This command created the following resources on AWS
- UpdateRolesWithIDPFunctionRole AWS::IAM::Role
- SNSRole AWS::IAM::Role
Expand Down Expand Up @@ -1599,4 +1597,104 @@ export default Footer;

[Code for this section](https://github.com/pairing4good/tdd-amplify-react/commit/22f23e1bc263d175dc699450e136a58e341b8fa2)

</details>

<details>
<summary>Log Out</summary>

## Backend API
Now that we have user authentication hooked up we need to add the ability for customer to get their "notes to show up on their mobile phone browser too". This means that we can not use local storage on the user's computer anymore. Instead we need to build backend [API](https://en.wikipedia.org/wiki/API) that will store notes independently from the frontend code.

- Run `amplify add api` at the root of your project
```
Please select from one of the below mentioned services: GraphQL
Provide API name: tddamplifyreact
Choose the default authorization type for the API API key
Enter a description for the API key: notes-api-key
After how many days from now the API key should expire (1-365): 7
Do you want to configure advanced settings for the GraphQL API No, I am done.
Do you have an annotated GraphQL schema? No
Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description)
Do you want to edit the schema now? Yes
```
- [GraphQL](https://graphql.org/) is an alternative to [REST](Representational state transfer). GraphQL APIs are more flexible than REST APIs.
- This command created
- `amplify/backend/api/`
- `amplify/backend/backend-config.json`

- Run `amplify push --y`

- This command created/updated the following resources on AWS
- authtddamplifyreact05a4d123 AWS::CloudFormation::Stack
- GraphQLAPI AWS::AppSync::GraphQLApi
- GraphQLAPIKey AWS::AppSync::ApiKey
- GraphQLSchema AWS::AppSync::GraphQLSchema
- NoteIAMRole AWS::IAM::Role
- NoteDataSource AWS::AppSync::DataSource
- ListNoteResolver AWS::AppSync::Resolver
- CreateNoteResolver AWS::AppSync::Resolver
- UpdateNoteResolver AWS::AppSync::Resolver
- DeleteNoteResolver AWS::AppSync::Resolver
- GetNoteResolver AWS::AppSync::Resolver
- NoteTable AWS::DynamoDB::Table
- amplify-tddamplifyreact-dev-121349-apitddamplifyreact-Z2AW8DQHJ787-Note-1FT5A8I4PYJH1 AWS::CloudFormation::Stack
- Note AWS::CloudFormation::Stack
- amplify-tddamplifyreact-dev-151647-apitddamplifyreact-Z2AW8DQHJ787-CustomResourcesjson-GB5TRK4AKZAU AWS::CloudFormation::Stack
- CustomResourcesjson AWS::CloudFormation::Stack
- amplify-tddamplifyreact-dev-151647-apitddamplifyreact-Z2AW8DQHJ787 AWS::CloudFormation::Stack
- apitddamplifyreact AWS::CloudFormation::Stack
- authtddamplifyreact03a1d234 AWS::CloudFormation::Stack
- amplify-tddamplifyreact-dev-121349 AWS::CloudFormation::Stack

### Cut Over Repository To Use GraphQL
Now that we have a GraphQL API that is storing our notes in a [DynamoDB](https://aws.amazon.com/dynamodb) table we can replace `localforage` calls with GraphQL API calls.

- Replace `localforage` calls in the `NoteRepository` with GraphQL API calls
```js
import { API } from 'aws-amplify';
import { listNotes } from './graphql/queries';
import { createNote as createNoteMutation} from './graphql/mutations';

export async function findAll(){
const apiData = await API.graphql({ query: listNotes });
return apiData.data.listNotes.items;
};

export async function save(note){
const apiData = await API.graphql({ query: createNoteMutation, variables: { input: note } });
return apiData.data.createNote;
}
```

- We do need to call save first in the `createNote` callback function in the `App` component because when GraphQL saves a note it generates a unique `ID` that we want to have access to in our `note` array.
```js
async function createNote() {
const newNote = await save(formData);
const updatedNoteList = [ ...notes, newNote ];
setNotes(updatedNoteList);
}
```

- The final place that we need to remove `localforage` is in the `note.spec.js` Cypress test. GraphQL does not provide an equivalent API endpoint to delete all of the notes so we will not be able to simply replace the `localforage.clear()` function call with a GraphQL one. In a separate commit we will added the ability to delete notes by `ID` through the UI. This is a [mutation](https://graphql.org/learn/queries/#mutations) that GraphQL provides. But for now we will just remove the clean up in the Cypress test.
```js
describe('Note Capture', () => {
before(() => {
cy.signIn();
});

after(() => {
cy.clearLocalStorageSnapshot();
cy.clearLocalStorage();
});
...
```
- Finally remove `localforage` by running `npm uninstall localforage`
- Rerun all of the tests
- Green!
- Commit
[Code for this section]()
</details>
5 changes: 5 additions & 0 deletions amplify/backend/api/tddamplifyreact/parameters.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"AppSyncApiName": "tddamplifyreact",
"DynamoDBBillingMode": "PAY_PER_REQUEST",
"DynamoDBEnableServerSideEncryption": false
}
2 changes: 2 additions & 0 deletions amplify/backend/api/tddamplifyreact/resolvers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Any resolvers that you add in this directory will override the ones automatically generated by Amplify CLI and will be directly copied to the cloud.
For more information, visit [https://docs.amplify.aws/cli/graphql-transformer/resolvers](https://docs.amplify.aws/cli/graphql-transformer/resolvers)
5 changes: 5 additions & 0 deletions amplify/backend/api/tddamplifyreact/schema.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type Note @model {
id: ID!
name: String!
description: String
}
58 changes: 58 additions & 0 deletions amplify/backend/api/tddamplifyreact/stacks/CustomResources.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "An auto-generated nested stack.",
"Metadata": {},
"Parameters": {
"AppSyncApiId": {
"Type": "String",
"Description": "The id of the AppSync API associated with this project."
},
"AppSyncApiName": {
"Type": "String",
"Description": "The name of the AppSync API",
"Default": "AppSyncSimpleTransform"
},
"env": {
"Type": "String",
"Description": "The environment name. e.g. Dev, Test, or Production",
"Default": "NONE"
},
"S3DeploymentBucket": {
"Type": "String",
"Description": "The S3 bucket containing all deployment assets for the project."
},
"S3DeploymentRootKey": {
"Type": "String",
"Description": "An S3 key relative to the S3DeploymentBucket that points to the root\nof the deployment directory."
}
},
"Resources": {
"EmptyResource": {
"Type": "Custom::EmptyResource",
"Condition": "AlwaysFalse"
}
},
"Conditions": {
"HasEnvironmentParameter": {
"Fn::Not": [
{
"Fn::Equals": [
{
"Ref": "env"
},
"NONE"
]
}
]
},
"AlwaysFalse": {
"Fn::Equals": ["true", "false"]
}
},
"Outputs": {
"EmptyOutput": {
"Description": "An empty output. You may delete this if you have at least one resource above.",
"Value": ""
}
}
}
4 changes: 4 additions & 0 deletions amplify/backend/api/tddamplifyreact/transform.conf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"Version": 5,
"ElasticsearchWarning": true
}
18 changes: 18 additions & 0 deletions amplify/backend/backend-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,23 @@
"dependsOn": [],
"customAuth": false
}
},
"api": {
"tddamplifyreact": {
"service": "AppSync",
"providerPlugin": "awscloudformation",
"output": {
"authConfig": {
"defaultAuthentication": {
"authenticationType": "API_KEY",
"apiKeyConfig": {
"apiKeyExpirationDays": 7,
"description": "notes-api-key"
}
},
"additionalAuthenticationProviders": []
}
}
}
}
}
3 changes: 0 additions & 3 deletions cypress/integration/note.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import localForage from "localforage";

describe('Note Capture', () => {
before(() => {
cy.signIn();
Expand All @@ -8,7 +6,6 @@ describe('Note Capture', () => {
after(() => {
cy.clearLocalStorageSnapshot();
cy.clearLocalStorage();
localForage.clear();
});

beforeEach(() => {
Expand Down
43 changes: 0 additions & 43 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"@testing-library/user-event": "^12.8.3",
"aws-amplify": "^3.4.3",
"cypress-localstorage-commands": "^1.4.4",
"localforage": "^1.9.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
Expand Down
6 changes: 3 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ function App() {
}

async function createNote() {
const updatedNoteList = [ ...notes, formData ];
setNotes(updatedNoteList);
await save(formData);
const newNote = await save(formData);
const updatedNoteList = [ ...notes, newNote ];
setNotes(updatedNoteList);
}

return (
Expand Down
Loading

0 comments on commit f6ee8a2

Please sign in to comment.