Tag: Android

Deploy a VueJS app with the Amplify Console using AWS CloudFormation

Deploy a VueJS app with the Amplify Console using AWS CloudFormation

This article was written by Simon Thulbourn, Solutions Architect, AWS.

Developers and Operations people love automation. It gives them the power to introduce repeatability into their applications. The provisioning of infrastructure components is no different. Being able to create and manage resources through the use of AWS CloudFormation is a powerful way to run and rerun the same code to create resources in AWS across accounts.

Today, the Amplify Console launched support for AWS CloudFormation resources to give developers the ability to have reliable and repeatable access to the Amplify Console service. The Amplify Console offers three new resources:

  • AWS::Amplify::App
  • AWS::Amplify::Branch
  • AWS::Amplify::Domain

For newcomers, AWS Amplify Console provides a Git-based workflow to enable developers to build, deploy and host web application, whether it’s  Angular, ReactJS, VueJS or something else. These web applications can then consume APIs based on GraphQL or Serverless technologies, enabling fullstack serverless applications on AWS.

Working with CloudFormation

As an example, you’ll deploy the Todo example app from VueJS using Amplify Console and the new CloudFormation resources.

deploy the Todo example app from VueJS

You’ll start by forking the Vue repository on GitHub to your account. You have to fork the repository since Amplify Console will want to add a webhook and clone the repository for future builds.

You’ll also create a new personal access token on GitHub since you’ll need one to embed in the CloudFormation. You can read more about creating personal access tokens on GitHub’s website. The token will need the “repo” OAuth scope.

Note: Personal access tokens should be treated as a secret.

You can deploy the Todo application using the CloudFormation template at the end of this blog post. This CloudFormation template will create an Amplify Console App, Branch & Domain with a TLS certificate and an IAM role. To deploy the CloudFormation template, you can either use the AWS Console or the AWS CLI. In this example, we’re using the AWS CLI:

aws cloudformation deploy \
  --template-file ./template.yaml \
  --capabilities CAPABILITY_IAM \
  --parameter-overrides \
      OAuthToken=<GITHUB PERSONAL ACCESS TOKEN> \
      Repository=https://github.com/sthulb/vue \
      Domain=example.com \
  --stack-name TodoApp

After deploying the CloudFormation template, you need to go into the Amplify Console and trigger a build. The CloudFormation template can provision the resources, but can’t trigger a build since it creates resources but cannot trigger actions.

Diving deeper Into the template

The CloudFormation template needs to be updated to add your forked GitHub URL, and the Oauth token created above, and a custom domain you own. The AmplifyApp resource is your project definition – it is a collection of all the branches (or the AmplifyBranch resource) in your repository. The BuildSpec describes the settings used to build and deploy the branches in your app. In this example, we are deploying an example Todo app, which consists of four files. The Todo app expects a vue.min.js file to be available at: https://a1b2c3.amplifyapp.com/dist/vue.min.js; as a part of the buildspec we made sure the vue.min.js was in the deployment artifact, but not in the right location. We used the CustomRules property to rewrite the URL, transforming the URL from https://a1b2c3.amplifyapp.com/vue.min.js to https://a1b2c3.amplifyapp.com/dist/vue.min.js.

