Giter Site home page Giter Site logo

awslabs / aws-mobile-appsync-sdk-android Goto Github PK

View Code? Open in Web Editor NEW
105.0 41.0 57.0 1.4 MB

Android SDK for AWS AppSync.

Home Page: https://docs.amplify.aws/sdk/api/graphql/q/platform/android/

License: Apache License 2.0

Java 85.69% Kotlin 12.93% Groovy 0.73% Ruby 0.12% Velocity Template Language 0.54%
android java appsync graphql graphql-client graphql-android aws-appsync appsync-sdk aws-mobile gradle

aws-mobile-appsync-sdk-android's Introduction

Recommendation: Use Amplify clients to connect to AppSync

For front-end web and mobile development, we recommend using the Amplify clients which are optimized to connect to the AppSync backend.

  • For DynamoDB data sources, use the DataStore category in the Amplify client. It provides the best developer experience and built-in conflict detection and resolution.
  • For non-DynamoDB data sources in scenarios where you have no offline requirements, use the API (GraphQL) category in the Amplify client.

AWS AppSync SDK for Android

GitHub release Maven Central Build Status

The AWS AppSync SDK for Android enables you to access your AWS AppSync backend and perform operations like queries, mutations, and subscription. The SDK also includes support for offline operations. This SDK is derrived from the Apollo project found here. Please log questions for this client SDK in this repo and questions for the AppSync service in the official AWS AppSync forum.

Platform Support

AWS AppSync SDK for Android supports Android API level 21 (Android 5.0) and above.

Samples

  1. A sample app using the events sample schema can be found here: https://github.com/aws-samples/aws-mobile-appsync-events-starter-android

Step by step documentation can be found here: https://aws-amplify.github.io/docs/android/api

Setup

Gradle setup

Project's build.gradle

In the project's build.gradle, add a dependency to the dependencies inside the buildscript block:

classpath 'com.amazonaws:aws-android-sdk-appsync-gradle-plugin:3.4.1'

Also, add the maven plugins repository to your repositories.

Do this for the repositories block under buildscript:

buildscript {
    repositories {
        // Add this maven block.
        maven {
            url "https://plugins.gradle.org/m2/"
        }
        google()
        mavenCentral()
    }
}

And also under allprojects, too:

allprojects {
    repositories {
        // Add this maven block.
        maven {
            url "https://plugins.gradle.org/m2/"
        }
        google()
        mavenCentral()
    }
}

Sample project's build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        maven {
            url "https://plugins.gradle.org/m2/"
        }
        google()
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'
        classpath 'com.amazonaws:aws-android-sdk-appsync-gradle-plugin:3.4.1'
    }
}

allprojects {
    repositories {
        maven {
            url "https://plugins.gradle.org/m2/"
        }
        google()
        mavenCentral()
    }
}

... other stuff ...

App's build.gradle

In the app's build.gradle, add the following plugin:

apply plugin: 'com.amazonaws.appsync'

Add the following dependency:

implementation 'com.amazonaws:aws-android-sdk-appsync:3.4.1'

Sample app's build.gradle

apply plugin: 'com.android.application'
apply plugin: 'com.amazonaws.appsync'

android {
    // Typical items
}

dependencies {
    // Typical dependencies
    implementation 'com.amazonaws:aws-android-sdk-appsync:3.4.1'
}

App's AndroidManifest.xml

To determine if the device is offline, add permissions to access the network state:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Code generation

To interact with AppSync, your client needs to define GraphQL queries and mutations.

For example, create a file named ./app/src/main/graphql/com/amazonaws/demo/posts/posts.graphql:

query GetPost($id:ID!) {
    getPost(id:$id) {
        id
        title
        author
        content
        url
        version
    }
}

mutation AddPost($id: ID!, $author: String!, $title: String, $content: String, $url: String, $ups: Int!, $downs: Int!, $expectedVersion: Int!) {
    putPost(id: $id, author: $author, title: $title, content: $content, url: $url, ups: $ups, downs: $downs, version: $expectedVersion) {
        id
        title
        author
        url
        content
    }
}

Next, fetch the schema.json file from the AppSync console and place it alongside the posts.graphql file:

./app/src/main/graphql/com/amazonaws/demo/posts/schema.json

Now build the project and the generated source files will be available to use within the app. They will not show up in your source directory, but are added in the build path.

Create a client

Configuration via code

AWSAppSyncClient client = AWSAppSyncClient.builder()
    .context(context)
    .apiKey(new BasicAPIKeyAuthProvider(Constants.APPSYNC_API_KEY)) // API Key based authorization
    .region(Constants.APPSYNC_REGION)
    .serverUrl(Constants.APPSYNC_API_URL)
    .build();

Configuration via a config file

Alternatively, you can use the awsconfiguration.json file to supply the configuration information required to create a AWSAppSyncClient object.

Create a file named awsconfiguration.json under your app's res/raw directory.

{
    "AppSync": {
        "Default": {
            "ApiUrl": "YOUR-GRAPHQL-ENDPOINT",
            "Region": "us-east-1",
            "ApiKey": "YOUR-API-KEY",
            "AuthMode": "API_KEY"
        }
    }
}

The AWSConfiguration represents the configuration information present in awsconfiguration.json file. By default, the information under Default section will be used.

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

You can override the Default configuration by using the AWSConfiguration#setConfiguration() method.

{
    "AppSync": {
        "Default": {
            "ApiUrl": "YOUR-GRAPHQL-ENDPOINT",
            "Region": "us-east-1",
            "ApiKey": "YOUR-API-KEY",
            "AuthMode": "API_KEY"
        },
        "Custom": {
            "ApiUrl": "YOUR-GRAPHQL-ENDPOINT",
            "Region": "us-east-2",
            "ApiKey": "YOUR-API-KEY",
            "AuthMode": "API_KEY"
        }
   }
}
AWSConfiguration awsConfig = new AWSConfiguration(context);
awsConfig.setConfiguration("Custom");

AWSAppSyncClient client = AWSAppSyncClient.builder()
    .context(context)
    .awsConfiguration(awsConfig)
    .build();

Authentication Modes

When making calls to AWS AppSync, there are several ways to authenticate those calls. API key authorization (API_KEY) is the simplest way to onboard. After onboarding, we recommend you use one of the other modes:

  1. Amazon IAM (AWS_IAM)
  2. Amazon Cognito UserPools (AMAZON_COGNITO_USER_POOLS)
  3. Any OpenID Connect Provider (OPENID_CONNECT)

API Key

For authorization using the API key, update the awsconfiguration.json file and code snippet as follows:

Configuration

Add the following snippet to your awsconfiguration.json file.

{
    "AppSync": {
        "Default": {
            "ApiUrl": "YOUR-GRAPHQL-ENDPOINT",
            "Region": "us-east-1",
            "ApiKey": "YOUR-API-KEY",
            "AuthMode": "API_KEY"
        }
    }
}

Code

In order to use the information in the Default section from awsconfiguration.json file, add the following code:

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

AWS IAM

For authorization using Amazon IAM credentials, using Amazon IAM or Amazon STS or Amazon Cognito, update the awsconfiguration.json file and code snippet as follows:

Configuration

Add the following snippet to your awsconfiguration.json file.

{
    "CredentialsProvider": {
        "CognitoIdentity": {
            "Default": {
                "PoolId": "YOUR-COGNITO-IDENTITY-POOLID",
                "Region": "us-east-1"
            }
        }
    },
    "AppSync": {
        "Default": {
            "ApiUrl": "YOUR-GRAPHQL-ENDPOINT",
            "Region": "us-east-1",
            "AuthMode": "AWS_IAM"
       }
    }
}

Code

Add the following code to use the information in the Default section from awsconfiguration.json file.

AWSConfiguration awsConfig = new AWSConfiguration(context);

CognitoCachingCredentialsProvider credentialsProvider =
    new CognitoCachingCredentialsProvider(context, awsConfig);

AWSAppSyncClient client = AWSAppSyncClient.builder()
    .context(context)
    .awsConfiguration(awsConfig)
    .credentialsProvider(credentialsProvider)
    .build();

Amazon Cognito UserPools

For authorization using the Amazon Cognito UserPools, update the awsconfiguration.json file and code snippet as follows:

Configuration

Add the following snippet to your awsconfiguration.json file.

{
    "CognitoUserPool": {
        "Default": {
            "PoolId": "POOL-ID",
            "AppClientId": "APP-CLIENT-ID",
            "AppClientSecret": "APP-CLIENT-SECRET",
            "Region": "us-east-1"
        }
    },
    "AppSync": {
        "Default": {
            "ApiUrl": "YOUR-GRAPHQL-ENDPOINT",
            "Region": "us-east-1",
            "AuthMode": "AMAZON_COGNITO_USER_POOLS"
        }
    }
}

Code

Add the following dependency to your app in order to use Amazon Cognito UserPools:

dependencies {
    implementation 'com.amazonaws:aws-android-sdk-cognitoidentityprovider:2.16.12'
}

Add the following code to use the information in the Default section from awsconfiguration.json file.

AWSConfiguration awsConfig = new AWSConfiguration(context);

CognitoUserPool cognitoUserPool = new CognitoUserPool(context, awsConfig);
BasicCognitoUserPoolsAuthProvider basicCognitoUserPoolsAuthProvider =
    new BasicCognitoUserPoolsAuthProvider(cognitoUserPool);

