Skip to content

Commit

Permalink
Add support for AWSConfiguration through awsconfiguration.json in AWS…
Browse files Browse the repository at this point in the history
…AppSyncClient
  • Loading branch information
Karthikeyan Vasuki Balasubramaniam authored and minbi committed Aug 11, 2018
1 parent bf158b4 commit 5d93e16
Show file tree
Hide file tree
Showing 10 changed files with 417 additions and 20 deletions.
6 changes: 0 additions & 6 deletions .idea/modules.xml

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

6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ android:
- tools
- platform-tools
- tools
- build-tools-26.0.2
- android-26
- build-tools-28.0.2
- android-28
- android-22
- sys-img-armeabi-v7a-android-22
script:
- android list target
- ./gradlew build connectedCheck
- ./gradlew build connectedCheck
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change Log - AWS AppSync SDK for Android

## [Release 2.6.25](https://github.com/awslabs/aws-mobile-appsync-sdk-android/releases/tag/release_v2.6.25)

### New Features
* Add support for `AWSConfiguration` through `awsconfiguration.json` in `AWSAppSyncClient`.

## [Release 2.6.24](https://github.com/awslabs/aws-mobile-appsync-sdk-android/releases/tag/release_v2.6.24)

### Bug Fixes
Expand Down
11 changes: 7 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
compileSdkVersion 27
compileSdkVersion 28
defaultConfig {
applicationId "com.amazonaws.mobileconnectors.appsync.demo"
minSdkVersion 15
targetSdkVersion 27
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Expand All @@ -18,13 +18,16 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
disable 'GradleDependency', 'GoogleAppIndexingWarning'
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation 'com.android.support:appcompat-v7:28.0.0-alpha3'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
package="com.amazonaws.mobileconnectors.appsync.demo">

<application
android:allowBackup="true"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
Expand Down
15 changes: 13 additions & 2 deletions aws-android-sdk-appsync/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ apply plugin: 'com.android.library'
apply from: rootProject.file('gradle-mvn-push.gradle')

android {
compileSdkVersion 27
compileSdkVersion 28

defaultConfig {
minSdkVersion 15
targetSdkVersion 26
targetSdkVersion 28
versionCode 1
versionName "1.0"

Expand All @@ -22,6 +22,9 @@ android {
}
}

lintOptions {
disable 'GradleDependency', 'HandlerLeak'
}
}

dependencies {
Expand All @@ -45,4 +48,12 @@ dependencies {

androidTestImplementation project(':aws-android-sdk-appsync-runtime')
androidTestImplementation project(':aws-android-sdk-appsync-api')

androidTestImplementation "com.amazonaws:aws-android-sdk-cognitoidentityprovider:$aws_version"
androidTestImplementation "org.mockito:mockito-core:2.19.1"

testImplementation "org.robolectric:robolectric:3.8"
testImplementation "org.mockito:mockito-core:2.19.1"
testImplementation "com.amazonaws:aws-android-sdk-cognitoidentityprovider:$aws_version"
testImplementation project(':aws-android-sdk-appsync-runtime')
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
import android.content.Context;

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.mobile.config.AWSConfiguration;
import com.amazonaws.mobileconnectors.appsync.cache.normalized.AppSyncStore;
import com.amazonaws.mobileconnectors.appsync.cache.normalized.sql.AppSyncSqlHelper;
import com.amazonaws.mobileconnectors.appsync.fetcher.AppSyncResponseFetchers;
import com.amazonaws.mobileconnectors.appsync.retry.RetryInterceptor;
import com.amazonaws.mobileconnectors.appsync.sigv4.APIKeyAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.AppSyncSigV4SignerInterceptor;
import com.amazonaws.mobileconnectors.appsync.sigv4.BasicAPIKeyAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.CognitoUserPoolsAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.OidcAuthProvider;
import com.amazonaws.mobileconnectors.appsync.subscription.RealSubscriptionManager;
Expand All @@ -48,8 +50,13 @@
import com.apollographql.apollo.internal.response.ScalarTypeAdapters;
import com.apollographql.apollo.internal.subscription.SubscriptionManager;

import org.json.JSONObject;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;

Expand All @@ -61,12 +68,55 @@
public class AWSAppSyncClient {
private static final String defaultSqlStoreName = "appsyncstore";
private static final String defaultMutationSqlStoreName = "appsyncstore_mutation";

private static final String TAG = AWSAppSyncClient.class.getSimpleName();

ApolloClient mApolloClient;
AppSyncStore mSyncStore;
private Context applicationContext;
S3ObjectManager mS3ObjectManager;
Map<Mutation, MutationInformation> mutationMap;

private enum AuthMode {
API_KEY("API_KEY"),
AWS_IAM("AWS_IAM"),
AMAZON_COGNITO_USER_POOLS("AMAZON_COGNITO_USER_POOLS"),
OPENID_CONNECT("OPENID_CONNECT");

private final String name;

AuthMode(String name) {
this.name = name;
}

/**
* @return the name of this AuthMode
*/
public String getName() {
return name;
}

/**
* Returns the AuthMode enum corresponding to the given AuthMode name.
*
* @param authModeName The name of the AuthMode. Ex.: API_KEY
* @return AuthMode enum representing the given AuthMode name.
*/
public static AuthMode fromName(String authModeName) {
for (final AuthMode authMode : AuthMode.values()) {
if (authModeName.equals(authMode.getName())) {
return authMode;
}
}
throw new IllegalArgumentException("Cannot create enum from " + authModeName + " value!");
}

@Override
public String toString() {
return name;
}
}

private AWSAppSyncClient(AWSAppSyncClient.Builder builder) {
applicationContext = builder.mContext.getApplicationContext();

Expand Down Expand Up @@ -164,6 +214,7 @@ public static class Builder {
NormalizedCacheFactory mNormalizedCacheFactory;
CacheKeyResolver mResolver;
ConflictResolverInterface mConflictResolver;
AWSConfiguration mAwsConfiguration;

// Apollo
String mServerUrl;
Expand Down Expand Up @@ -236,7 +287,7 @@ public Builder conflictResolver(ConflictResolverInterface conflictResolver) {
}

public <T> Builder addCustomTypeAdapter(ScalarType scalarType,
final CustomTypeAdapter<T> customTypeAdapter) {
final CustomTypeAdapter<T> customTypeAdapter) {
customTypeAdapters.put(scalarType, customTypeAdapter);
return this;
}
Expand Down Expand Up @@ -266,6 +317,39 @@ public Builder persistentMutationsCallback(PersistentMutationsCallback persisten
return this;
}

/**
* Read the AppSync section of the awsconfiguration.json file and populate the serverUrl and region
* variables from the ApiUrl and Region keys. The AuthMode specified in the file will be used to
* validate the Authentication mechanism chosen during the object construction.
*
* Example: API_KEY
*
* <p>
* "AppSync": {
* "Default": {
* "ApiUrl": "https://xxxx.appsync-api.<region>.amazonaws.com/graphql",
* "Region": "us-east-1",
* "ApiKey": "da2-yyyy",
* "AuthMode": "API_KEY"
* }
* }
* </p>
*
* <p>
* Usage:
* .awsConfiguration(new AWSConfiguration(getApplicationContext())
*
* </p>
* @param awsConfiguration The object representing the configuration
* information from awsconfiguration.json
*
* @return the builder object
*/
public Builder awsConfiguration(AWSConfiguration awsConfiguration) {
mAwsConfiguration = awsConfiguration;
return this;
}

public AWSAppSyncClient build() {
if (mNormalizedCacheFactory == null) {
AppSyncSqlHelper appSyncSqlHelper = AppSyncSqlHelper.create(mContext, defaultSqlStoreName);
Expand All @@ -274,6 +358,60 @@ public AWSAppSyncClient build() {
mNormalizedCacheFactory = new SqlNormalizedCacheFactory(appSyncSqlHelper);
}

// Read serverUrl, region and AuthMode from awsconfiguration.json if present
if (mAwsConfiguration != null) {
try {
// Populate the serverUrl and region from awsconfiguration.json
JSONObject appSyncJsonObject = mAwsConfiguration.optJsonObject("AppSync");
if (appSyncJsonObject == null) {
throw new RuntimeException("AppSync configuration is missing from awsconfiguration.json");
}

mServerUrl = appSyncJsonObject.getString("ApiUrl");
mRegion = Regions.fromName(appSyncJsonObject.getString("Region"));

Map<Object, AuthMode> authModeObjects = new HashMap<>();
authModeObjects.put(mApiKey, AuthMode.API_KEY);
authModeObjects.put(mCredentialsProvider, AuthMode.AWS_IAM);
authModeObjects.put(mCognitoUserPoolsAuthProvider, AuthMode.AMAZON_COGNITO_USER_POOLS);
authModeObjects.put(mOidcAuthProvider, AuthMode.OPENID_CONNECT);
authModeObjects.remove(null);

// Validate if only one Auth object is passed in to the builder
if (authModeObjects.size() > 1) {
throw new RuntimeException("More than one AuthMode has been passed in to the builder. " +
authModeObjects.values().toString() +
". Please pass in exactly one AuthMode into the builder.");
}

// Store references to the authMode object passed in to the builder and the
// corresponding AuthMode
Object selectedAuthModeObject = null;
AuthMode selectedAuthMode = null;
Iterator<Object> iterator = authModeObjects.keySet().iterator();
if (iterator.hasNext()) {
selectedAuthModeObject = iterator.next();
selectedAuthMode = authModeObjects.get(selectedAuthModeObject);
}

// Read the AuthMode and validate if the corresponding Auth object is passed in
// to the builder
final AuthMode authMode = AuthMode.fromName(appSyncJsonObject.getString("AuthMode"));
if (selectedAuthModeObject == null && authMode.equals(AuthMode.API_KEY)) {
mApiKey = new BasicAPIKeyAuthProvider(appSyncJsonObject.getString("ApiKey"));
selectedAuthMode = authMode;
}

// Validate if the AuthMode match
if (!authMode.equals(selectedAuthMode)) {
throw new RuntimeException("Found conflicting AuthMode. Should be " +
authMode.toString() + " but you selected " + selectedAuthMode.toString());
}
} catch (Exception exception) {
throw new RuntimeException("Please check the AppSync configuration in awsconfiguration.json.", exception);
}
}

if (mResolver == null) {
mResolver = new CacheKeyResolver() {
@Nonnull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,50 @@

package com.amazonaws.mobileconnectors.appsync.sigv4;

import android.util.Log;

import com.amazonaws.mobile.config.AWSConfiguration;

public class BasicAPIKeyAuthProvider implements APIKeyAuthProvider {
private final String apiKey;

private static final String TAG = BasicAPIKeyAuthProvider.class.getSimpleName();

public BasicAPIKeyAuthProvider(String apiKey) {
this.apiKey = apiKey;
}

/**
* Read the ApiKey from AppSync section of the awsconfiguration.json file.
*
* <p>
* "AppSync": {
* "Default": {
* "ApiUrl": "https://xxxxxxxxxxxxxxxx.appsync-api.<region>.amazonaws.com/graphql",
* "Region": "us-east-1",
* "ApiKey": "da2-yyyyyyyyyyyyyyyy",
* "AuthMode": "API_KEY"
* }
* }
* </p>
*
* <p>
* AWSConfiguration awsConfiguration = new AWSConfiguration(getApplicationContext();
* APIKeyAuthProvider apiKeyAuthProvider = new BasicAPIKeyAuthProvider(awsConfiguration);
* </p>
*
* @param awsConfiguration The object representing the configuration
* information from awsconfiguration.json
*/
public BasicAPIKeyAuthProvider(AWSConfiguration awsConfiguration) {
try {
this.apiKey = awsConfiguration.optJsonObject("AppSync").getString("ApiKey");
} catch (Exception exception) {
Log.e(TAG, "Please check the ApiKey passed from awsconfiguration.json.");
throw new RuntimeException("Please check the ApiKey passed from awsconfiguration.json.", exception);
}
}

@Override
public String getAPIKey() {
return apiKey;
Expand Down
Loading

0 comments on commit 5d93e16

Please sign in to comment.