The AmplifyDomain resource allows you to connect your domain (https://yourdomain.com) or a subdomain (https://foo.yourdomain.com) so end users can start visiting your site.

Template

AWSTemplateFormatVersion: 2010-09-09

Parameters:
  Repository:
    Type: String
    Description: GitHub Repository URL

  OauthToken:
    Type: String
    Description: GitHub Repository URL
    NoEcho: true

  Domain:
    Type: String
    Description: Domain name to host application

Resources:
  AmplifyRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - amplify.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: Amplify
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action: "amplify:*"
                Resource: "*"

  AmplifyApp:
    Type: "AWS::Amplify::App"
    Properties:
      Name: TodoApp
      Repository: !Ref Repository
      Description: VueJS Todo example app
      OauthToken: !Ref OauthToken
      EnableBranchAutoBuild: true
      BuildSpec: |-
        version: 0.1
        frontend:
          phases:
            build:
              commands:
                - cp dist/vue.min.js examples/todomvc/
          artifacts:
            baseDirectory: examples/todomvc/
            files:
              - '*'
      CustomRules:
        - Source: /dist/vue.min.js
          Target: /vue.min.js
          Status: '200'
      Tags:
        - Key: Name
          Value: Todo
      IAMServiceRole: !GetAtt AmplifyRole.Arn

  AmplifyBranch:
    Type: AWS::Amplify::Branch
    Properties:
      BranchName: master
      AppId: !GetAtt AmplifyApp.AppId
      Description: Master Branch
      EnableAutoBuild: true
      Tags:
        - Key: Name
          Value: todo-master
        - Key: Branch
          Value: master

  AmplifyDomain:
    Type: AWS::Amplify::Domain
    Properties:
      DomainName: !Ref Domain
      AppId: !GetAtt AmplifyApp.AppId
      SubDomainSettings:
        - Prefix: master
          BranchName: !GetAtt AmplifyBranch.BranchName

Outputs:
  DefaultDomain:
    Value: !GetAtt AmplifyApp.DefaultDomain

  MasterBranchUrl:
    Value: !Join [ ".", [ !GetAtt AmplifyBranch.BranchName, !GetAtt AmplifyDomain.DomainName ]]

Conclusion

To start using Amplify Console’s CloudFormation resources, visit the CloudFormation documentation page.

Acknowledgements

All of the code in the VueJS repository is licensed under the MIT license and property of Evan You and contributors.

 

from AWS Mobile Blog

Amplify Framework Adds Support for AWS Lambda Functions and Amazon DynamoDB Custom Indexes in GraphQL Schemas

Amplify Framework Adds Support for AWS Lambda Functions and Amazon DynamoDB Custom Indexes in GraphQL Schemas

Written by Kurt Kemple, Sr. Developer Advocate at AWS, Nikhil Dabhade, Sr. Product Manager at AWS, & Me!

The Amplify Framework is an open source project for building cloud-enabled mobile and web applications. Today, we’re happy to announce new features for the Function and API categories in the Amplify CLI.

It’s now possible to add an AWS Lambda function as a data source for your AWS AppSync API using the GraphQL transformer that is included in the Amplify CLI. You can also grant permissions for interacting with AWS resources from the Lambda function. This updates the associated IAM execution role policies without needing you to perform manual IAM policy updates.

The GraphQL transformer also includes a new @key directive that simplifies the syntax for creating custom indexes and performing advanced query operations with Amazon DynamoDB. This streamlines the process of configuring complex key structures to fit various access patterns when using DynamoDB as a data source.

Adding a Lambda function as a data source for your AWS AppSync API

The new @function directive in the GraphQL transform library provides an easy mechanism to call a Lambda function from a field in your AppSync API.  To connect a Lambda data source, add the @function directive to a field in your annotated GraphQL schema that’s managed by the Amplify CLI. You can also create and deploy the Lambda functions by using the Amplify CLI.

Let’s look at how you can use this feature.

What are we building?

In this blog post, we will create a React JavaScript application which uses a Lambda function as a data source for your GraphQL API. The Lambda function writes to storage which in this case will be Amazon DynamoDB. In addition, we will illustrate how you can easily grant create/read/update/delete permissions for interacting with AWS resources such as DynamoDB from a Lambda function.

Setting up the project

Pre-requisites

Download, install and configure the Amplify CLI.

$ npm install -g @aws-amplify/cli 
$ amplify configure

Next, create your project if you don’t already have one. We’re creating a React application here, but you can choose to create a project with any other Amplify-supported framework such as Angular, Vue or Ionic.

$ npx create-react-app my-project

Download, install and configure the Amplify CLI.

$ cd my-project
$ amplify init
$ npm i aws-amplify
$ cd my-project<br />$ amplify init<br />$ npm i aws-amplify

The ‘amplify init’ command initializes the project, sets up deployment resources in the cloud, and makes your project ready for Amplify.

Adding storage to your project

Next, we will setup the backend to add Storage using Amazon DynamoDB for your React JavaScript application.

$ amplify add storage
? Please select from one of the below mentioned services NoSQL Database
Welcome to the NoSQL DynamoDB database wizard
This wizard asks you a series of questions to help determine how to set up your NoSQL database table.

? Please provide a friendly name for your resource that will be used to label this category in the project: teststorage
? Please provide table name: teststorage
You can now add columns to the table.
? What would you like to name this column: id
? Please choose the data type: number
? Would you like to add another column? Yes
? What would you like to name this column: email
? Please choose the data type: string
? Would you like to add another column? Yes
? What would you like to name this column: createdAt
? Please choose the data type: string
? Would you like to add another column? No
Before you create the database, you must specify how items in your table are uniquely organized. You do this by specifying a primary key. The primary key uniquely identifies each item in the table so that no two items can have the same key.
This can be an individual column, or a combination that includes a primary key and a sort key.
To learn more about primary keys, see: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey
? Please choose partition key for the table: id
? Do you want to add a sort key to your table? No
You can optionally add global secondary indexes for this table. These are useful when you run queries defined in a different column than the primary key.
To learn more about indexes, see: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.SecondaryIndexes
? Do you want to add global secondary indexes to your table? No
Successfully added resource teststorage locally

Adding a function to your project

Next, we will add a Lambda function by using the Amplify CLI. We will also grant permissions for the Lambda function to be able to interact with DynamoDB table which we created in the previous step.

$ amplify add function
Using service: Lambda, provided by: awscloudformation
? Provide a friendly name for your resource to be used as a label for this category in the project: addEntry
? Provide the AWS Lambda function name: addEntry
? Choose the function template that you want to use: Hello world function
? Do you want to access other resources created in this project from your Lambda function? Yes
? Select the category storage
? Select the resources for storage category teststorage
? Select the operations you want to permit for teststorage create, read, update, delete

You can access the following resource attributes as environment variables from your Lambda function
var environment = process.env.ENV
var region = process.env.REGION
var storageTeststorageName = process.env.STORAGE_TESTSTORAGE_NAME
var storageTeststorageArn = process.env.STORAGE_TESTSTORAGE_ARN

? Do you want to edit the local lambda function now? Yes 

This will open the Hello world function template file ‘index.js’ in the editor you selected during the ‘amplify init’ step.

Auto populating environment variables for your Lambda function

The Amplify CLI adds the environment variables, representing the AWS resources that the Lambda interacts with, as comments to your index.js files at the top for ease of reference. In this case, the AWS resource is DynamoDB. We want to have the Lambda function add an entry to the DynamoDB table with the parameters we pass to the GraphQL API. Add the following code to the Lambda function which utilizes the environment variables representing the DynamoDB table and region:

/* Amplify Params - DO NOT EDIT
You can access the following resource attributes as environment variables from your Lambda function
var environment = process.env.ENV
var region = process.env.REGION
var storageTeststorageName = process.env.STORAGE_TESTSTORAGE_NAME
var storageTeststorageArn = process.env.STORAGE_TESTSTORAGE_ARN

Amplify Params - DO NOT EDIT */

var AWS = require('aws-sdk');
var region = process.env.REGION
var storageTeststorageName = process.env.STORAGE_TESTSTORAGE_NAME
AWS.config.update({region: region});
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
var ddb_table_name = storageTeststorageName
var ddb_primary_key = 'id';

function write(params, context){
    ddb.putItem(params, function(err, data) {
    if (err) {
      console.log("Error", err);
    } else {
      console.log("Success", data);
    }
  });
}
 

exports.handler = function (event, context) { //eslint-disable-line
  
  var params = {
    TableName: ddb_table_name,
    AWS.DynamoDB.Converter.input(event.arguments)
  };
  
  console.log('len: ' + Object.keys(event).length)
  if (Object.keys(event).length > 0) {
    write(params, context);
  } 
}; 

After you replace the function, jump back to the command line and press Enter to continue.

Next, run the ‘amplify push’ command to deploy your changes to the AWS cloud.

$ amplify push

Adding and updating the Lambda execution IAM role for Amplify managed resources

When you run the ‘amplify push’ command, the IAM execution role policies associated with the permissions you granted earlier are updated automatically to allow the Lambda function to interact with DynamoDB.

Setting up the API

After completing the function setup, the next step is to add a GraphQL API to your project:

$ amplify add api
? Please select from one of the below mentioned services GraphQL
? Provide API name: myproject
? Choose an authorization type for the API API key
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? Yes
? What best describes your project: Single object with fields (e.g., “Todo” with ID, name, description)
? Do you want to edit the schema now? Yes

This will open the schema.graphql file in the editor you selected during the ‘amplify init’ step.

Replace the annotated schema template located in your <project-root>/amplify/backend/api/<api-name>/schema.graphql file with the following code:

type Customer @model {
  id: ID!
  name: String!
  createdAt: String
}

type Mutation {
  addEntry(id: Int, email: String, createdAt: String): String @function(name: "addEntry-${env}")
}

Check if the updates to your schema are compiled successfully by running the following command:

$ amplify api gql-compile

Now that your API is configured, run the amplify push command to deploy your changes to create the corresponding AWS backend resources.

When you’re prompted about code generation for your API, choose Yes. You can accept all default options. This generates queries, mutations, subscriptions, and boilerplate code for the Amplify libraries to consume. For more information, see Codegen in the Amplify CLI docs.

Accessing the function from your project

Now that your function and API are configured, you can access them through the API class, which is part of the Amplify JavaScript Library.

Open App.js and add the following import and call to Amplify API as shown below:

import awsconfig from './aws-exports';
import { API, graphqlOperation } from "aws-amplify";
import { addEntry }  from './graphql/mutations';
API.configure(awsconfig);

const entry = {id:“1”, email:“[email protected]”, createdAt:“2019-5-29”}
const data = await API.graphql(graphqlOperation(addEntry, entry))
console.log(data)

Running the app

Now that you have your application code complete, run the application and verify that the API call outputs “Success”.

Setting Amazon DynamoDB custom indexes in your GraphQL schemas

When building an application on top of DynamoDB, it helps to first think about access patterns. The new @key directive, which is a part of the GraphQL transformer in the Amplify CLI, makes it simple to configure complex key structures in DynamoDB that can fit your access patterns.

Let’s say we are using DynamoDB as a backend for your GraphQL API. The initial GraphQL schemas we can use to represent @model types Customer and Item are as shown below:

type Customer @model {
  email: String!
  username: String!
}

type Item @model {
    orderId: ID!
    status: Status!
    createdAt: AWSDateTime!
    name: String!
}

enum Status {
    DELIVERED
    IN_TRANSIT
    PENDING
    UNKNOWN
}

Access Patterns

For example, let’s say this application needs to facilitate the following access patterns:

  • Get customers by email – email is the primary key.
  • Get Items by status and by createdAt – orderId is the primary key

Let’s walkthrough how you would accomplish these use cases and call the APIs for these queries in your React JavaScript application.

Assumption: You completed the pre-requisites and created your React JavaScript application as shown in section 1.

Create an API

First, we will create a GraphQL API using the ‘amplify add api’ command:

$ amplify add api
? Please select from one of the below mentioned services GraphQL
? Provide API name: myproject
? Choose an authorization type for the API API key
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? Yes
? What best describes your project: Single object with fields (e.g., “Todo” with ID, name, description)
? Do you want to edit the schema now? Yes
? Press enter to continue

This will open the schema.graphql file under <myproject>/amplify/backend/api/myproject/schema.graphql

Modifying the schema.graphql file

Let’s dive in to the details with respect to the new @key directive.

Query by primary key

Add the following Customer @model type to your schema.graphql

type Customer @model @key(fields: ["email"]) {
    email: String!
    username: String
}

For Customer @model type, a @key without a name specifies the key for the DynamoDB table’s primary index. Here the hash key for the table’s primary index is email. You can only provide one @key without a name per @model type.

Query by composite keys (one or more fields are sort key)

type Item @model
    @key(fields: ["orderId", "status", "createdAt"])
    @key(name: "ByStatusAndCreatedAt", fields: ["status", "createdAt"], queryField: "itemsByStatusAndCreatedAt")
{
    orderId: ID!
    status: Status!
    createdAt: AWSDateTime!
    name: String!
}

enum Status {
    DELIVERED
    IN_TRANSIT
    PENDING
    UNKNOWN
}

Let’s break down the above Item @model type.

DynamoDB lets you query by at most two attributes. We added three fields to our first key directive ‘@key(fields: [“orderId”, “status”, “createdAt”])‘. The first field ‘orderId; will be the hash key as expected, but the sort key will be the new composite key named status#createdAt that is made of the status and createdAt fields. This enables us to run queries using more than two attributes at a time.

Run the ‘amplify push’ command to deploy your changes to the AWS cloud. Since we have the @key directive, it will create the DynamoDB tables for Customer and Item with the primary indexes, sort keys and generate resolvers that inject composite key values during queries and mutation.

$ amplify push
Current Environment: dev
? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2

The file <myproject>/src/graphlql/queries.js will contain the auto generated queries for our  intended access patterns “Get customers by email” and “Get Items by status and by createdAt”.

Accessing the API from your application

Now that your API is configured, you can access it through the API class, which is part of the Amplify JavaScript Library. We will call the query for “Get Items by status and by createdAt”

Open App.js and add the following import and call to Amplify API as shown below:

import awsconfig from './aws-exports';
import { API, graphqlOperation } from "aws-amplify";
import { itemsByStatusAndCreatedAt }  from './graphql/queries';
API.configure(awsconfig);

const entry = {status:'PENDING', createdAt: {beginsWith:"2019"}};
const data = await API.graphql(graphqlOperation(itemsByStatusAndCreatedAt, entry))
console.log(data)

To learn more, refer to the documentation here.

Feedback

We hope you like these new features! As always, let us know how we’re doing, and submit any requests in the Amplify Framework GitHub Repository. You can read more about AWS Amplify on the AWS Amplify website.

 

from AWS Mobile Blog

Using multiple authorization types with AWS AppSync GraphQL APIs

Using multiple authorization types with AWS AppSync GraphQL APIs

Written by Ionut Trestian, Min Bi, Vasuki Balasubramaniam, Karthikeyan, Manuel Iglesias, BG Yathi Raj, and Nader Dabit

Today, AWS announced that AWS AppSync now supports configuring more than one authorization type for GraphQL APIs. You can now configure a single GraphQL API to deliver private and public data. Private data requires authenticated access using authorization mechanisms such as IAM, Amazon Cognito User Pools, and OIDC. Public data does not require authenticated access and is delivered through authorization mechanisms such as API Keys.

You can also configure a single GraphQL API to deliver private data using more than one authorization type. For example, you can configure your GraphQL API to authorize some schema fields using OIDC, while other schema fields through Amazon Cognito User Pools or IAM.

AWS AppSync is a managed GraphQL service that simplifies application development. It allows you to create a flexible API to securely access, manipulate, and combine data from one or more data sources.

With today’s launch, you can configure additional authorization types while retaining the authorization settings of your existing GraphQL APIs. To ensure that there are no behavioral changes in your existing GraphQL APIs, your current authorization settings are set as the default.  You can add additional authorization types using the AWS AppSync console, AWS CLI, or AWS CloudFormation templates.

To add more authorization types using the AWS AppSync console, launch the console, choose your GraphQL API, then choose Settings and scroll to the Authorization settings. The snapshot below shows a GraphQL API configured to use API Key as the default authorization type. It also has two Amazon Cognito user pools and AWS IAM as additional authorization types.

  • To add more authorization types using the AWS CLI, see the create-graphql-api section of the AWS CLI Command Reference.
  • To add more authorization types through AWS CloudFormation, see AWS::AppSync::GraphQLApi in the AWS CloudFormation User Guide.

After configuring the authorization types for your GraphQL API, you can use schema directives to set the authorization types for one or more fields in your GraphQL schema. AWS AppSync now supports the following schema directives for authorization:

  • @aws_api_key to—A field uses API_KEY for authorization.
  • @aws_iam—A field uses AWS_IAM for authorization.
  • @aws_oidc—A field uses OPENID_CONNECT for authorization.
  • @aws_cognito_user_pools—A field uses AMAZON_COGNITO_USER_POOLS for authorization.

The following code example shows using schema directives for authorization:

schema {
    query: Query
    mutation: Mutation
}

type Query {
    getPost(id: ID): Post
    getAllPosts(): [Post]
    @aws_api_key
}

type Mutation {
    addPost(
        id: ID!
        author: String!
        title: String!
        content: String!
        url: String!
    ): Post!
}

type Post @aws_api_key @aws_iam {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
}

Assume that AWS_IAM is the default authorization type for this GraphQL schema. This means that fields without directives are protected using AWS_IAM. An example is the getPost() field in Query.

Next, look at the getAllPosts() field in Query. This field is protected using @aws_api_key, which means that you can access this field using API keys. Directives work at the field level. This means that you must give API_KEY access to the Post type as well. This can be done in two ways:

  • Mark each field in the Post type with a directive.
  • Mark the Post type itself with the @aws_api_key directive.

For this example, I chose the latter option.

Now, to restrict access to fields in the Post type, you can configure directives for individual fields, as shown below. You can add a field called restrictedContent to Post and restrict access to it by using the @aws_iam directive. With this setup, AWS_IAM authenticated requests can access restrictedContent, while requests authenticated with API keys do not have access.

type Post @aws_api_key @aws_iam {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    restrictedContent: String!
    @aws_iam
}

Amplify CLI

Amplify CLI version 1.6.8 supports adding AWS AppSync APIs configured with multiple authorization types. To add an API with mixed authorization mode, you can run the following command:

$ amplify add codegen —apiId <API_ID>

✖ Getting API details
✔ Getting API details
Successfully added API to your Amplify project
? Enter the file name pattern of graphql queries, mutations and subscriptions graphql/**/*.graphql
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2
? Enter the file name for the generated code API.swift
? Do you want to generate code for your newly created GraphQL API Yes
✔ Downloaded the schema
✔ Generated GraphQL operations successfully and saved at graphql
✔ Code generated successfully and saved in file API.swift

Android & iOS client support

AWS also updated the Android and iOS clients to support multiple authorization types. You can enable multiple clients by setting the useClientDatabasePrefix flag to true. The awsconfiguration.json file is generated by the AWS AppSync console, and the Amplify CLI adds an entry in the AWS AppSync section. This is used to separate the caches used for operations such as query, mutation, and subscription.

Important: If you are an existing client, the useClientDatabasePrefix flag has a default value of false.  When you use multiple clients, setting useClientDatabasePrefix to true changes the location of the caches used by the client. You also must migrate any data within the caches to keep.

The following code examples highlight the new values in the awsconfiguration.json and the client code configurations.

awsconfiguration.json

The friendly_name illustrated here is created from a prompt from the Amplify CLI. There are four clients in this configuration that connect to the same API, except that they use different AuthMode and ClientDatabasePrefix settings.

{
  "Version": "1.0",
  "AppSync": {
    "Default": {
      "ApiUrl": "https://xyz.us-west-2.amazonaws.com/graphql",
      "Region": "us-west-2",
      "AuthMode": "API_KEY",
      "ApiKey": "da2-xyz",
      "ClientDatabasePrefix": "friendly_name_API_KEY"
    },
    "friendly_name_AWS_IAM": {
      "ApiUrl": "https://xyz.us-west-2.amazonaws.com/graphql",
      "Region": "us-west-2",
      "AuthMode": "AWS_IAM",
      "ClientDatabasePrefix": "friendly_name_AWS_IAM"
    },
    "friendly_name_AMAZON_COGNITO_USER_POOLS": {
      "ApiUrl": "https://xyz.us-west-2.amazonaws.com/graphql",
      "Region": "us-west-2",
      "AuthMode": "AMAZON_COGNITO_USER_POOLS",
      "ClientDatabasePrefix": "friendly_name_AMAZON_COGNITO_USER_POOLS"
    },
    "friendly_name_OPENID_CONNECT": {
      "ApiUrl": "https://xyz.us-west-2.amazonaws.com/graphql",
      "Region": "us-west-2",
      "AuthMode": "OPENID_CONNECT",
      "ClientDatabasePrefix": "friendly_name_OPENID_CONNECT"
    }
  }
}

 

Android—Java

The useClientDatabasePrefix is added on the client builder, which signals to the builder that the ClientDatabasePrefix value should be used from the AWSConfiguration object (awsconfiguration.json).

AWSAppSyncClient client = AWSAppSyncClient.builder()
   .context(getApplicationContext())
   .awsConfiguration(new AWSConfiguration(getApplicationContext()))
   .useClientDatabasePrefix(true)
   .build();

iOS—Swift

The useClientDatabasePrefix is added to the AWSAppSyncCacheConfiguration, which reads the ClientDatabasePrefix value from the AWSAppSyncServiceConfig object (awsconfiguration.json).

let serviceConfig = try AWSAppSyncServiceConfig()
let cacheConfig = AWSAppSyncCacheConfiguration(useClientDatabasePrefix: true,
                                            appSyncServiceConfig: serviceConfig)
let clientConfig = AWSAppSyncClientConfiguration(appSyncServiceConfig: serviceConfig,
                                                   cacheConfiguration: cacheConfig)

let client = AWSAppSyncClient(appSyncConfig: clientConfig)

Public/private use case example

Here’s an example of how the newly introduced capabilities can be used in a client application.

Android—Java

The following code example creates a client factory to retrieve the client based on the need to operate in public (API_KEY) or private (AWS_IAM) authorization mode.

// AppSyncClientMode.java
public enum AppSyncClientMode {
    PUBLIC,
    PRIVATE
}

// ClientFactory.java
public class ClientFactory {

    private static Map<AWSAppSyncClient> CLIENTS;

    public static AWSAppSyncClient getAppSyncClient(AppSyncClientMode choice) {
        return CLIENTS[choice];
    }

    public static void initClients(final Context context) {
        AWSConfiguration awsConfigPublic = new AWSConfiguration(context);
        CLIENTS[PUBLIC] = AWSAppSyncClient.builder()
                                          .context(context)
                                          .awsConfiguration(awsConfigPublic)
                                          .useClientDatabasePrefix(true)
                                          .build();
        AWSConfiguration awsConfigPrivate = new AWSConfiguration(context);
        awsConfigPrivate.setConfiguration("friendly_name_AWS_IAM");
        CLIENTS[PRIVATE] = AWSAppSyncClient.builder()
                                           .context(context)
                                           .awsConfiguration(awsConfigPrivate)
                                           .useClientDatabasePrefix(true)
                                           .credentialsProvider(AWSMobileClient.getInstance())
                                           .build();
    }
}

This is what the usage would look like.

ClientFactory.getAppSyncClient(AppSyncClientMode.PRIVATE).query(fooQuery).enqueue(...);

iOS—Swift

The following code example creates a client factory to retrieve the client based on the need to operate in public (API_KEY) or private (AWS_IAM) authorization mode.

public enum AppSyncClientMode {
    case `public`
    case `private`
}

public class ClientFactory {
    static var clients: [AppSyncClientMode:AWSAppSyncClient] = [:]

    class func getAppSyncClient(mode: AppSyncClientMode) -> AWSAppSyncClient? {
        return clients[mode];
    }

    class func initClients() throws {
        let serviceConfigAPIKey = try AWSAppSyncServiceConfig()
        let cacheConfigAPIKey = try AWSAppSyncCacheConfiguration(useClientDatabasePrefix: true, appSyncServiceConfig: serviceConfigAPIKey)
        let clientConfigAPIKey = try AWSAppSyncClientConfiguration(appSyncServiceConfig: serviceConfigAPIKey, cacheConfiguration: cacheConfigAPIKey)
        clients[AppSyncClientMode.public] = try AWSAppSyncClient(appSyncConfig: clientConfigAPIKey)

        let serviceConfigIAM = try AWSAppSyncServiceConfig(forKey: "friendly_name_AWS_IAM")
        let cacheConfigIAM = try AWSAppSyncCacheConfiguration(useClientDatabasePrefix: true, appSyncServiceConfig: serviceConfigIAM)
        let clientConfigIAM = try AWSAppSyncClientConfiguration(appSyncServiceConfig: serviceConfigIAM,cacheConfiguration: cacheConfigIAM)
        clients[AppSyncClientMode.private] = try AWSAppSyncClient(appSyncConfig: clientConfigIAM)
    }
}

Conclusion

In this post, we showed how you can use the new multiple authorization type setting in AWS AppSync to allow separate public and private data authorization in your GraphQL API. While your current authorization settings are the default on existing GraphQL APIs, you can add additional authorization types using the AWS AppSync console, AWS CLI, or AWS CloudFormation templates.

from AWS Mobile Blog

Getting more visibility into GraphQL performance with AWS AppSync logs

Getting more visibility into GraphQL performance with AWS AppSync logs

Written by Shankar Raju, SDE at AWS & Nader Dabit, Sr. Developer Advocate at AWS.

Today, we are happy to announce that AWS AppSync now enables you to better understand the performance of your GraphQL requests and usage characteristics of your schema fields. You can easily identify resolvers with large latencies that may be the root cause of a performance issue. You can also identify the most and least frequently used fields in your schema and assess the impact of removing GraphQL fields. Offering support for these capabilities has been one of the top feature requests by our customers.

AWS AppSync is a managed GraphQL service that simplifies application development by letting you create a flexible API to securely access, manipulate, and combine data from one or more data sources. AWS AppSync now emits log events in a fully structured JSON format. This enables seamless integration with log analytics services such as Amazon CloudWatch Logs Insights and Amazon Elasticsearch Service (Amazon ES), and other log analytics solutions.

We have also added new fields to log events to increase your visibility into the performance and health of your GraphQL operations:

  • To search and analyze one or more log types, GraphQL requests, and multiple GraphQL API actions, we added new log fields (logType, requestId, and graphQLAPIId) to every log event that AWS AppSync emits.
  • To quickly identify errors and performance bottlenecks, we added new log fields to the existing request-level logs. These log fields contain information about the HTTP response status code (HTTPStatusResponseCode) and latency of a GraphQL request (latency).
  • To uniquely identify and run queries against any field in your GraphQL schema, we added new log fields to the existing field-level logs. These log fields contain information about the parent (parentType) and name (fieldName) of a GraphQL field.
  • To gain visibility into the time taken to resolve a GraphQL field, we also included the resolver ARN (resolverARN) in the tracing information of GraphQL fields in the field-level logs.

In this post, we show how you can get more visibility into the performance and health of your GraphQL operations using CloudWatch Logs Insights and Amazon ES. As a prerequisite, you must first enable field-level logging for your GraphQL API so that AWS AppSync can emit logs to CloudWatch Logs.

Analyzing your logs with CloudWatch Logs Insights

You can analyze your AWS AppSync logs with CloudWatch Logs Insights to identify performance bottlenecks and the root cause of operational issues. For example, you can find the resolvers with the maximum latency, the most (or least) frequently invoked resolvers, and the resolvers with the most errors.

There is no setup required to get started with CloudWatch Logs Insights. This is because AWS AppSync automatically emits logs into CloudWatch Logs when you enable field-level logging on your GraphQL API.

The following are examples of queries that you can run to get actionable insights into the performance and health of your GraphQL operations. For your convenience, we added these examples as sample queries in the CloudWatch Logs Insights console.

In the CloudWatch console, choose Logs, Insights, select the AWS AppSync log group for your GraphQL API, and then choose Sample queries, AWS AppSync queries.

Find top 10 GraphQL requests with maximum latency

fields requestId, latency
| filter logType = "RequestSummary"
| limit 10
| sort latency desc

Find top 10 resolvers with maximum latency

fields resolverArn, duration
| filter logType = "Tracing"
| limit 10
| sort duration desc

Find the most frequently invoked resolvers

fields ispresent(resolverArn) as isRes
| stats count() as invocationCount by resolverArn
| filter isRes and logType = "Tracing"
| limit 10
| sort invocationCount desc

Find resolvers with most errors in mapping templates

fields ispresent(resolverArn) as isRes
| stats count() as errorCount by resolverArn, logType
| filter isRes and (logType = "RequestMapping" or logType = "ResponseMapping") and fieldInError
| limit 10
| sort errorCount desc

The results of CloudWatch Logs Insights queries can be exported to CloudWatch dashboards. We added a CloudWatch dashboard template for AWS AppSync logs in the AWS Samples GitHub repository. You can import this template into CloudWatch dashboards to have continuous visibility into your GraphQL operations.

Analyzing your logs with Amazon ES

You can search, analyze, and visualize your AWS AppSync logs with Amazon ES to identify performance bottlenecks and root cause of operational issues. Not only can you identify resolvers with the maximum latency and errors, but you can also use Kibana to create dashboards with powerful visualizations. Kibana is an open source, data visualization and exploration tool available in Amazon ES.

To get started with Amazon ES:

  1. Create an Amazon ES cluster, if you don’t have one already.
  2. In the CloudWatch Logs console, select the log group for your GraphQL API.
  3. Choose Actions, Stream to Amazon Elasticsearch Service and select the Amazon ES cluster to which to stream your logs. You can also use a log filter pattern to stream a specific set of logs. The following example is the log filter pattern for streaming log events containing information about the request summary, tracing, and GraphQL execution summary for AWS AppSync logs.
{ ($.logType = "Tracing") || ($.logType = "RequestSummary") || ($.logType = "ExecutionSummary") }

You can create Kibana dashboards to help you identify performance bottlenecks and enable you to continuously monitor your GraphQL operations. For example, to debug a performance issue, start by visualizing the P90 latencies of your GraphQL requests and then drill into individual resolver latencies.

To build a Kibana dashboard containing these visualizations, use the following steps:

  1. Launch Kibana and choose Dashboard, Create new dashboard.
  2. Choose Add. For Visualization type, choose Line.
  3. For the filter pattern to search Elasticsearch indexes, use cwl*. Elasticsearch indexes logs streamed from CloudWatch Logs (including AWS AppSync logs) with a prefix of “cwl-”. To differentiate AWS AppSync logs from other CloudWatch logs sent to Amazon ES, we recommend adding an additional filter expression of graphQLAPIID.keyword=<AWS AppSync GraphQL API ID> to your search.
  4. To get GraphQL request data from AWS AppSync logs, choose Add Filter and use the filter expression logType.keyword=RequestSummary.
  5. Choose Metrics, Y-Axis. For Aggregation, choose Percentile; for Field, choose latency, and for Percents, enter a value of 90. This enables you to view GraphQL request latencies on the Y axis.
  6. Choose Buckets, X-Axis. For Aggregation, choose Date Histogram; for Field, choose @timestamp; and for Interval, choose Minute. This enables you to view GraphQL request latencies aggregated in 1-minute intervals. You can change the aggregation internal to view latencies aggregated at a coarser or finer grained time interval to match your data density.
  7. Save your widget and add it to the Kibana dashboard, as shown below:

  1. To build a widget that visualizes the P90 latency of each resolver, repeat steps 1, 2, 3, and 4 earlier. For step 4, use a filter expression of logType.keyword=Tracing to get resolver latencies from AWS AppSync Logs.
  2. Repeat step 5 using duration as the Field value and then repeat step 6.
  3. Choose Add sub-buckets, Split Series. For Sub Aggregation, use Terms and for Field, choose resolverArn.keyword. This enables you to visualize the latencies of individual resolvers.
  4. Save your widget and add it to the Kibana dashboard, as shown below:

Here’s a Kibana dashboard containing widgets for the P90 request latencies and individual resolver latencies:

Availability
The new logging capabilities are available in the following AWS Regions and you can start analyzing your logs today:

  • US East (N. Virginia)
  • US East (Ohio)
  • US West (Oregon)
  • Europe (Ireland)
  • Europe (Frankfurt)
  • Europe (London)
  • Asia Pacific (Tokyo)
  • Asia Pacific (Mumbai)
  • Asia Pacific (Seoul)
  • Asia Pacific (Sydney)
  • Asia Pacific (Singapore)

Log events emitted on May 8, 2019, or later use the new logging format. To analyze GraphQL requests before May 8, 2019, you can migrate older logs to the new format using a script available in the GitHub sample.

from AWS Mobile Blog

Use the Amplify Console with incoming webhooks to trigger deployments

Use the Amplify Console with incoming webhooks to trigger deployments

Written by Nikhil Swaminathan, Sr. Product Manager (Tech) at AWS.

The Amplify Console recently launched support for incoming webhooks. This feature enables you to use third-party applications such as Contentful and Zapier to trigger deployments in the Amplify Console without requiring a code commit.

You can use headless CMS tools such as Contentful with the Amplify Console incoming webhook feature to trigger a deployment every time content is updated—for example, when a blog author publishes a new post. Modern CMSs are headless in nature, which gives developers the freedom to develop with any technology because the content itself doesn’t have a presentation layer. Content creators get the added benefit of publishing a single instance of the content to both web and mobile devices.

In this blog post, we set up Amplify Console to deploy updates every time new content is published.

1. Create a Contentful account using the Contentful CLI, and follow the steps in the getting started guide. The CLI helps you create a Contentful account, a Contentful project (called a space) with a sample blog content model, and a starter repository that’s downloaded to your local machine.

2. After the CLI creates a Contentful space, log in to your Contentful space at the Contentful website and choose ‘Settings > API Keys’.

3. The API keys were generated when you ran the CLI. Copy the Space ID and Content Delivery API. You’ll need these to trigger content deployments.

4. Push the code to a Git repo of your choice (Amplify Console supports GitHub, BitBucket, GitLab, and CodeCommit).

Log in to the Amplify Console, connect your repo, and pick a branch. On the Build Settings page, enter the CONTENTFUL_DELIVERY_TOKEN and the CONTENTFUL_SPACE_ID into the environment variables section. These tokens are used by your app during the build to authenticate with the Contentful service. Review the changes, and choose Save and deploy. Your app builds and deploys to an amplifyapp.com URL. It should look like this:

5. Create an incoming webhook to publish content updates. Choose App Settings > Build Settings, and then choose Create webhook. This webhook enables you to trigger a build in the Amplify Console on every POST to the HTTP endpoint. After you create the webhook, copy the URL (it looks like  https://webhooks.amplify…)

6. Go back to the Contentful dashboard, and choose Settings > Webhooks. Then choose  Add Webhook. Paste the webhook URL you copied from the Amplify Console into the URL section and update the Content Type to application/json. Choose Save.

 

7. We’re now ready to trigger a new build through a content update! Go to the Content tab on Contentful and add a new entry with the following fields—Name: Deploying to Amplify Console and Content Type: Blog Post. Enter the other required fields, and choose Publish.

8. The Amplify Console will kickoff a new build with the newest post.

You can also use the incoming webhook feature to trigger builds on post-commit Git hooks, and through daily build schedulers. We hope you like this new feature – learn more about the Amplify Console at https://console.amplify.aws.

from AWS Mobile Blog

Amplify Framework announces new Amazon Aurora Serverless and GraphQL Transform features for building AWS AppSync APIs

Amplify Framework announces new Amazon Aurora Serverless and GraphQL Transform features for building AWS AppSync APIs

The Amplify Framework is an open-source project for building cloud-enabled applications. Today, we’re happy to announce new features for the GraphQL Transform library, which is part of the AWS Amplify command line interface (CLI). The GraphQL Transform library enables you to quickly deploy scalable AWS AppSync backends for your web and mobile applications. In this release, we’ve added new features to the library that allow for more flexibility and control over your API.

In addition, you can now use an existing Amazon Aurora Serverless database as a data source for your AWS AppSync GraphQL APIs when you’re building your mobile and web applications. This enables you to use the Amplify CLI to generate a GraphQL API with an auto-generated schema and resolvers that work with an existing Aurora Serverless database.

Support for Amazon Aurora Serverless

You can now generate a GraphQL API in front of an existing Aurora Serverless database.

Let’s say that you already have an existing database and you’d like to generate a GraphQL API in front of it. You can easily do so with the amplify api add-graphql-datasource command, and select your preferred data source type. Currently, only Aurora Serverless is supported, so the following steps  show how to import an Aurora Serverless MySQL database as a data source.

1. In the AWS Amplify CLI , run the following:

$ amplify api add-graphql-datasource
Using datasource: Aurora Serverless, provided by: awscloudformation

To import an Aurora Serverless MySQL database, you first have to choose the AWS Region where it exists. The CLI only provides the Regions where the Aurora Serverless data API is available.

? Provide the region in which your cluster is located: (Use arrow keys)
❯ us-east-1

3. Next, choose the appropriate cluster identifier from the list that the CLI provides.

? Select the Aurora Serverless cluster that will be used as the data source for your API (Use arrow keys)
❯ animals
  owners

4. Next, the CLI attempts to determine the appropriate secret to use automatically. If it’s not able to, it presents you with a list to select from .

5. Finally, the CLI provides a list of all of the databases that are active inside the selected cluster (this could take a few seconds). Choose the database that you’d like to import.

? Select the database to use as the data source: (Use arrow keys)
❯ Animals

At this point, the command executes and creates an AWS CloudFormation template with a schema that’s defined by the database it just parsed, as well as pre-generated resolvers for basic operations.

If the schema generation conflicts with resources that might already exist in your API schema or if the schema generated is invalid, the process fails and gives an appropriate error message. Otherwise, the schema is output in the amplify/backend/api/YOUR-API-NAME/ directory. The template is available in the amplify/backend/api/YOUR-API-NAME/stacks directory.

When the schema and resolvers look ready , run amplify push to build the template.

To learn more, see the documentation.

Improved GraphQL API operations control

In the past, rules defined in the @auth directive only protected top-level fields.

With the latest release, @connection resolvers now also protect access to connected fields based on the @auth rules that are defined on the related model type. Let’s take a look at how this works.

Take note of the Post type definition in the schema.

# schema.graphql
# Conceptually we are saying protect read access to owners. This has always protected
# top level fields.
type Post @model @auth(rules: [{ allow: owner, operations: [read, create, update, delete] }]) {
  id: ID!
  title: String
  author: User @connection(name: "UserPosts")
}
type User @model {
  id: ID!
  username: String
  posts: [Post] @connection(name: "UserPosts")
 # @connection resolvers used to bypass @auth rules defined on @models.
 # @connection resolvers now take those @auth directives into account. In this case,
 # you will only see posts where the $ctx.identity.username is the same as
 # the "owner" on the Post object.
}

With this schema, the @connection field also inherits the @auth rules that are set at the top-level field.

To learn more, see the documentation.

Authorization (@auth) directive improvement – Field level auth

Previously the @auth directive protected only the root-level query and mutation fields.

You can now use the @auth directive on individual fields, in addition to the object type definition. This means that you now have the ability to implement fine-grained access control across your API at both the top level  and the field level.

An @auth directive that you use on an @model OBJECT augments the top-level queries and mutations. An @auth directive that you use on a FIELD_DEFINITION protects that field’s resolver by comparing the identity to the source object designated through $ctx.source.

For example, you might have the following:

type User @model {
    id: ID!
    username: String
    
    # Can be used to protect @connection fields.
    # This resolver will compare the $ctx.identity to the "username" attribute on the User object (via $ctx.source in the User.posts resolver).
    # In other words, we are authorizing access to posts based on information in the user object.
    posts: [Post] @connection(name: "UserPosts") @auth(rules: [{ allow: owner, ownerField: "username" }])

    # Can also be used to protect other fields
    ssn: String @auth(rules: [{ allow: owner, ownerField: "username" }])
}
# Users may create, update, delete, get, & list at the top level if they are the
# owner of this post itself.
type Post @model @auth(rules: [{ allow: owner }]) {
    id: ID!
    title: String
    author: User @connection(name: "UserPosts")
    owner: String
}

With this schema, authorization rules are in place that allow only the owner to query for posts and ssn fields on the User type, while the id and username fields aren’t be protected by any authorization rules.

To learn more, see the documentation.

Feedback

We hope you like these new features! As always, let us know how we’re doing, and submit any requests in the Amplify Framework GitHub Repository. You can read more about AWS Amplify on the AWS Amplify website.

 

from AWS Mobile Blog

AWS AppSync real-time reference architecture

AWS AppSync real-time reference architecture

* This article was written by Ed Lima, Solutions Architect at AWS.

Mobile and web applications need to provide engaging and collaborative experiences to users, and real-time access to data is essential. Real-time technologies enable users to receive information as it happens. Use cases such as live scores, second screen, chat applications, dashboards, all sorts of collaborative applications, and many more wouldn’t be possible without bidirectional communication technologies such as the WebSocket protocol.

As your application and user base grows, implementing a real-time backend at scale becomes a complex problem to solve. You have to solve the problem of how to manage live connections, fan-out capabilities, and seamless performance as the number of connected clients increase. What if you could build and deploy scalable real-time applications without worrying about backend servers, connection management, high availability, and reliability? Also, what if you could take advantage of a highly optimized and robust GraphQL data layer to deliver your application data to your front-end app users? This would enable you to spend more time focusing on your business logic?

AWS AppSync is a GraphQL serverless backend for mobile, web, and enterprise applications. It provides a flexible, smart, and reliable data layer to access multiple data sources in your AWS account. AWS AppSync takes advantage of GraphQL subscriptions to perform real-time operations by pushing data to clients that choose to listen to specific events from the backend. This means that you can easily and effortlessly make any supported data source in AWS AppSync real time. The connection management is handled automatically by the AWS AppSync client SDK or AWS Amplify client using WebSocket as the network protocol between the client and service:

AWS AppSync takes care of the undifferentiated heavy lifting of managing your real-time backend infrastructure. However, it’s important that your application is well architected and designed in order to provide the best experience possible to your end users. For that reason, today we’re publishing our first AWS AppSync real-time reference architecture. It includes sample code that helps you understand and apply best practices to your next collaborative real-time application development project.

For a detailed explanation of how all the serverless parts of the puzzle fit together to perform different types of real-time use cases (one-to-many and/or many-to-many), as well as the GitHub repository where you can try the code yourself, see related article on OPENGraphQL.

from AWS Mobile Blog

Invoke AWS services directly from AWS AppSync

Invoke AWS services directly from AWS AppSync

* Written by Josh Kahn, Senior AWS Solutions Architect based in Chicago, IL.

AWS AppSync is a managed GraphQL service that enables developers to easily build data-driven mobile and web applications. Using a serverless backend, you can build a GraphQL API by connecting AWS AppSync to various data sources—including Amazon DynamoDB, AWS Lambda, and Amazon Elasticsearch Service. AWS AppSync added support for HTTP data sources in May 2018, which makes it easy to add legacy APIs to your GraphQL endpoints.

AWS AppSync has now been extended to support calling AWS services via HTTP data sources. In order for AWS to identify and authorize HTTP requests, they must be signed with the Signature Version 4 process. Otherwise, those requests are rejected. AWS AppSync can now calculate the signature on your behalf, based on the IAM role that’s provided as part of the HTTP data source configuration.

This means that you can call a broad array of AWS services without the need to write an intermediary Lambda function. For example, you could start execution of an AWS Step Functions state machine, retrieve a secret from AWS Secrets Manager, or list available GraphQL APIs from AWS AppSync itself from an AWS AppSync resolver.

Implementing a long-running query on AWS AppSync

To demonstrate the utility of this new feature, let’s build a GraphQL API with a long-running query. AWS AppSync limits GraphQL query execution to a maximum of 30 seconds. However, we might have a query (for example, a search) that sometimes takes up to one minute. We’re using AWS Step Functions to coordinate the long-running query across multiple steps, but you could opt to implement in other ways as well. We use AWS AppSync subscriptions to asynchronously update the client when the query is finished.

Create an HTTP data source

To start execution of the search state machine from AWS AppSync, we start by defining a new HTTP data source. We need to provide two additional components to invoke an AWS service:

  • An IAM role that can be assumed by AWS AppSync with permission to call states:StartExecution for our Step Functions state machine
  • The signing configuration

AWS AppSync currently provides support to add these components by using the AWS CLI, SDKs, and AWS CloudFormation.

First, we create a new IAM role with the following policy (be sure to note the role Amazon Resource Name (ARN)):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "states:StartExecution"
            ],
            "Resource": [
                "arn:aws:states:<REGION>:<ACCOUNT_ID>:stateMachine:aws-appsync-long-query"
            ],
            "Effect": "Allow"
        }
    ]
}