AWSAppSyncClient awsAppSyncClient = AWSAppSyncClient.builder()
    .context(context)
    .awsConfiguration(awsConfig)
    .cognitoUserPoolsAuthProvider(basicCognitoUserPoolsAuthProvider)
    .build();

OIDC (OpenID Connect)

For authorization using any OIDC (OpenID Connect) Identity Provider, update the awsconfiguration.json file and code snippet as follows:

Configuration

Add the following snippet to your awsconfiguration.json file.

{
    "AppSync": {
        "Default": {
            "ApiUrl": "YOUR-GRAPHQL-ENDPOINT",
            "Region": "us-east-1",
            "AuthMode": "OPENID_CONNECT"
        }
    }
}

Code

Add the following code to use the information in the Default section from awsconfiguration.json file.

AWSAppSyncClient client = AWSAppSyncClient.builder()
    .context(context)
    .awsConfiguration(new AWSConfiguration(context))
    .oidcAuthProvider(() -> "jwt-token-from-oidc-provider")
    .build();

Make a call

public void addPost() {
    GraphQLCall.Callback<AddPostMutation.Data> postsCallback = new GraphQLCall.Callback<>() {
        @Override
        public void onResponse(Response<AddPostMutation.Data> response) {
            // Called on a non-UI thread
            runOnUiThread(() -> { /* do stuff on UI thread */ });
        }

        @Override
        public void onFailure(ApolloException failure) {
            // Error handling
        }
    };

    AddPostMutation addPostMutation = AddPostMutation.builder()
        .id(UUID.randomUUID().toString())
        .title(title)
        .author(author)
        .url(url)
        .content(content)
        .ups(0)
        .downs(0)
        .expectedVersion(1)
        .build();

    client.mutate(addPostMutation).enqueue(postsCallback);
}

License

This library is licensed under the Apache License 2.0.

aws-mobile-appsync-sdk-android's People

Contributors

alanvan0502 avatar ankpshah avatar baksansk avatar changxu0306 avatar desokroshan avatar div5yesh avatar frankmuellr avatar github-actions[bot] avatar gpanshu avatar jamesonwilliams avatar jpeddicord avatar jpignata avatar kevinold avatar majsty avatar mattcreaser avatar minbi avatar mslagle-godaddy avatar olga-dorogan avatar palpatim avatar peter-major-apadmi avatar raphkim avatar richardmcclellan avatar rjuliano avatar rohandubal avatar scb01 avatar sdhuka avatar sktimalsina avatar tjleing avatar tylerjroach avatar undefobj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aws-mobile-appsync-sdk-android's Issues

Mixed object in response when using the cache

In what version of SDK are you facing the problem? Can you try the latest version?

classpath 'com.amazonaws:aws-android-sdk-appsync-gradle-plugin:2.6.21'
implementation 'com.amazonaws:aws-android-sdk-appsync:2.6.21'

Is the issue limited to Simulators or Physical Devices?

Both

Is this problem related to a specific API level or Brand?

Tested only on SDK 27

Can you give us steps to reproduce with a minimal, complete, and verifiable example?

AppSync client:

val appSyncSqlHelper = AppSyncSqlHelper.create(ctx, DB_NAME)
val sqlCacheFactory = SqlNormalizedCacheFactory(appSyncSqlHelper)

// Store Mutations even if application close
val persistentMutationsCallback = object : PersistentMutationsCallback {
    override fun onResponse(response: PersistentMutationsResponse) {
        if (response.mutationClassName == "AddATMMutation") {
            Log.d("SUCCESS PERSISTENT M", response.dataJSONObject.toString())
        }
    }

    override fun onFailure(error: PersistentMutationsError) {
        Log.d("RES_ERROR PERSISTENT", error.mutationClassName)
    }
}

// Set AppSyncSigner
val appSyncSigV4SignerInterceptor = AppSyncSigV4SignerInterceptor(
    BasicAPIKeyAuthProvider(SharedPreferencesProvider.getAppSyncApiKey(ctx)),
    APPSYNC_API_REGION.getName()
)

// Change http client timeouts
val okHttpClient = OkHttpClient.Builder()
    .connectTimeout(HTTP_TIMEOUT, TimeUnit.SECONDS)
    .readTimeout(HTTP_TIMEOUT, TimeUnit.SECONDS)
    .writeTimeout(HTTP_TIMEOUT, TimeUnit.SECONDS)
    .addInterceptor(appSyncSigV4SignerInterceptor)
    .build()

// create appsync appSyncClient
appSyncClient = AWSAppSyncClient.builder()
    .context(ctx)
    .apiKey(BasicAPIKeyAuthProvider(SharedPreferencesProvider.getAppSyncApiKey(ctx)))
    .region(APPSYNC_API_REGION)
    .serverUrl(APPSYNC_API_URL)
    .normalizedCache(sqlCacheFactory)
    .okHttpClient(okHttpClient)
    .defaultResponseFetcher(AppSyncResponseFetchers.NETWORK_FIRST)
    .persistentMutationsCallback(persistentMutationsCallback)
    .build()

For exemple with

type Country{
    id: ID!
    tag: String!
}

type Color{
    id: ID!
    tag: String!
    hexValue: String!
}
  1. Load some data on multiple object with network
  2. Disable network and access again data. The data are mixed with other objects.

When we query Language, we have some country on the reply

This problem only occurs if we use the cache.

Thanks,
David

SDK doesn't upload Complex objects to S3 if the input object of a mutation has fields which are of type "S3ObjectInput"

The SDK uploads Complex objects to S3 only if the query document has the text "S3ObjectInput" [1].

However, the text "S3ObjectInput" will be only present in the query document only if the input object of the mutation is of type "S3ObjectInput".

mutation ($obj: S3ObjectInput!){
  putUser(obj: $obj){
      bucket
      region
      key
  }
}

The query document will not have the word "S3ObjectInput" in cases like the following. Here, UserInput has another object called pic which is of type 'S3ObjectInput`.

mutation ($user: UserInput!){
  putUser(user: $user){
    user
    email
    pic {
      bucket
      region
      key
    }
  }
}

The getS3ComplexObject(..) function checks for these kind of cases recursively [2]. However, due to the above issue, this part of the code never gets executed. Also, it checks for "S3ObjectInput" objects only inside a Map and doesn't check inside List.

Subscriptions with IAM does not work propely

I'm using IAM authentication (access key and secret key), so that users can share their locations in real-time. For that, I created a subscription that works fine in queries console of AppSync. But when I implemented it in my app, it does not work properly.

Here is a use case :

  1. user A access to the map, see his location, and a subscription is opened to locations
  2. user B does the same thing
  3. user A send his last location
  4. user B receive it, and user A appear on the user B map
  5. user B send his last location, but user A never receive it

Sometimes if user A leave the map and come back to it, with a subscription newly opened, he receive the last user B location sent after that subscription, and after that no location is received even if user B continue to send his locations.

Is there a recommended proguard configuration?

To help us solve your problem better, please answer the following list of questions.

In what version of SDK are you facing the problem? Can you try the latest version?

2.6.22

Is the issue limited to Simulators or Physical Devices?

Both

Is this problem related to a specific API level or Brand?

No

Can you give us steps to reproduce with a minimal, complete, and verifiable example?

Please include any specific network conditions that might be required to reproduce the problem.

I tried to run my application using proguard configuration given in https://github.com/aws/aws-sdk-android/blob/master/Proguard.md.
But I got some errors which I've put in the stacktrace.

Is there a recommended proguard file I can reference when integrating AppSync?

If my code snippet looks suspicious, please let me know.

Thanks in advance

proguard.pro

# Class names are needed in reflection
-keepnames class com.amazonaws.**
-keepnames class com.amazon.**
# Request handlers defined in request.handlers
-keep class com.amazonaws.services.**.*Handler
# The following are referenced but aren't required to run
-dontwarn com.fasterxml.jackson.**
-dontwarn org.apache.commons.logging.**
# Android 6.0 release removes support for the Apache HTTP client
-dontwarn org.apache.http.**
# The SDK has several references of Apache HTTP client
-dontwarn com.amazonaws.http.**
-dontwarn com.amazonaws.metrics.**

build.gradle


android {

   buildType {
      ...
      release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
   }

   dependencies {
       implementation "com.amazonaws:aws-android-sdk-appsync:2.6.22"
       implementation "org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.0"
       implementation "org.eclipse.paho:org.eclipse.paho.android.service:1.1.1"
       ...
   }
}

Please include a stacktrace if applicable.

Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find superclass or interface com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.AuthenticationHandler
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.PutObjectRequest
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.PutObjectRequest
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.ObjectMetadata
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.ObjectMetadata
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.ObjectMetadata
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.PutObjectRequest
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.PutObjectRequest
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.AmazonS3Client
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.AmazonS3Client
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.GetObjectRequest
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.GetObjectRequest
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.AmazonS3Client
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.AmazonS3Client
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.AmazonS3Client
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.AmazonS3Client
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.PutObjectRequest
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.ObjectMetadata
Warning: com.amazonaws.mobileconnectors.appsync.S3ObjectManagerImplementation: can't find referenced class com.amazonaws.services.s3.model.GetObjectRequest
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserPool
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserPool
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserPool
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserPool
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserPool
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.AuthenticationHandler
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.tokens.CognitoAccessToken
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.tokens.CognitoAccessToken
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoDevice
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoDevice
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.AuthenticationContinuation
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.AuthenticationContinuation
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.MultiFactorAuthenticationContinuation
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.MultiFactorAuthenticationContinuation
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.ChallengeContinuation
Warning: com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider$1: can't find referenced class com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.ChallengeContinuation