And trust relationship:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "appsync.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Next, we create the HTTP data source for AWS AppSync. To do this, create the following configuration file named http.json.

{
    "endpoint": "https://states.<REGION>.amazonaws.com/",
    "authorizationConfig": {
        "authorizationType": "AWS_IAM",
        "awsIamConfig": {
            "signingRegion": "<REGION>",
            "signingServiceName": "states" 
        }
    }
}

Then, use the AWS CLI to create the data source:

$ aws appsync create-data-source --api-id <API_ID> \
                                 --name StepFunctionHttpDataSource \
                                 --type HTTP \
                                 --http-config file:///http.json \
                                 --service-role-arn <ROLE_ARN>

Alternatively, you can create the same data source by using AWS CloudFormation:

StepFunctionsHttpDataSource:
    Type: AWS::AppSync::DataSource
    Properties:
      ApiId: !GetAtt SearchApi.ApiId
      Name: StepFunctionsHttpDataSource
      Description: Step Functions HTTP
      Type: HTTP
      # IAM role defined elsewhere in AWS CloudFormation template
      ServiceRoleArn: !GetAtt AppSyncServiceRole.Arn
      HttpConfig:
        Endpoint: !Sub https://states.${AWS::Region}.amazonaws.com/
        AuthorizationConfig:
          AuthorizationType: AWS_IAM
          AwsIamConfig:
            SigningRegion: !Ref AWS::Region
            SigningServiceName: states

With our HTTP data source defined and configured to sign requests to Step Functions, we can build our GraphQL API.

Building a GraphQL API

To enable our long running query, we submit the search request and immediately return the status of the search (PENDING), as well as a unique identifier for the query. Our client then needs to subscribe to updates for that particular query. The GraphQL schema is as follows:

type Result {
  id: ID!
  status: ResultStatus!
  listings: [String]
}

enum ResultStatus {
  PENDING
  COMPLETE
  ERROR
}

input ResultInput {
  id: ID!
  status: ResultStatus!
  listings: [String]!
}

type Query {
  # called by client to initiate long running search
  search(text: String!): Result
}

type Mutation {
  # called by backend when search is complete
  publishResult(result: ResultInput): Result
}

type Subscription {
  onSearchResult(id: ID!): [Result]
    @aws_subscribe(mutations: [ "publishResult" ])
}

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

Next, define the resolver for the search query in the AWS Management Console, AWS CLI, SDK, or AWS CloudFormation. Select the HTTP data source that was created previously and configure the request mapping template as follows:

$util.qr($ctx.stash.put("executionId", $util.autoId()))

{
  "version": "2018-05-29",
  "method": "POST",
  "resourcePath": "/",
  "params": {
    "headers": {
      "content-type": "application/x-amz-json-1.0",
      "x-amz-target":"AWSStepFunctions.StartExecution"
    },
    "body": {
      "stateMachineArn": "arn:aws:states:<REGION>:<ACCOUNT_ID>:stateMachine:aws-appsync-long-query",
      "input": "{ \"name\": \"$ctx.stash.executionId\" }"
    }
  }
}