How to receive pending subscription on Android app, when it restarts after a force close?

I am using AppSync version 2.6.+ . I checked , when I force close the app and a subscription is triggered, it is not received by the app. This is because my app was force closed or was completely closed.

Now when I restart my app, I still do not get that pending subscription. Does it mean, if I miss a subscription, I will never receive it ? The only way is to manually query for that particular table and update my data.

Thanks in Advance.

http fails to execute after offline mutation

steps:
offline mutation
turn internet on
mutation fails

sdk version: 2.6.25 (latest)
device type: emulator

// appsync client instance
public static AWSAppSyncClient getInstance(final Activity context) {
if(client == null){
            client = AWSAppSyncClient.builder()
                    .context(context)
                    .cognitoUserPoolsAuthProvider(getUserPoolAuthProvider(context))
                    .region(Constants.APPSYNC_REGION)
                    .serverUrl(Constants.APPSYNC_API_URL)
                    .persistentMutationsCallback(persistentMutationCallback)
                    .build();
}
}
// mutation call
public void createCall() {

// other code
        Log.d(TAG, "create call");
        AppSyncFactory.getInstance(activity).mutate(createCallMutation, expected)
                .enqueue(new GraphQLCall.Callback<CreateCallMutation.Data>() {
                    @Override
                    public void onResponse(@Nonnull final Response<CreateCallMutation.Data> response) {
                        Log.d(TAG, "success");
                    }
                    @Override
                    public void onFailure(@Nonnull ApolloException e) {
                        Log.e(TAG, "Failed to make call", e);
                        
                    }
                });
    }

Please include a stacktrace if applicable.

D/AppSync: Internet connected.
D/AppSync: Checking if I need to process next originalMutation
D/AppSync: First check: Internet Available
D/AppSync: Second check: Persistent mutations queue is EMPTY!
D/AppSync: Processing next in queue: INMEMORY.
D/AppSync: Handling offline originalMutation.
D/AppSync: onFetch()
D/AppSync: onFailure()Failed to execute http call
D/AppSync: Got message to take action regarding mutation queue.
D/AppSync: Got message that a originalMutation process errored out.
D/AppSync: Checking if I need to process next originalMutation
D/AppSync: First check: Internet Available
D/AppSync: Second check: Persistent mutations queue is EMPTY!
D/AppSync: Third check: Inmemory mutations queue is EMPTY!
                          
 com.apollographql.apollo.exception.ApolloNetworkException: Failed to execute http call
                                                                                       at com.apollographql.apollo.internal.interceptor.ApolloServerInterceptor$1$1.onFailure(ApolloServerInterceptor.java:105)
                                                                                       at okhttp3.RealCall$AsyncCall.execute(RealCall.java:148)
                                                                                       at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
                                                                                       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
                                                                                       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
                                                                                       at java.lang.Thread.run(Thread.java:818)
                                                                                    Caused by: java.net.SocketTimeoutException: timeout
                                                                                       at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException(Http2Stream.java:593)
                                                                                       at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut(Http2Stream.java:601)
                                                                                       at okhttp3.internal.http2.Http2Stream.takeResponseHeaders(Http2Stream.java:146)
                                                                                       at okhttp3.internal.http2.Http2Codec.readResponseHeaders(Http2Codec.java:120)
                                                                                       at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:75)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
                                                                                       at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
                                                                                       at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
                                                                                       at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
                                                                                       at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
                                                                                       at com.amazonaws.mobileconnectors.appsync.sigv4.AppSyncSigV4SignerInterceptor.intercept(AppSyncSigV4SignerInterceptor.java:165)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
                                                                                       at com.amazonaws.mobileconnectors.appsync.retry.RetryInterceptor.intercept(RetryInterceptor.java:43)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
                                                                                       at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
                                                                                       at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
                                                                                       at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
                                                                                       at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 
                                                                                       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
                                                                                       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
                                                                                       at java.lang.Thread.run(Thread.java:818) 

Authentication stops working when OkHttpClient is added

When building a AWSAppSyncClient if a OkHttpClient is provided to the builder, authentication will stop working.

Example:

val okHttpClient = OkHttpClient.Builder().addNetworkInterceptor {
    val request = it.request()
    Log.d("Request", request.toString())
    val response = it.proceed(request)
    Log.d("Response", response.toString())
    response
}.build()

client = AWSAppSyncClient.builder().context(applicationContext)
        .okHttpClient(okHttpClient)
        .apiKey(BasicAPIKeyAuthProvider("my-key"))
        .region(Regions.US_EAST_2)
        .serverUrl("my-url")
        .build()

Looking at the AWSAppSyncClient this part seems to be the issue:

AppSyncSigV4SignerInterceptor appSyncSigV4SignerInterceptor = null;

// appSyncSigV4SignerInterceptor init code excluded...

OkHttpClient okHttpClient = builder.mOkHttpClient;

if (okHttpClient == null) {
    okHttpClient = new OkHttpClient.Builder()
    .addInterceptor(appSyncSigV4SignerInterceptor)
    .build();
}

I think the authentication interceptor should also be added if okHttpClient is not null.
Is it ok if I send a PR?

How to handle multiple subscriptions in Android?

State your question
How to handle multiple subscriptions in Android? Can someone post working snippet? I get working only first initialized subscription in application.
I'm not using API key authorization.

Provide code snippets (if applicable)

Subscription2 sub2 = Subscription2.builder().build();

sub1Watcher = appSyncClient.subscribe(sub1);
sub2Watcher = appSyncClient.subscribe(sub2);

sub1Watcher.execute(sub1Callback);
sub2Watcher.execute(sub2Callback);

Everytime only sub1 is executed.

Environment(please complete the following information):

  • AppSync SDK Version: 2.6.25

Device Information (please complete the following information):

  • Device: Simulator

Thanks.

Queued mutations are lost on network exception

Description
It turns out that AppSync on Android "looses" the mutations after the network exception.

To Reproduce

  1. A couple of mutations were successfully queued while device had no connection to any network
  2. Device was connected to the wifi network with no internet connection
  3. AppSync SDK detected the connection and tried to synchronize the mutations and that expectedly failed
  4. Wifi was turned off and mobile internet (cellular connection) was turned on

Result: none of the queued (and failed on step 3) mutations were executed after that, the data was lost.

Expected behavior

  1. Events are stored in the way they are queued. If mutations A, B and C are queued and mutation A fails to be synced, mutation B synchronization should not even start (but it did actually)
  2. Failed mutations should not be lost (they have to be retried)

Logs

Mutation failure (step 3)

2018-12-14 15:05:16.939 5692-18037/com.ehawk.ehawktracker E/SaveEventOperations_AS: Mutation CreateEvent failed, op id: 49d603320ce837d012619193ffb100e60bb4a6fd0e66345e8cd2341ac729b185, input: {id=afe67eb2-e0ed-4064-bfdd-afd3b09a852b, caseId=5678-chorniy-11635e63-5674-4018-a08d-aad42fd9f02e, offenderId=5678-chorniy-b49b6766-9da0-4d03-bea5-72e9188a8e3d, time=1.544792706797E9, type=ServiceStateChange, agencyId=agency, serviceName=LocationService, enabledProviders=["GPS","NETWORK"]}
    com.apollographql.apollo.exception.ApolloNetworkException: Failed to execute http call
        at com.apollographql.apollo.internal.interceptor.ApolloServerInterceptor$1$1.onFailure(ApolloServerInterceptor.java:105)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:148)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
        at java.lang.Thread.run(Thread.java:760)
     Caused by: java.net.SocketTimeoutException: timeout
        at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException(Http2Stream.java:593)
        at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut(Http2Stream.java:601)
        at okhttp3.internal.http2.Http2Stream.takeResponseHeaders(Http2Stream.java:146)
        at okhttp3.internal.http2.Http2Codec.readResponseHeaders(Http2Codec.java:120)
        at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:75)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
        at com.amazonaws.mobileconnectors.appsync.sigv4.AppSyncSigV4SignerInterceptor.intercept(AppSyncSigV4SignerInterceptor.java:181)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
        at com.amazonaws.mobileconnectors.appsync.retry.RetryInterceptor.intercept(RetryInterceptor.java:50)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) 
        at java.lang.Thread.run(Thread.java:760) 

Environment:

Device Information:

  • Device: Xiaomi MI5s
  • Android Version: 7.0

MqttService keep working even if subscription is cancelled

Hi,

When I cancel subscription on changing activity, why MqttService keep working with the following logs :

05-02 10:48:36.084 13552-3743 D/AlarmPingSender: Success. Release lock(MqttService.client.ndbexhommzflhkswq7nlvxysk4):1525250916084 05-02 10:48:38.562 13552-13552 D/AlarmPingSender: Sending Ping at:1525250918562 05-02 10:48:38.577 13552-13552 D/AlarmPingSender: Schedule next alarm at 1525250923577 Alarm scheule using setExactAndAllowWhileIdle, next: 5000

Thx in advance for your answer.

RXJava support

Hi,