There are a few items to note in this mapping:

  • We use the AWS AppSync $util.autoId() function to generate a unique identifier for the search query. This value is stored in the context stash and is later returned to the client. This value is also passed as an input parameter to our state machine.
  • The x-amz-target header specifies the AWS service and associated action to invoke.
  • JSON input data, such as the input to our state machine, must be double escaped.

The response mapping for this resolver immediately returns the query identifier and a PENDING status:

{
  "id": "${ctx.stash.executionId}",
  "status": "PENDING"
}

On executing the search query, AWS AppSync now starts the execution of the desired Step Functions state machine, passing the execution identifier as a parameter. The last step of the state machine is a task step that invokes a Lambda function. This function triggers an AppSync mutation. This causes the results to be published to the client through a subscription.

Implementing the long-running query and returning a result

With our AWS AppSync query now starting execution of the Step Functions state machine, we can implement the query and return a response to the client. For the purpose of demonstration, our state machine is quite simple. It waits for one minute, and then invokes the “Return Results” task, as shown in the following diagram.

You can easily update this simple state machine definition to support more complex or multistep queries, as long as the definition returns results through an AWS AppSync mutation along the way.

Generally, a GraphQL client executes a mutation of data that causes a change in a data source, which then triggers a notification of the change to subscribers. In this case, we define a Lambda function that calls the mutation to trigger a notification of the results.

Instead of including a GraphQL library, we’re using a simple HTTP POST to the AWS AppSync endpoint, passing an appropriate payload for a mutation. While our sample uses an AWS AppSync API_KEY authorization type, we recommend AWS_IAM in a production scenario, and would require signing the request as well. Using the nodejs8.10 runtime, our simple Return Result function is implemented as follows:

const PublishResultMutation = `mutation PublishResult(
    $id: ID!,
    $status: ResultStatus!,
    $listings: [String]!
  ) {
    publishResult(result: {id: $id, status: $status, listings: $listings}) {
      id
      status
      listings
    }
  }`;

const executeMutation = async(id) => {
  const mutation = {
    query: PublishResultMutation,
    operationName: 'PublishResult',
    variables: {
      id: id,
      status: 'COMPLETE',
      listings: [ "foo", "bar" ]
    },
  };

  try {
    let response = await axios({
      method: 'POST',
      url: process.env.APPSYNC_ENDPOINT,
      data: JSON.stringify(mutation),
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': process.env.APPSYNC_API_KEY,
      }
    });
    console.log(response.data);
  } catch (error) {
    console.error(`[ERROR] ${error.response.status} - ${error.response.data}`);
    throw error;
  }
};

exports.handler = async(event) => {
  // unique identifier of query is passed as event.name
  await executeMutation(event.name)
  return { message: `finished` }
}