Does appsync sdk support RxJava like Apollo?
I experimented adapting the Rx2Apollo.java class to AppSync and I got the queries part working but not mutation.

So does AppSync properly support RxJava?

Thanks

Subscriptions Not Triggering Sometimes Even Though There Is Stable Internet

We are using the SDK for an android project. When the mutations are triggered from backend (Lambdas, APIs, etc.) the subscriptions are not getting invoked sometimes even though the internet connection is stable. After 3-4 retries, it will get invoked

Expected behaviour
The subscriptions should trigger in any case if there is a stable internet connection

Environment:

  • AppSync SDK Version: 2.6.26

Device Information:

  • Device: Samsung J8, Redmi Note 5 Pro
  • Android Version: Marshmallow 6.0, Oreo 8.1

Thanks in advance

RealSubscriptionManager: No listeners for message

Describe the bug
W/RealSubscriptionManager: No listeners for message: {"data":{"onUpda......
Callback does not work properly.

Code:
OnCreateMessageSubscription onCreateMessageSubscription = OnCreateMessageSubscription
.builder()
.build();
onCreateMessageWatcher = appSyncClient.subscribe(onCreateMessageSubscription); onCreateMessageWatcher.execute(new AppSyncSubscriptionCall.Callback.....);

When my app turn to background, I shutdown the subscription.
onCreateMessageWatcher.cancel();
onCreateMessageWatcher = null;

And when I resumed app, I regisitered the watcher again.

Do it like this as soon as possiable several times.
This subscription can't bring the messages to my callback any more.

Environment(please complete the following information):

  • AppSync SDK Version: [2.6.28]

Support for OpenID Connect

Hi,

I noticed OpenID Connect now shows up as a new authorization method for AppSync on the dev guide here, wondering if there is an ETA on this new feature.

Thanks

Memory Leak from the AWSAppSyncClient

What happened:
When working off from the official sample app, aws-mobile-appsync-events-starter-android,
I kept getting memory leaks from the AWSAppSyncClient instance,
which is declared as a static volatile variable in ClientFactory.java.

In com.amazonaws.postsapp:1.0:1.
* com.amazonaws.postsapp.PostsActivity has leaked:
* GC ROOT static com.amazonaws.postsapp.ClientFactory.client
* references com.amazonaws.mobileconnectors.appsync.AWSAppSyncClient.mApolloClient
* references com.apollographql.apollo.ApolloClient.subscriptionManager
* references com.amazonaws.mobileconnectors.appsync.subscription.RealSubscriptionManager.subscriptionsById
* references java.util.HashMap.table
* references array java.util.HashMap$HashMapEntry[].[11]
* references java.util.HashMap$HashMapEntry.value
* references com.amazonaws.mobileconnectors.appsync.subscription.SubscriptionObject.listeners
* references java.util.HashSet.backingMap
* references java.util.HashMap.table
* references array java.util.HashMap$HashMapEntry[].[0]
* references java.util.HashMap$HashMapEntry.key
* references com.amazonaws.postsapp.PostsActivity$3.this$0 (anonymous implementation of com.amazonaws.mobileconnectors.appsync.AppSyncSubscriptionCall$Callback)
* leaks com.amazonaws.postsapp.PostsActivity instance

AppSync SDK Versions tried : 2.6.16 & 2.6.19

What I have tried:

  • Setting the AWSAppSyncClient to null on Activity's onDestroy method.
    --> This solved the memory leak from the AwsAppSyncClient instance.
    --> But I got more memory leaks from
    1. AppSyncOfflineMutationManager, which references mContext
In com.amazonaws.postsapp:1.0:1.
* com.amazonaws.postsapp.PostsActivity has leaked:
* GC ROOT android.os.HandlerThread.<Java Local>
* references com.amazonaws.mobileconnectors.appsync.AppSyncOfflineMutationManager$NetworkUpdateHandler.this$0
* references com.amazonaws.mobileconnectors.appsync.AppSyncOfflineMutationManager.mContext
* leaks com.amazonaws.postsapp.PostsActivity instance
  1. ThreadPoolExecutor in ApolloClient.Builder's SubscriptionManager, which references Activity

(I guess I can pass in a custom Executor when instantiating an AWSAppSyncClient instance, but I couldn't figure out quite how to do this)

In com.amazonaws.postsapp:1.0:1.
* com.amazonaws.postsapp.PostsActivity has leaked:
* GC ROOT thread java.lang.Thread.<Java Local> (named 'Apollo Dispatcher')
* references java.util.concurrent.ThreadPoolExecutor.threadFactory
* references com.apollographql.apollo.ApolloClient$Builder$1.this$0 (anonymous implementation of java.util.concurrent.ThreadFactory)
* references com.apollographql.apollo.ApolloClient$Builder.subscriptionManager
* references com.amazonaws.mobileconnectors.appsync.subscription.RealSubscriptionManager.subscriptionsById
* references java.util.HashMap.table
* references array java.util.HashMap$HashMapEntry[].[4]
* references java.util.HashMap$HashMapEntry.value
* references com.amazonaws.mobileconnectors.appsync.subscription.SubscriptionObject.listeners
* references java.util.HashSet.backingMap
* references java.util.HashMap.table
* references array java.util.HashMap$HashMapEntry[].[0]
* references java.util.HashMap$HashMapEntry.key
* references com.amazonaws.postsapp.PostsActivity$3.this$0 (anonymous implementation of com.amazonaws.mobileconnectors.appsync.AppSyncSubscriptionCall$Callback)
* leaks com.amazonaws.postsapp.PostsActivity instance

Some parts of the code in the SDK hold on to the references to the Context.

  • Most of these seem to come from the Handler or Thread related classes

I can't think of a good way to clear these references for the garbage collection to work correctly when the application is finished. Would making the SDK receive a WeakReference of Context be a solution to this problem?

If there are errors in the way I set up my project or my understanding of the code is incorrect,
please let me know.

How do you handle HTTP 403 ApolloParseInterceptor.java line 147

We seem to get lots of HTTP 403 at ApolloParseInterceptor.java line 147.

During sign-up / sign-in, we try to get auth token (we have few methods) and small portion of our users are getting HTTP 403.

  1. Is this normal?
  2. What can be the case - 99% of our clients are ok, but some have 403s

Here is an example of the same user (Galaxy S6) who experience this issue on few of the apis:
https://cl.ly/1f8711673c0f

Environment(please complete the following information):

  • AppSync SDK Version: 2.6.28

Device Information (please complete the following information):

  • Device: Can happen to all devices
  • Android Version: Happens to all Android OS
  • Specific to simulators: no

Subscription Gotchas

For those trying to get Subscriptions to work in their Android app and follow the sample app. The documentation leaves out some details (but are in the code which you can easily overlook!) that are essential to get Subscriptions to work in your app.

Note to AWS: Please update your description of the demo app and include a section on what to do to get subscriptions to work.

1. Manifest File
Check that you have the following permissions specified:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

Check that you have added the following service
<service android:name="org.eclipse.paho.android.service.MqttService" />

2. app.gradle
Add these libraries

 implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.0'
 implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'

Build fails with AppSync Gradle plugin version 2.6.18

The build is failing during the afterEvaluate phase.

. . .
09:44:27.476 [DEBUG] [org.gradle.internal.progress.DefaultBuildOperationExecutor] Completing Build operation 'Configure project :app'
09:44:27.476 [null] [org.gradle.internal.progress.DefaultBuildOperationExecutor]
09:44:27.476 [DEBUG] [org.gradle.internal.progress.DefaultBuildOperationExecutor] Completing Build operation 'Configure build'
09:44:27.478 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter]
09:44:27.478 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] FAILURE: Build failed with an exception.
09:44:27.478 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter]
09:44:27.478 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] * Where:
09:44:27.478 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] Build file '/Users/*******/******/appsync/AppSyncExperiment2/app/build.gradle' line: 2
09:44:27.478 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter]
09:44:27.479 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] * What went wrong:
09:44:27.479 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] A problem occurred evaluating project ':app'.
09:44:27.479 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] > com/moowork/gradle/node/NodePlugin
09:44:27.479 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter]
09:44:27.479 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] * Try:
09:44:27.479 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter]  Run with --scan to get full insights.
09:44:27.479 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter]
09:44:27.479 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] * Exception is:
09:44:27.480 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] org.gradle.api.GradleScriptException: A problem occurred evaluating project ':app'.
09:44:27.480 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter]   at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:92)
09:44:27.480 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter]   at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl$2.run(DefaultScriptPluginFactory.java:199)
09:44:27.480 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter]   at org.gradle.configuration.ProjectScriptTarget.addConfiguration(ProjectScriptTarget.java:77)
09:44:27.480 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter]   at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:204)
. . .

I think the recent change which made NodePlugin as compileOnly dependency in gradle is what causing the issue.

compileOnly "com.moowork.gradle:gradle-node-plugin:1.0.0"

Does the cached credentials renew automatically when creating mutation offline?

State your question
Login online
Add cached credentials provider for access to s3
Go offline
Create mutation with s3 upload
Wait until expiration
Go online
Does the cached credentials renew automatically and complete the mutation?
Or will the mutation fail?

Environment(please complete the following information):

  • AppSync SDK Version: 2.6.+

MqttSubscriptionClient: connection lost

Hi,