AWS AppSync sends a notification to the client that’s subscribed for this particular query result. The client can then update appropriately.

Client implementation

Using AWS Amplify, we can implement a simple client to demonstrate the process of submitting the query and then subscribing for results using the returned identifier.

import Amplify, { API, graphqlOperation } from "aws-amplify";

// 1. Submit search query
const { data: { search } } = await API.graphql(
    graphqlOperation(searchQuery, { text: 'test' })
);
console.log(`Query ID: ${search.id}`);

// 2. Subscribe to search result
const subscription = API.graphql(
        graphqlOperation(onSearchResultSubscription, { queryId: search.id })
    ).subscribe({
        next: (result) => {
            // Stop receiving data updates from the subscription
            subscription.unsubscribe();
            console.log(result);
        }
    });

You can find a working example of this project on GitHub.

Invoking AWS services directly from AWS AppSync can simplify a number of use cases, such as implementation of a long-running query, or enabling the use of AWS AppSync as a GraphQL facade for other services. For more details, see the AWS AppSync Developer Guide. We’re eager to see how you take advantage of this new capability. Please reach out to us on the AWS AppSync Forum with any feedback.

from AWS Mobile Blog

Amplify Framework simplifies configuration for OAuth flows, the hosted UI, and AR/VR scenes for mobile and web developers

Amplify Framework simplifies configuration for OAuth flows, the hosted UI, and AR/VR scenes for mobile and web developers

Written by Kurt Kemple, Sr. Developer Advocate & Gerard Sans, Sr. Developer Advocate

The Amplify Framework is an open-source project for building cloud-enabled applications. Today, we’re happy to announce new features in the authentication , XR, storage, and API categories. It’s now possible to configure OAuth 2.0 authorization flows and enable the Amazon Cognito hosted UI from the Amplify command line interface (CLI) (part of the Amplify Framework). Previously, you had to go to the Amazon Cognito console to set this up and construct the proper application configurations manually in the web or mobile application.

For the Amplify XR category, the Amplify Framework provides a simplified configuration setup for augmented reality (AR) or virtual reality (VR) scenes that are powered by Amazon Sumerian in your mobile and web applications. The Amplify Framework enables you to automatically add authorization to your scenes from the Amplify CLI by using Amazon Cognito and IAM.

In addition, the Amplify Framework now supports setting up fine-grained CRUD-style permissions for the storage and API (REST) categories. This works for both authenticated and unauthenticated workflows.

Setting up the hosted UI and OAuth from the Amplify CLI

In this release of Amplify, you can now set up the Amazon Cognito hosted UI and OAuth from the Amplify CLI. This enables you to use Amazon Cognito user pools to set up federation between various identity providers (such as Amazon, Google, and Facebook) from the Amplify CLI. You use OAuth flows for the federation—either through the hosted UI or your application code, by using the endpoints directly.

Let’s look at setting up OAuth for Facebook in a React app. The first step is to create a new project.

Setting up the project

We will be using npx to create the React application. To learn more about npx, click here.

Use the following command to create a new React app project:

npx create-react-app my-project

Next you need to set up Amplify:

cd my-project
amplify init
npm i aws-amplify aws-amplify-react

Setting up OAuth with Facebook

After Amplify is set up, the next step is to create a Facebook app to enable authentication. For instructions on how to set up a Facebook app, see Adding Social Identity Providers to a User Pool.

Adding the authentication category with the Amplify CLI

Now that you have a Facebook app, it’s time to add authentication to your app with the Amplify CLI. Make sure to enter the values in the snippet below.

amplify add auth

// Do you want to use the default authentication and security configuration? Default configuration with Social Provider (Federation)
// How do you want end users to be able to sign up? Username
// What attributes are required for signing up? Email
// What domain name prefix you want us to create for you? <enter-a-unique-domain>
// Enter your redirect signin URI: http://localhost:3000/
// Do you want to add another redirect signin URI? No
// Enter your redirect signout URI: http://localhost:3000/
// Do you want to add another redirect signout URI? No
// Select the identity providers you want to configure for your user pool: Facebook
// Enter your Facebook App ID: <facebook_app_id>
// Enter your Facebook App Secret: <facebook_app_secret>

After the command completes, you need to run amplify push to create your Amazon Cognito user pool and get your endpoints for your hosted UI. You get the domain URL from the output of the “amplify push” command.

You need the endpoints to finish configuring your Facebook app.

To finish setting up your Facebook app go back to the documentation linked in the “Setting Up OAuth With Facebook” section and follow the steps for adding your app domain. You get the domain URL from the output of the amplify push command.

Add OAuth to React

The next step is to configure Amplify and the authentication category in your app. Update src/App.js with the following:

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import Amplify from "aws-amplify";
import { withOAuth } from "aws-amplify-react";
import aws_exports from "./aws-exports";

Amplify.configure(aws_exports);

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
          <button onClick={this.props.OAuthSignIn}>
            Sign in with Facebook
          </button>
        </header>
      </div>
    );
  }
}

export default withOAuth(App);

The withOAuth higher-order component (HOC) provides the property OAuthSignIn that takes the user to the hosted UI for authenticating. If you don’t want to use the HOC and instead want to handle sending users to the hosted UI, see the instructions in Launching the Hosted UI.

Try it out!

After you’ve updated App.js, it’s time to run the app and test the login. You should see a screen like the following when you choose the Sign in with Facebook button.

Improving the Amazon Sumerian scene setup

The XR category within Amplify Framework enables applications to use AR and VR content, and provides built-in Amazon Sumerian support.

With this release, we’ve simplified adding scenes from Amazon Sumerian. The Amplify CLI now prompts a few more questions so it can set up authorization for a scene, and add all the necessary roles and policies for you.

Adding a new scene workflow

Let’s look at the new workflow. We’re using a React app to demonstrate it.

npx create-react-app xr-app
cd xr-app
npm i aws-amplify aws-amplify-react

To initialize the new project, run the following:

amplify init

Before proceeding, we need to publish the scene and download its configuration as before by using the Amazon Sumerian console.

  1. In the Sumerian console, create or open the scene that you want to use, choose Publish dropdown from the top-right corner, and then choose Host privately.
  2. You’re then prompted with a dialog box. Choose the Publish button.
  3. Choose the Download JSON configuration button, and place the file into the src directory of xr-app.
  4. Rename the file to ‘scene1_config.json’.

We’re now ready to add the scene. Make sure that you have the name of your scene and the path to its configuration (for example: src/scene1_config.json). The Amplify CLI prompts all these settings, including a setting for if you want to allow unauthenticated users.

amplify add xr

Open the Amazon Sumerian console: https://console.aws.amazon.com/sumerian/home/start
Publish the scene you want to add.
Then download the JSON configuration to your local computer.
? Press Enter when ready.
? Provide a name for the scene: scene1
? Enter the path to the downloaded JSON configuration file for scene1: src/scene1_config.json
Adding XR to your project requires the Auth category.
Successfully added auth resource locally.
? Allow unauthenticated users to access your XR scene (y/N): Y
Successfully added resource scene1 locally

Some next steps:
"amplify push" builds all of your local backend resources and provisions them in the cloud
"amplify publish" builds all of your local backend and front-end resources (if you added hosting category) and provisions them in the cloud

We’re ready to create all the resources in the cloud:

amplify push

Current Environment: dev

| Category | Resource name   | Operation | Provider plugin   |
| -------- | --------------- | --------- | ----------------- |
| Auth     | cognito         | Create    | awscloudformation |
| Xr       | scene1          | Create    | awscloudformation |
? Are you sure you want to continue? (Y/n) Y
⠹ Updating resources in the cloud. This may take a few minutes...
✔ All resources are updated in the cloud

Let’s add the scene to the new project. Open ‘src/App.js’ and replace its content with the following:

import React, { Component } from 'react';
import Amplify from 'aws-amplify';
import { SumerianScene } from 'aws-amplify-react';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);

class App extends Component {
  render() {
    return (
      <div className="App">
        <SumerianScene sceneName={"scene1"} />
      </div>
    );
  }
}

export default App;

We need to make some adjustments to the styles so that our scene shows as centered for all devices and screen sizes. Open src/index.css and add the following styles at the top:

html, body {
  height: 100%;
}

Open src/App.css and replace the create-react-app default styles with the following:

.App {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

To try out the scene, we can run our project with the following:

npm start

Check the final output at http://localhost:3000. The following image is only for demonstration purposes.

We hope you like these new features! As always, let us know how we’re doing, and submit any requests in the Amplify Framework GitHub Repository. You can read more about AWS Amplify on the AWS Amplify website.

 

 

 

 

 

 

from AWS Mobile Blog