I'm facing a problem when using subscriptions with Android SDK. When I start my app subscriptions work well for some time (about 1 min). After this time connection is lost and not established any more. It seams that this happens always when I restart my app but I have not figured out a predictable behavior or why this happens.

With my JS client I don't have this issue. So I tried your sample app https://github.com/aws-samples/aws-mobile-appsync-events-starter-android and I have the same issue.

07-09 07:07:50.995 2734-3072/com.amazonaws.deepdish.postsgraphql D/AlarmPingSender: Schedule next alarm at 1531120071095
    Alarm scheule using setExactAndAllowWhileIdle, next: 100
07-09 07:07:51.332 2734-2734/com.amazonaws.deepdish.postsgraphql D/ViewEventActivity: Wrote comment to database
07-09 07:07:51.429 2734-2734/com.amazonaws.deepdish.postsgraphql D/MqttSubscriptionClient: connection lost
07-09 07:07:51.429 2734-2734/com.amazonaws.deepdish.postsgraphql D/ViewEventActivity: Subscription completed

I'm using the latest version of this SDK (2.6.21) but also faced the problem with earlier versions.

Thx in advance for your answer.

Mutation is not performed

Describe the bug
I am using the 2.8.4 Appsync and AWSMobileClient. I am running a mutation from a forground task in a timer interval of 1 minute to send a heartbeat to my server.

        SetPosStatusMutation mutation = SetPosStatusMutation
                .builder()
                .pos_status(PosStatusValue.ACTIVE)
                .build();

        appSyncClient.mutate(mutation).enqueue(setPosStatusCallback);

The Mutation is initiated but a network request is never performed and the callback is never called either.

I initialize the AppSyncClient with the following code:

AWSConfiguration awsConfig = new AWSConfiguration(this.context);
        awsConfig.setConfiguration(AWSMobileClient.getInstance().getConfiguration().getConfiguration());
appSyncClient = AWSAppSyncClient
            .builder()
            .context(this.context)
            .awsConfiguration(awsConfig)
            .cognitoUserPoolsAuthProvider(new CognitoUserPoolsAuthProvider() {
                @Override
                public String getLatestAuthToken() {
                try {
                    return AWSMobileClient.getInstance().getTokens().getIdToken().getTokenString();
                } catch (Exception e){
                    Log.e(TAG,"APPSYNC_ERROR", e);
                    return e.getLocalizedMessage();
                }
                }
            })
            .build();

The Log output:

12-06 10:06:22.754 16882-16966/pos.octobox.com.getsbyconnector D/AWSMobileClient: Inspecting user state details
12-06 10:06:22.794 16882-16966/pos.octobox.com.getsbyconnector D/AWSMobileClient: hasFederatedToken: true provider: cognito-idp.eu-central-1.amazonaws.com/eu-central-1_RETO432t8
12-06 10:06:22.794 16882-16966/pos.octobox.com.getsbyconnector D/GetsbyConnector: Current AWS Cognito user state: SIGNED_IN
12-06 10:06:22.794 16882-16966/pos.octobox.com.getsbyconnector D/AppSync: Checking if it is conflict mutation.
    Adding object: SetPosStatusMutation 
    
     {"query":"mutation setPosStatus($pos_status: PosStatusValue!) {  setPosStatus(pos_status: $pos_status) {    __typename    pos_status  }}","variables":{"pos_status":"ACTIVE"}}
12-06 10:06:22.814 16882-16966/pos.octobox.com.getsbyconnector D/AppSync: Created both in-memory and persistent records. Now checking queue.
    Checking if I need to process next originalMutation
    First check: Internet Available
    Second check: Persistent mutations queue is EMPTY!
    Processing next in queue: INMEMORY.

No further log output is produced.

Expected behavior
I would expect the mutation to be performed and the callback called.

Environment(please complete the following information):

    implementation 'com.amazonaws:aws-android-sdk-mobile-client:2.8.+'
    implementation 'com.amazonaws:aws-android-sdk-appsync:2.7.+'
    implementation 'com.amazonaws:aws-android-sdk-cognitoidentityprovider:2.8.+'
    implementation 'com.amazonaws:aws-android-sdk-s3:2.8.+'

Device Information (please complete the following information):

  • Device: Samsung Tablet
  • Android Version: 4.4.4

Subscriptions are closed when wifi is disabled even if there is a cellular connection.

Describe the bug
Mutation subscriptions are closed when wifi is disabled even if there is a cellular connection available. The "onCompleted" method of the subscription callback is called when wifi is disabled. And "re-subscribing" during the "onCompleted" method will result in a exception being thrown.

To Reproduce
Steps to reproduce the behavior:

  1. Start a subscription for a mutation.
  2. Turn WIFI off on the Android device.
  3. Note that the subscription callback "onCompleted" method is triggered. Indicating that the subscription has been canceled.

Expected behavior
Since there is still a connection available (cellular) I would expect that the subscription would stay active and continue. In general, I'm not sure why the subscription is canceled even if all connectivity is lost. Couldn't it hang around and wait till a connection is reestablished?

Environment(please complete the following information):

  • AppSync SDK Version: 2.6.26

Device Information (please complete the following information):

  • Device: Samsung S9
  • Android Version: Oreo 8.0

Offline persistent cache not working

I am following the steps by steps guide in the aws appsync, and i cloned the example repository.

In simulator

  1. Turn off mobile data
  2. Add event
  3. Back to the event list
  4. Refresh, new Event is not added

Am i missed out any settings?

Gradle plugin incompatible with Kotlin Android experiemental extensions

Describe the bug
When using Kotlin Android extensions experimental mode in the app project, with AppSync used in a library project, the build fails.

To Reproduce

  1. cd into attached project
  2. ./gradlew clean assembleDebug

Sample project
AppSyncLibraryProject.zip

Expected behavior
Build succeeds.

Actual behavior
Build fails with compilation errors such as the following:

AppSyncLibraryProject/library/build/generated/source/appsync/type/CreateTodoInput.java:3: error: package com.apollographql.apollo.api does not exist

Commenting-out the following lines from app/build.gradle fixes it:

androidExtensions {
    experimental = true
}

Environment:

  • AppSync SDK Version: 2.7.0
  • Kotlin 1.3.10
  • Android Gradle Plugin 3.3.0-rc01
  • Gradle 4.10.2 and 4.10.1

Additional context

Also posted here, in case it's a Kotlin bug: https://youtrack.jetbrains.com/issue/KT-28548

The app crashes when the sdk is launched

Describe the bug
The app crashes when the sdk is launched

Environment(please complete the following information):

  • AppSync SDK Version: 2.6.28

Device Information (please complete the following information):

  • Device: LLD-AL00 UNROOT
  • Android Version: Android 8.0.0, level 26
  • CPU Architecture: aarch64

Additional context
The bug only occurs when we use the HUAWEI device.

Here is the crash log:

2018-11-16 16:32:55.123 17910-17910/[Project Name] E/CrashReport: # LAUNCH TIME: 2018-11-16 16:32:47
2018-11-16 16:32:55.123 17910-17910/[Project Name] E/CrashReport: # CRASH TYPE: JAVA_CRASH
2018-11-16 16:32:55.123 17910-17910/[Project Name] E/CrashReport: # CRASH TIME: 2018-11-16 16:32:55
2018-11-16 16:32:55.124 17910-17910/[Project Name] E/CrashReport: # CRASH PROCESS: [Project Name]
2018-11-16 16:32:55.124 17910-17910/[Project Name] E/CrashReport: # CRASH THREAD: main
2018-11-16 16:32:55.124 17910-17910/[Project Name] E/CrashReport: # REPORT ID: a18ab2f7-8c1b-4d37-8046-01e8b65081e5
2018-11-16 16:32:55.124 17910-17910/[Project Name] E/CrashReport: # CRASH DEVICE: LLD-AL00 UNROOT
2018-11-16 16:32:55.125 17910-17910/[Project Name] E/CrashReport: # RUNTIME AVAIL RAM:1477627904 ROM:10434588672 SD:10413617152
2018-11-16 16:32:55.125 17910-17910/[Project Name] E/CrashReport: # RUNTIME TOTAL RAM:2916519936 ROM:25696387072 SD:25675415552
2018-11-16 16:32:55.125 17910-17910/[Project Name] E/CrashReport: # CRASH STACK:
2018-11-16 16:32:55.125 17910-17910/[Project Name] E/CrashReport: java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Out of memory
at java.lang.Thread.nativeCreate(Native Method)
at java.lang.Thread.start(Thread.java:753)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:970)
at java.util.concurrent.ThreadPoolExecutor.ensurePrestart(ThreadPoolExecutor.java:1611)
at java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(ScheduledThreadPoolExecutor.java:342)
at java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:562)
at java.util.concurrent.ScheduledThreadPoolExecutor.execute(ScheduledThreadPoolExecutor.java:654)
at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.start(ClientComms.java:675)
at org.eclipse.paho.client.mqttv3.internal.ClientComms.connect(ClientComms.java:280)
at org.eclipse.paho.client.mqttv3.internal.ConnectActionListener.connect(ConnectActionListener.java:185)
at org.eclipse.paho.client.mqttv3.MqttAsyncClient.connect(MqttAsyncClient.java:774)
at org.eclipse.paho.android.service.MqttConnection.connect(MqttConnection.java:295)
at org.eclipse.paho.android.service.MqttService.connect(MqttService.java:329)
at org.eclipse.paho.android.service.MqttAndroidClient.doConnect(MqttAndroidClient.java:467)
at org.eclipse.paho.android.service.MqttAndroidClient.access$200(MqttAndroidClient.java:76)
at org.eclipse.paho.android.service.MqttAndroidClient$MyServiceConnection.onServiceConnected(MqttAndroidClient.java:115)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1818)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1847)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:101)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7428)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)

How do you handle java.lang.IllegalStateException at RealAppSyncCall.java:350

We have a lot of IllegalStateException at RealAppSyncCall.java:350.

After user logs in, our app subscribes to the topic, run some queries and mutations.
First time we faced the issue when the same mutations were processed almost simultaneously, so we got rid of them by cancelling all previously started before starting new one.
But it helps only for this case

How can we handle the exception?

Here is an example of stack trace

Fatal Exception: java.lang.IllegalStateException
Expected: TERMINATED, but found [ACTIVE, CANCELED]
com.apollographql.apollo.internal.RealAppSyncCall.b (RealAppSyncCall.java:350)
com.apollographql.apollo.internal.RealAppSyncCall.a (RealAppSyncCall.java:69)
com.apollographql.apollo.internal.RealAppSyncCall$1.onFetch (RealAppSyncCall.java:278)
com.amazonaws.mobileconnectors.appsync.InterceptorCallback.onFetch (AppSyncOfflineMutationInterceptor.java:141)
com.apollographql.apollo.internal.interceptor.ApolloCacheInterceptor$1$1.onFetch (ApolloCacheInterceptor.java:115)
com.apollographql.apollo.internal.interceptor.ApolloParseInterceptor$1.onFetch (ApolloParseInterceptor.java:102)
com.apollographql.apollo.internal.interceptor.ApolloServerInterceptor$1.run (ApolloServerInterceptor.java:91)
java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1162)
java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:636)
java.lang.Thread.run (Thread.java:764)

Environment:

  • AppSync SDK Version: 2.6.28

Device Information:

  • Device: happens to all devices
  • Android Version: happens to all versions
  • Specific to simulators: No

Completion hander for S3ObjectManager.download()

To help us solve your problem better, please answer the following list of questions.

In what version of SDK are you facing the problem? Can you try the latest version?

AWS AppSync SDK version 2.6.16

Is the issue limited to Simulators or Physical Devices?

Both, I have an API question.

Is this problem related to a specific API level or Brand?

No.

Can you give us steps to reproduce with a minimal, complete, and verifiable example?

Please include any specific network conditions that might be required to reproduce the problem.

Not applicable.

Please include a stacktrace if applicable.

Not applicable.

Hi, I am wondering why in the Android SDK S3ObjectManager.download() doesn't seem to have a completion handler or callback so one can know when the file has finished downloading?

By way of comparison, in the iOS SDK seems to have a completion handler with success and error. See AWSS3ObjectProtocol.swift.

Perhaps I am missing something, almost no sample code anywhere that I can find for working with complex objects in an Android app and getting, for example, images into Glide or Picasso to show images.

Exception at AppSyncSigV4SignerInterceptor

In our production app, the below crash has been detected many times via Firebase Crashlytics. please help me.

Fatal Exception: com.amazonaws.AmazonClientException
Unable to execute HTTP request: Unable to resolve host "cognito-identity.us-east-1.amazonaws.com": No address associated with hostname
com.amazonaws.http.AmazonHttpClient.executeHelper (AmazonHttpClient.java:441)
com.amazonaws.http.AmazonHttpClient.execute (AmazonHttpClient.java:212)
com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient.invoke (AmazonCognitoIdentityClient.java:559)
com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient.getCredentialsForIdentity (AmazonCognitoIdentityClient.java:389)
com.amazonaws.auth.CognitoCredentialsProvider.populateCredentialsWithCognito (CognitoCredentialsProvider.java:748)
com.amazonaws.auth.CognitoCredentialsProvider.startSession (CognitoCredentialsProvider.java:674)
com.amazonaws.auth.CognitoCredentialsProvider.getCredentials (CognitoCredentialsProvider.java:445)
com.amazonaws.auth.CognitoCachingCredentialsProvider.getCredentials (CognitoCachingCredentialsProvider.java:485)
com.amazonaws.auth.CognitoCachingCredentialsProvider.getCredentials (CognitoCachingCredentialsProvider.java:77)
com.amazonaws.mobileconnectors.appsync.sigv4.AppSyncSigV4SignerInterceptor.intercept (AppSyncSigV4SignerInterceptor.java:123)
okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:92)
okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:67)
okhttp3.RealCall.getResponseWithInterceptorChain (RealCall.java:185)
okhttp3.RealCall$AsyncCall.execute (RealCall.java:135)
okhttp3.internal.NamedRunnable.run (NamedRunnable.java:32)
java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1133)
java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:607)
java.lang.Thread.run (Thread.java:761)

The below is our initialized code.

private AppSyncClient(Context context) {
        mContext = context.getApplicationContext();

        mCredentialsProvider = new CognitoCachingCredentialsProvider(
            context,
            Constants.COGNITO_IDENTITY,
            Constants.COGNITO_REGION
        );

        mAppSyncClient = AWSAppSyncClient.builder()
            .context(context)
            .credentialsProvider(mCredentialsProvider)
            .region(Constants.APPSYNC_REGION)
            .serverUrl(Constants.getAppSyncUrl())
            .build();

        mS3 = new AmazonS3Client(mCredentialsProvider);
        mS3.setRegion(Region.getRegion(Constants.S3_REGION));
    }

the SDK version is 2.6.17

Subscription is not canceled if user is logged out

I have a subscription running, which is working fine. I authenticate my user with cognito pools. When I sign-out my user I am calling subscriptionWatcher.cancel(); But the subscription still stays active. The output in the Log then is: RealSubscriptionManager: No listeners for message: {"data"....

This is potentially a security issue.

Attached the logcat output from the logout and then a subscription event.

07-31 15:53:15.189 6361-6426/by.gets.cashregisterexample D/AlarmPingSender: Success. Release lock(MqttService.client.k5wke5g44rbiroq5n635jktv6i):1533052395189
07-31 15:53:17.876 6361-6361/by.gets.cashregisterexample D/MainActivity: Logout successful: dev@...
07-31 15:53:17.877 6361-6361/by.gets.cashregisterexample D/AddedOrderCollection: Completed
07-31 15:53:17.877 6361-6361/by.gets.cashregisterexample D/AddedReceipt: Completed
07-31 15:53:20.167 6361-6361/by.gets.cashregisterexample D/AlarmPingSender: Sending Ping at:1533052400167
07-31 15:53:20.168 6361-6361/by.gets.cashregisterexample D/AlarmPingSender: Schedule next alarm at 1533052405168
    Alarm scheule using setExactAndAllowWhileIdle, next: 5000
07-31 15:53:20.192 6361-6426/by.gets.cashregisterexample D/AlarmPingSender: Success. Release lock(MqttService.client.k5wke5g44rbiroq5n635jktv6i):1533052400192
07-31 15:53:22.672 6361-6426/by.gets.cashregisterexample D/MqttSubscriptionClient: com.amazonaws.mobileconnectors.appsync.subscription.mqtt.MqttSubscriptionClient@67f6d16 transmit: true mqttL: com.amazonaws.mobileconnectors.appsync.subscription.mqtt.MqttSubscriptionClient$MessageListener@a1da87csubL: com.amazonaws.mobileconnectors.appsync.subscription.RealSubscriptionManager$2@3deb505 Topic: 539758482264/t32cxqtfibbt5dat3btonwp4je/addedOrderCollection/667fa970f2bc3dd6455c2091045461e9f909a5efaab7164c41255af131b458c0 Msg: {"data":{"addedOrderCollection":{"__typename":"OrderCollection","uuid":"cfb42766-4fca-416c-917b-d30c72395686","user_uuid":"36cca46f-17da-467c-a310-99ef3669fc5e","created_at":null,"table_uuid":"49cfaac0-dec9-4ad8-8c5d-58461fb644ec","app_user_uuid":"a5386302-cfac-4cad-9098-f97812e54f61","order_status":null,"orderItems":[{"__typename":"OrderItem","uuid":"8b6fb149-9b7c-4e0e-9793-c93b5372b713","order_collection_uuid":"cfb42766-4fca-416c-917b-d30c72395686","article_uuid":"6088d75d-f959-46f9-ba15-8dda10f0bc7b","comment":"","article_title":"0.33l Frucade","article_price":170}]}}}
07-31 15:53:22.672 6361-6426/by.gets.cashregisterexample W/RealSubscriptionManager: No listeners for message: {"data":{"addedOrderCollection":{"__typename":"OrderCollection","uuid":"cfb42766-4fca-416c-917b-d30c72395686","user_uuid":"36cca46f-17da-467c-a310-99ef3669fc5e","created_at":null,"table_uuid":"49cfaac0-dec9-4ad8-8c5d-58461fb644ec","app_user_uuid":"a5386302-cfac-4cad-9098-f97812e54f61","order_status":null,"orderItems":[{"__typename":"OrderItem","uuid":"8b6fb149-9b7c-4e0e-9793-c93b5372b713","order_collection_uuid":"cfb42766-4fca-416c-917b-d30c72395686","article_uuid":"6088d75d-f959-46f9-ba15-8dda10f0bc7b","comment":"","article_title":"0.33l Frucade","article_price":170}]}}} from topic: 539758482264/t32cxqtfibbt5dat3btonwp4je/addedOrderCollection/667fa970f2bc3dd6455c2091045461e9f909a5efaab7164c41255af131b458c0
07-31 15:53:25.170 6361-6361/by.gets.cashregisterexample D/AlarmPingSender: Sending Ping at:1533052405170
07-31 15:53:25.171 6361-6361/by.gets.cashregisterexample D/AlarmPingSender: Schedule next alarm at 1533052410171
    Alarm scheule using setExactAndAllowWhileIdle, next: 5000
07-31 15:53:25.250 6361-6426/by.gets.cashregisterexample D/AlarmPingSender: Success. Release lock(MqttService.client.k5wke5g44rbiroq5n635jktv6i):1533052405250
07-31 15:53:30.176 6361-6361/by.gets.cashregisterexample D/AlarmPingSender: Sending Ping at:1533052410176
07-31 15:53:30.178 6361-6361/by.gets.cashregisterexample D/AlarmPingSender: Schedule next alarm at 1533052415178
    Alarm scheule using setExactAndAllowWhileIdle, next: 5000

AppsyncMutationCall retry error in Appsync Android SDK

(Migrating this issue from the AWS Forums on behalf of forums user ep-mt)

Describe the bug
I'm developing a chat screen with AWS Appsync and I found an error or probably a bug when the AppsyncMutationCall fails.

I'm trying to send 4 messages (4 mutations) with my Android phone with none internet connection signal. Then AppsyncMutationCall fails throwing the following exception:

 E/ChatManager$$Lambda: com.apollographql.apollo.exception.ApolloNetworkException: Failed to execute http call
        at com.apollographql.apollo.internal.interceptor.ApolloServerInterceptor$1$1.onFailure(ApolloServerInterceptor.java:105)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:161)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: java.net.SocketTimeoutException: failed to connect to jpdpd773vrhnjnzojnxddagfli.appsync-api.us-east-1.amazonaws.com/13.33.131.80 (port 443) from /10.210.90.89 (port 47932) after 10000ms
        at libcore.io.IoBridge.connectErrno(IoBridge.java:185)
        at libcore.io.IoBridge.connect(IoBridge.java:130)
        at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:129)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:365)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:209)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:191)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:356)
        at java.net.Socket.connect(Socket.java:616)
        at okhttp3.internal.platform.AndroidPlatform.connectSocket(AndroidPlatform.java:71)
        at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:240)
        at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:160)
        at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257)
        at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
        at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at com.amazonaws.mobileconnectors.appsync.sigv4.AppSyncSigV4SignerInterceptor.intercept(AppSyncSigV4SignerInterceptor.java:165)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at com.amazonaws.mobileconnectors.appsync.retry.RetryInterceptor.intercept(RetryInterceptor.java:43)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:147)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 
        at java.lang.Thread.run(Thread.java:764) 

The problem is that when I turn on the WiFi and network connection is on again, my mutations are written in a wrong order. I think my app is trying to mutate every message at the same time instead of mutate the first and if success try the second, and the third in chain.Is there any way to chain mutations and maintain the order of my retries with the Appsync SDK?

To Reproduce
See above

Expected behavior
See above

Screenshots
N/A

ConcurrentModificationException

Describe the bug
When multiSubscription using, unexpectly exception happened.

To Reproduce
Steps to reproduce the behavior:
When net changed, easily happened

Expected behavior
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:851)
at java.util.HashMap$KeyIterator.next(HashMap.java:885)
at com.amazonaws.mobileconnectors.appsync.subscription.RealSubscriptionManager$1.onError(RealSubscriptionManager.java:196)
at com.amazonaws.mobileconnectors.appsync.subscription.mqtt.MqttSubscriptionClient$1.onFailure(MqttSubscriptionClient.java:85)
at org.eclipse.paho.android.service.MqttTokenAndroid.notifyFailure(MqttTokenAndroid.java:146)
at org.eclipse.paho.android.service.MqttAndroidClient.simpleAction(MqttAndroidClient.java:1501)
at org.eclipse.paho.android.service.MqttAndroidClient.connectAction(MqttAndroidClient.java:1439)
at org.eclipse.paho.android.service.MqttAndroidClient.onReceive(MqttAndroidClient.java:1368)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6242)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)

Environment(please complete the following information):

  • AppSync SDK Version: [2.6.+]

Device Information (please complete the following information):

  • Device: SHAPE AQUOS SH-M05
  • Android Version: [Nougat 7.1.2]

App crashes when subscriptions are cancelled

Hi,

In my app, I have an map activity in which users share their locations.
I use a subscription that starts in Activity.onResume() method and cancels in Activity.onPause().
If user leaves the activity and receives a new location from subscription at the same time, the app crashes in onResponse() method of AppSyncSubscriptionCall.Callback, because the view no longer exists.

Is there a way to prevend this ?

Mqtt subscriptions fail when bluetooth printer is connected

I am using appsync subscriptions on a android 5.0.2 device. They work when the device is just connected to the wlan. As soon as I connect a bluetooth printer to the device they fail. Normal queries/mutations work fine, just the subscriptions fail without any message.
When I restart the app after the printer is connected subscriptions work again.

I trying this with 'com.amazonaws:aws-android-sdk-appsync:2.6.25'

'compile' instead of 'implementation' complaint when building project

Hi I am seeing a message like following when I build my project using aws-mobile-appsync-sdk-android

Configuration 'compile' is obsolete and has been replaced with 'implementation' and 'api'.
It will be removed at the end of 2018.

This happens due to build.gradle file in aws-mobile-appsync-sdk-android

Is there a plan to replace 'compile' keyword with 'implementation' or 'api' keyword soon?

AppSync deletes mutation before calling http that leads to lost of data

Describe the bug
PersistentOfflineMutationManager.java deletes mutation even before successful http call leads to data loss

Expected behavior
mutation should only be deleted after a successful http call
deletion of mutation should be call after httpCall.enqueue on success response
should be retried on fail
.

Environment(please complete the following information):

  • AppSync SDK Version: 2.6.25+

Additional context
`
// PersistentOfflineMutationManager.java

public PersistentOfflineMutationObject removeAndGetLastInQueue() {
if (persistentOfflineMutationObjects.size() >= 1) {
PersistentOfflineMutationObject mutationObject = persistentOfflineMutationObjects.remove(0);
mutationSqlCacheOperations.deleteRecord(mutationObject.recordIdentifier); // <- deletes before http success
return mutationObject;
}
throw new IllegalStateException("Persistent Mutation Queue is empty. Cannot remove object.");
}`

Just one question

In the response of query, I do not get json. I just get the Response_datum object. I am unable to convert this to json. While the same response in iOS is being easily converted to json. I have not found a way to convert it to json.

Here is the query :
query GetHomeLayout($app_id : String!, $api_name : String!, $current_layout : String! ){
getHomeLayout(app_id: $app_id, api_name: $api_name, current_layout: $current_layout){
response_code
response_message
response_data {
background_color
transparency
font_color
menu_bar_title
total_rows
total_columns
image_overlay
current_layout
news_list{
news_id
news_title
news_date
display_news_date
news_content
news_url
}
image_overlay_text
show_grid_line
scroll_direction
show_menu_in_header
phone_image
tablet_image
croppable_image
}
}
}

In the callback, I get this object :
Response<GetHomeLayoutQuery.Data> response

How do I convert it to json ?

Also check the attached screenshot. Please let me know if you need anything else.

Aws version : ext.awsVersion = '2.6.+'

screenshot 2018-11-01 at 2 17 39 pm

Thanks

Getting SocketTimeOutException while executing query

I am getting SocketTimeOutException while executing query, instead in iOS platform it is working without any exception. I have tried to increase timeout as well but it din't help.

#Graphql Query

query getUserConversationConnectionThroughUser($after: String, $first: Int)
{
    me
    {
        id

        __typename
        conversations(first: $first, after: $after)
        {
            __typename
            nextToken
            userConversations
            {
                __typename
                userId
                conversationId

                associated
                {
                    __typename
                    userId
                }
                conversation
                {
                    __typename
                    id
                    name
                    privacy
                }
            }
        }
    }
}

#Code.

private void getAllConversationList() {
        if (mAWSAppSyncClient == null) {
            mAWSAppSyncClient = AppHelper.getInstance(getActivity());
        }
        mAWSAppSyncClient.query(GetUserConversationConnectionThroughUserQuery.builder().build())
                .responseFetcher(AppSyncResponseFetchers.CACHE_AND_NETWORK)
                .enqueue(userListCallback);
    }

#Callback.

    GraphQLCall.Callback<GetUserConversationConnectionThroughUserQuery.Data> userListCallback
            = new GraphQLCall.Callback<GetUserConversationConnectionThroughUserQuery.Data>() {
        @Override
        public void onResponse(@Nonnull Response<GetUserConversationConnectionThroughUserQuery.Data> response) {
            if (response.data() != null) {
                Log.d(TAG, "Check Items: " + response.data().toString());
            }
        }

        @Override
        public void onFailure(@Nonnull ApolloException e) {
            Log.e(TAG, "Failed to make api call", e);
            Log.e(TAG, e.getMessage());
        }
    };

#Logs.
MessageFragment: Failed to make api call

    com.apollographql.apollo.exception.ApolloNetworkException: Failed to execute http call
        at com.apollographql.apollo.internal.interceptor.ApolloServerInterceptor$1$1.onFailure(ApolloServerInterceptor.java:105)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:215)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
        at java.lang.Thread.run(Thread.java:760)
     Caused by: java.net.SocketTimeoutException: timeout
        at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException(Http2Stream.java:656)
        at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut(Http2Stream.java:664)
        at okhttp3.internal.http2.Http2Stream.takeHeaders(Http2Stream.java:153)
        at okhttp3.internal.http2.Http2Codec.readResponseHeaders(Http2Codec.java:131)
        at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at com.amazonaws.mobileconnectors.appsync.sigv4.AppSyncSigV4SignerInterceptor.intercept(AppSyncSigV4SignerInterceptor.java:181)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at com.amazonaws.mobileconnectors.appsync.retry.RetryInterceptor.intercept(RetryInterceptor.java:50)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:254)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:200)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) 
        at java.lang.Thread.run(Thread.java:760) 
    Failed to execute http call

Environment(please complete the following information):

  • AppSync SDK Version: [e.g. 2.7.+]
    classpath "com.amazonaws:aws-android-sdk-appsync-gradle-plugin:2.7.+"

**Device Information

  • Redmi 4, Moto g4, Samsung S7

Additional context
When I try to execute mutation and other queries, it is working as expected.

Implement callback to specify a custom retry policy

I am using AppSync with Cognito. The app I am building allows the user to make some mutations offline, even before the authentication is complete. Basically there is no need for the user to use the credentials, but those are auto-generated, and the app automatically sign-up and sign-in.

So, when the user starts the app for the first time, offline, and makes some mutations, as soon as the device goes online, the mutations will be sent to the cloud, but most of them are going to fail, cause the authentication is usually slower.

So my question is: can I prevent the mutations to be sent till the authentication is valid?

If that's not possible, is it possible to cancel all the enqueued mutations?

App crashes when app is running

App crashes when app is running especially when app come into foreground from background. I guess that the problem lies in the cached database. The cursor is not closed when executing the database operation. And here is the snapshot of the crash log.
488499a5-d644-4b8c-81fb-8a63693831fd

Mapping AWS built-in scalar types

Question

How does one map an AWS built-in scalar type? Currently, using AWS scalar types results in generated models with "stringly typed" fields, which are bad practise and undesirable for a variety of well understood reasons.

If the schema defines:

type Todo {
	id: ID!
	name: String!
	description: String
	created: AWSDateTime
}

How can we configure the code generator to output created with a date/time type of our choosing? Eg ZonedDateTime?

Since Apollo provides support for this functionality, I'd expect it to be available to AWS AppSync SDK users as well.

Environment

AppSync SDK 2.7.0
Kotlin 1.3.10
Android Studio 3.3 RC 1
Build #AI-182.5107.16.33.5138683, built on November 19, 2018
JRE: 1.8.0_152-release-1248-b01 x86_64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
macOS 10.13.6

Sample project

Attached is a sample project demonstrating how I'd expect this to work, based on the Apollo documentation:

AppSyncCustomTypes.zip

It does not compile, because the generated type is always a String.

One object for Query and Subscription?

State your question
I have query which returns PersonConnection type, subscription which is returning Person and mutation for update values. Below is the scheme.

Right now I have to use:

  1. OnUpdatePersonSubscription.Data.OnUpdatePerson
  2. UpdatePersonMutation.Data.UpdatePerson
  3. ListPersonsQuery.Data.listPersons.items

So right now when all fields for person are updated and subscription is fired. And I don't want to fetch data again, I just want to update current Person data in UI, I have to manually update every field.
As the mutation, query and subscription returns same return values (id, name, address), is it possible to generate android classes which are using same objects?

Code snippets:

AWS Appsync Console

type PersonConnection {
	items: [Person]
	nextToken: String
}

type Person {
	id: ID!
	name: String!
	address: String
}

listPersons(limit: Int, nextToken: String): PersonConnection

onUpdatePerson(id: String): Person
		@aws_subscribe(mutations: ["updatePerson"])

updatePerson(input: UpdatePersonInput!): Person

Android graphql file

query ListPersons($limit: Int, $nextToken: String) {
  listPersons(limit: $limit, nextToken: $nextToken) {
    items {
        id
        name
        address
    }
    nextToken
  }
}

mutation UpdatePerson($input: UpdatePersonInput!) {
    updatePerson(input:$input) {
        id
        name
        address
    }
}

subscription OnUpdatePerson($id: String!) {
  onUpdatePerson(id: $id) {
    id
    name
    address
  }
}

Environment:

  • AppSync SDK Version: 2.6.26

Getting nullpoint exception when calling mutation

Getting nullpoint exception when calling mutation with invalid id
I am having an issue with AppSync SDL. My application getting crashed when I try to update a value corresponding to an id which is not present in dynamo DB.

Environment:

AppSync SDK Version: 2.6.26

Device Information:

Device: Samsung J8, Redmi Note 5 Pro
Android Version: Marshmallow 6.0, Oreo 8.1

*Since the exception occur inside the SDK i am not able to control this situation
FATAL EXCEPTION: OkHttp Dispatcher Process: PID: 12394 java.lang.NullPointerException: Attempt to invoke interface method 'java.util.Set java.util.Map.entrySet()' on a null object reference at org.json.JSONObject.<init>(JSONObject.java:132) at com.amazonaws.mobileconnectors.appsync.InterceptorCallback.onResponse(AppSyncOfflineMutationInterceptor.java:106) at com.apollographql.apollo.internal.interceptor.ApolloCacheInterceptor$1$1.onResponse(ApolloCacheInterceptor.java:102) at com.apollographql.apollo.internal.interceptor.ApolloParseInterceptor$1.onResponse(ApolloParseInterceptor.java:84) at com.apollographql.apollo.internal.interceptor.ApolloServerInterceptor$1$1.onResponse(ApolloServerInterceptor.java:110) at okhttp3.RealCall$AsyncCall.execute(RealCall.java:153) at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) at java.lang.Thread.run(Thread.java:764)

App sync subscriptions doesn't return response if something is done when offline.

App sync subscriptions doesn't return response if changes are done in one device, but other device is offline and after sometime turned online.

In what version of SDK are you facing the problem? Can you try the latest version?

implementation 'com.amazonaws:aws-android-sdk-appsync:2.6.20'
implementation 'com.amazonaws:aws-android-sdk-appsync-compiler:2.6.20'
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.0'
implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'

Is the issue limited to Simulators or Physical Devices?

Physical device

Is this problem related to a specific API level or Brand?

no

using simple subscriptions, it works when both device are online, but will not return the previous changes to second device, done by first device when second was offline.

It just simply ignores the changes done if the device is offline, and when turned online it receives changes done at that time but not changes done when it was offine.

I think it doesn't queues the changes done but just works on the go.

private void subscribe() {
        PutPostSubsSubscription subscription = PutPostSubsSubscription.builder().build();

        subscriptionWatcher = ClientFactory.getInstance(this).subscribe(subscription);
        subscriptionWatcher.execute(subCallback);
    }

    private AppSyncSubscriptionCall.Callback subCallback = new AppSyncSubscriptionCall.Callback<PutPostSubsSubscription.Data>() {
        @Override
        public void onResponse(@Nonnull final Response<PutPostSubsSubscription.Data> response) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "SubsResponded", Toast.LENGTH_SHORT).show();
                    Log.e("SahajLOG", "ResponseSUBSSSSS**>> " +response.data());
                }
            });

            // Further code can update UI or act upon this new comment
        }

        @Override
        public void onFailure(@Nonnull ApolloException e) {
            Log.e("SahajLOG", "Error::>> " +e);
        }

        @Override
        public void onCompleted() {
            Log.d("Completed", "Completed");
        }
    };

When a conflict occurs and ConflictResolver is invoked, GraphQL.Callback is not called back

Describe the bug
As a dev, I should be able to fail in a conflict resolver and have that failure come back as an onFailure callback in GraphQL.Callback with a special code telling me the failure was do to a conflict.

To Reproduce
Steps to reproduce the behavior:

  1. Create a mutation in AppSync that will conditionally update based on expectedVersion. (i.e. there is a template for this)
  2. Execute the mutation on Android client using enqueue with GraphQL.Callback using an object in with an older version number.
  3. A conflict occurs on the AppSync backend as expected and and my custom ConflictResolver is invoked on the client.
  4. I fail the conflict.
  5. GraphQL.Callback is never called back. I have no way of telling my UI to stop waiting.

Expected behavior
GraphQL.Callback should be called back in a ConflictResolver failure scenario.

Environment(please complete the following information):

  • AppSync SDK Version: 2.6.26

Device Information (please complete the following information):

  • Device: Pixel XL API 28 Simulator

If subscriptionWatcher cancel too early. App is not opened

My Code like this
onCreate

subscriptionWatcher = ClientFactory.getInstance(mAuthProvider, mContext.getApplicationContext()).subscribe(subscription);
        subscriptionWatcher.execute(subscriptionCallback);

onDestroy
subscriptionWatcher.cancel();

When I reopen the APP after APP closed as soon as APP is opend
My App is no response.

simply

  1. open the App
  2. close the App Immediately.
  3. reopen the App
  4. No response.. just white.
  • I'm in Seoul And my Appsync Server is in Ireland.. So All query is slow

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.