diff --git a/appsync/aws-appsync-core-amplify/.gitignore b/appsync/aws-appsync-amplify/.gitignore similarity index 100% rename from appsync/aws-appsync-core-amplify/.gitignore rename to appsync/aws-appsync-amplify/.gitignore diff --git a/appsync/aws-appsync-core-amplify/build.gradle.kts b/appsync/aws-appsync-amplify/build.gradle.kts similarity index 96% rename from appsync/aws-appsync-core-amplify/build.gradle.kts rename to appsync/aws-appsync-amplify/build.gradle.kts index e9397fcc7e..88e6bd0e67 100644 --- a/appsync/aws-appsync-core-amplify/build.gradle.kts +++ b/appsync/aws-appsync-amplify/build.gradle.kts @@ -31,7 +31,7 @@ project.setProperty("VERSION_NAME", readVersion()) group = properties["POM_GROUP"].toString() android { - namespace = "com.amplifyframework.aws.appsync.core" + namespace = "com.amplifyframework.aws.appsync.amplify" defaultConfig { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/appsync/aws-appsync-amplify/gradle.properties b/appsync/aws-appsync-amplify/gradle.properties new file mode 100644 index 0000000000..2e25005862 --- /dev/null +++ b/appsync/aws-appsync-amplify/gradle.properties @@ -0,0 +1,4 @@ +POM_ARTIFACT_ID=aws-appsync-amplify +POM_NAME=Amplify Extensions for AWS AppSync +POM_DESCRIPTION=Amplify Extensions for AWS AppSync +POM_PACKAGING=aar \ No newline at end of file diff --git a/appsync/aws-appsync-core-amplify/src/main/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyIamAuthorizer.kt b/appsync/aws-appsync-amplify/src/main/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyIamAuthorizer.kt similarity index 100% rename from appsync/aws-appsync-core-amplify/src/main/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyIamAuthorizer.kt rename to appsync/aws-appsync-amplify/src/main/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyIamAuthorizer.kt diff --git a/appsync/aws-appsync-core-amplify/src/main/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyUserPoolAuthorizer.kt b/appsync/aws-appsync-amplify/src/main/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyUserPoolAuthorizer.kt similarity index 100% rename from appsync/aws-appsync-core-amplify/src/main/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyUserPoolAuthorizer.kt rename to appsync/aws-appsync-amplify/src/main/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyUserPoolAuthorizer.kt diff --git a/appsync/aws-appsync-core-amplify/src/main/java/com/amplifyframework/aws/appsync/core/util/AppSyncRequestSigner.kt b/appsync/aws-appsync-amplify/src/main/java/com/amplifyframework/aws/appsync/core/util/AppSyncRequestSigner.kt similarity index 100% rename from appsync/aws-appsync-core-amplify/src/main/java/com/amplifyframework/aws/appsync/core/util/AppSyncRequestSigner.kt rename to appsync/aws-appsync-amplify/src/main/java/com/amplifyframework/aws/appsync/core/util/AppSyncRequestSigner.kt diff --git a/appsync/aws-appsync-core-amplify/src/test/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyIamAuthorizerTest.kt b/appsync/aws-appsync-amplify/src/test/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyIamAuthorizerTest.kt similarity index 100% rename from appsync/aws-appsync-core-amplify/src/test/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyIamAuthorizerTest.kt rename to appsync/aws-appsync-amplify/src/test/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyIamAuthorizerTest.kt diff --git a/appsync/aws-appsync-core-amplify/src/test/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyUserPoolAuthorizerTest.kt b/appsync/aws-appsync-amplify/src/test/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyUserPoolAuthorizerTest.kt similarity index 100% rename from appsync/aws-appsync-core-amplify/src/test/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyUserPoolAuthorizerTest.kt rename to appsync/aws-appsync-amplify/src/test/java/com/amplifyframework/aws/appsync/core/authorizers/AmplifyUserPoolAuthorizerTest.kt diff --git a/appsync/aws-appsync-core-amplify/src/test/java/com/amplifyframework/aws/appsync/core/util/AppSyncRequestSignerTest.kt b/appsync/aws-appsync-amplify/src/test/java/com/amplifyframework/aws/appsync/core/util/AppSyncRequestSignerTest.kt similarity index 100% rename from appsync/aws-appsync-core-amplify/src/test/java/com/amplifyframework/aws/appsync/core/util/AppSyncRequestSignerTest.kt rename to appsync/aws-appsync-amplify/src/test/java/com/amplifyframework/aws/appsync/core/util/AppSyncRequestSignerTest.kt diff --git a/appsync/aws-appsync-core-amplify/gradle.properties b/appsync/aws-appsync-core-amplify/gradle.properties deleted file mode 100644 index aeb50c7e4b..0000000000 --- a/appsync/aws-appsync-core-amplify/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -POM_ARTIFACT_ID=aws-appsync-core-amplify -POM_NAME=AWS AppSync Core for Amplify Android -POM_DESCRIPTION=AWS AppSync Core for Amplify Android Library -POM_PACKAGING=aar \ No newline at end of file diff --git a/appsync/aws-appsync-events-amplify/.gitignore b/appsync/aws-appsync-events-amplify/.gitignore deleted file mode 100644 index 42afabfd2a..0000000000 --- a/appsync/aws-appsync-events-amplify/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/appsync/aws-appsync-events-amplify/build.gradle.kts b/appsync/aws-appsync-events-amplify/build.gradle.kts deleted file mode 100644 index caa20213f0..0000000000 --- a/appsync/aws-appsync-events-amplify/build.gradle.kts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import java.util.Properties - -plugins { - id("com.android.library") - id("kotlin-android") -} - -apply(from = rootProject.file("configuration/publishing.gradle")) - -fun readVersion() = Properties().run { - file("../version.properties").inputStream().use { load(it) } - get("VERSION_NAME").toString() -} - -project.setProperty("VERSION_NAME", readVersion()) -group = properties["POM_GROUP"].toString() - -android { - namespace = "com.amplifyframework.aws.appsync.events.amplify" - defaultConfig { - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - testOptions { - execution = "ANDROIDX_TEST_ORCHESTRATOR" - } -} - -dependencies { - api(project(":aws-appsync-events")) - - testImplementation(libs.test.junit) - testImplementation(libs.test.mockk) - testImplementation(libs.test.kotlin.coroutines) - testImplementation(libs.test.kotest.assertions) -} diff --git a/appsync/aws-appsync-events-amplify/gradle.properties b/appsync/aws-appsync-events-amplify/gradle.properties deleted file mode 100644 index bc0b3408e9..0000000000 --- a/appsync/aws-appsync-events-amplify/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -POM_ARTIFACT_ID=aws-appsync-events-amplify -POM_NAME=AWS AppSync Events for Amplify Android -POM_DESCRIPTION=AWS AppSync Events Library for Amplify Android -POM_PACKAGING=aar \ No newline at end of file diff --git a/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/Events.kt b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/Events.kt new file mode 100644 index 0000000000..1a7af0269c --- /dev/null +++ b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/Events.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.amplifyframework.aws.appsync.events + +import com.amplifyframework.aws.appsync.core.AppSyncAuthorizer +import com.amplifyframework.aws.appsync.events.data.ChannelAuthorizers +import com.amplifyframework.aws.appsync.events.data.EventsException +import com.amplifyframework.aws.appsync.events.data.PublishResult +import kotlinx.serialization.json.JsonElement + +/** + * The main class for interacting with AWS AppSync Events + * + * @property endpoint AWS AppSync Events endpoint. + * @param connectAuthorizer for AWS AppSync Websocket Pub/Sub connection. + * @param defaultChannelAuthorizers passed to created channels if not overridden. + */ +class Events( + val endpoint: String, + val connectAuthorizer: AppSyncAuthorizer, + val defaultChannelAuthorizers: ChannelAuthorizers +) { + + /** + * Publish a single event to a channel. + * + * @param channelName of the channel to publish to. + * @param event formatted in json. + * @param authorizer for the publish call. If not provided, the EventChannel publish authorizer will be used. + * @return result of publish. + */ + @Throws(EventsException::class) + suspend fun publish( + channelName: String, + event: JsonElement, + authorizer: AppSyncAuthorizer = this.defaultChannelAuthorizers.publishAuthorizer + ): PublishResult { + TODO("Need to implement") + } + + /** + * Publish a multiple events (up to 5) to a channel. + * + * @param channelName of the channel to publish to. + * @param events list of formatted json events. + * @param authorizer for the publish call. If not provided, the EventChannel publish authorizer will be used. + * @return result of publish. + */ + @Throws(EventsException::class) + suspend fun publish( + channelName: String, + events: List, + authorizer: AppSyncAuthorizer = this.defaultChannelAuthorizers.publishAuthorizer + ): PublishResult { + TODO("Need to implement") + } + + /** + * Create a channel. + * + * @param channelName of the channel to use. + * @param authorizers for the channel to use for subscriptions and publishes. + * @return a channel to manage subscriptions and publishes. + */ + @Throws(EventsException::class) + fun channel( + channelName: String, + authorizers: ChannelAuthorizers = this.defaultChannelAuthorizers, + ): EventsChannel { + TODO("Need to implement") + } + + /** + * Method to disconnect from all channels. + * + * @param flushEvents set to true (default) to allow all pending publish calls to succeed before disconnecting. + * Setting to false will immediately disconnect, cancelling any in-progress or queued event publishes. + * @param authorizers for the channel to use for subscriptions and publishes. + * @return a channel to manage subscriptions and publishes. + */ + @Throws(EventsException::class) + suspend fun disconnect(flushEvents: Boolean = true) { + TODO("Need to implement") + } +} diff --git a/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/EventsChannel.kt b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/EventsChannel.kt new file mode 100644 index 0000000000..cfe4eba15f --- /dev/null +++ b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/EventsChannel.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.amplifyframework.aws.appsync.events + +import com.amplifyframework.aws.appsync.core.AppSyncAuthorizer +import com.amplifyframework.aws.appsync.events.data.ChannelAuthorizers +import com.amplifyframework.aws.appsync.events.data.EventsException +import com.amplifyframework.aws.appsync.events.data.EventsMessage +import com.amplifyframework.aws.appsync.events.data.PublishResult +import kotlinx.coroutines.flow.Flow +import kotlinx.serialization.json.JsonElement + +/** + * A class to manage channel subscriptions and publishes + * + * @property name of the channel + * @property authorizers used for channel subscriptions and publishes + */ +class EventsChannel internal constructor( + val name: String, + val authorizers: ChannelAuthorizers +) { + + /** + * Subscribe to a channel. + * + * @param authorizer for the subscribe call. If not provided, the EventChannel subscribe authorizer will be used. + * @return flow of event messages. Collect flow to receive messages. + */ + @Throws(EventsException::class) + fun subscribe( + authorizer: AppSyncAuthorizer = this.authorizers.subscribeAuthorizer + ): Flow { + TODO("Need to implement") + } + + /** + * Publish a single event to a channel. + * + * @param event formatted in json. + * @param authorizer for the publish call. If not provided, the EventChannel publish authorizer will be used. + * @return result of publish. + */ + @Throws(EventsException::class) + suspend fun publish( + event: JsonElement, + authorizer: AppSyncAuthorizer = this.authorizers.publishAuthorizer + ): PublishResult { + TODO("Need to implement") + } + + /** + * Publish a multiple events (up to 5) to a channel. + * + * @param events list of formatted json events. + * @param authorizer for the publish call. If not provided, the EventChannel publish authorizer will be used. + * @return result of publish. + */ + @Throws(EventsException::class) + suspend fun publish( + events: List, + authorizer: AppSyncAuthorizer = this.authorizers.publishAuthorizer + ): PublishResult { + TODO("Need to implement") + } +} diff --git a/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/ChannelAuthorizers.kt b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/ChannelAuthorizers.kt new file mode 100644 index 0000000000..32e740a6dc --- /dev/null +++ b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/ChannelAuthorizers.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.amplifyframework.aws.appsync.events.data + +import com.amplifyframework.aws.appsync.core.AppSyncAuthorizer + +/** + * Authorizers passed to a channel to manage subscriptions and publishes. + * + * @property subscribeAuthorizer used for subscription requests. + * @property publishAuthorizer used for publish requests. + * @constructor Pass subscribe and publisher authorizer types. + */ +data class ChannelAuthorizers( + val subscribeAuthorizer: AppSyncAuthorizer, + val publishAuthorizer: AppSyncAuthorizer +) diff --git a/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/EventsException.kt b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/EventsException.kt new file mode 100644 index 0000000000..78034e574f --- /dev/null +++ b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/EventsException.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.amplifyframework.aws.appsync.events.data + +/** + * Base class for exceptions thrown in Events library + * + * @param message of the exception. + * @param cause of the exception. + * @param recoverySuggestion recommendation to resolve exception. + */ +open class EventsException internal constructor( + message: String, + cause: Throwable? = null, + val recoverySuggestion: String? = null +) : Exception(message, cause) + +/** + * Thrown when failing to connect to Events WebSocket. + */ +class ConnectException internal constructor(cause: Throwable?) : EventsException( + message = "Failed to connect to the Events Api", + cause = cause, + recoverySuggestion = "See the underlying exception for cause" +) + +/** + * Thrown when call is unauthorized. + */ +class UnauthorizedException internal constructor() : EventsException( + message = "Unauthorized", + recoverySuggestion = "Check your authorizer and Event configuration values and try again" +) + +/** + * Thrown when connection is unexpectedly closed. + */ +class ConnectionClosedException internal constructor(cause: Throwable? = null) : EventsException( + message = "The websocket connection was closed", + cause = cause, + recoverySuggestion = "Check your internet connection and try again" +) + +/** + * Thrown when rate limit is exceeded. + */ +internal class RateLimitExceededException internal constructor() : EventsException( + message = "Rate limit exceeded", + recoverySuggestion = "Try again later" +) + +/** + * Thrown when operation is unsupported. + */ +internal class UnsupportedOperationException internal constructor() : EventsException( + message = "WebSocket did not understand the operation", + recoverySuggestion = "This is not expected to occur. Contact AWS" +) + +/** + * Thrown when resource is not found. + */ +internal class ResourceNotFoundException internal constructor() : EventsException( + message = "Resource not found", + recoverySuggestion = "Check Event configuration values and try again" +) + +/** + * Thrown when hitting max subscription limit. + */ +class MaxSubscriptionsReachedException internal constructor(throwable: Throwable) : EventsException( + message = "Max number of subscriptions reached", + recoverySuggestion = "Unsubscribe from existing channels before attempting to subscribe." +) + +/** + * Thrown when attempting to send too many events or invalid request. + */ +class BadRequestException internal constructor() : EventsException( + message = "Input exceeded 5 event limit", + recoverySuggestion = "Submit 5 events or less." +) diff --git a/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/EventsMessage.kt b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/EventsMessage.kt new file mode 100644 index 0000000000..68a1ce735e --- /dev/null +++ b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/EventsMessage.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.amplifyframework.aws.appsync.events.data + +import kotlinx.serialization.json.JsonElement + +/** + * An event received through a subscription. + * + * @property data of the received event, formatted in json. + */ +data class EventsMessage(val data: JsonElement) \ No newline at end of file diff --git a/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/PublishResult.kt b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/PublishResult.kt new file mode 100644 index 0000000000..6b45cc7f72 --- /dev/null +++ b/appsync/aws-appsync-events/src/main/java/com/amplifyframework/aws/appsync/events/data/PublishResult.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.amplifyframework.aws.appsync.events.data + +/** + * Contains the result of an event(s) publish call. + * + * @property successfulEvents list of events successfully processed by AWS AppSync. + * @property failedEvents list of events that AWS AppSync failed to process. + * @property status of the publish call. + * Successful = All events published successfully + * Failed = All events failed to publish + * PartialSuccess = Mix of successful and failed events. Check event indexes to determine individual states. + */ +data class PublishResult internal constructor( + val successfulEvents: List, + val failedEvents: List +) { + + /** + * Contains identifying information of an event AWS AppSync failed to process. + */ + sealed class Status { + data object Successful: Status() + data object Failed: Status() + data object PartialSuccess: Status() + } + + val status: Status + get() { + return when { + successfulEvents.isNotEmpty() && failedEvents.isNotEmpty() -> Status.PartialSuccess + failedEvents.isNotEmpty() -> Status.Failed + else -> Status.Successful + } + } +} + +/** + * Contains identifying information of a successfully processed event. + * + * @property identifier identifier of event used for logging purposes. + * @property index of the event as it was sent in the publish. + */ +data class SuccessfulEvent internal constructor( + val identifier: String, + val index: Int +) + +/** + * Contains identifying information of an event AWS AppSync failed to process. + * + * @property identifier identifier of event used for logging purposes. + * @property index of the event as it was sent in the publish. + * @property errorCode for the failed event. + * @property errorMessage for the failed event. + */ +data class FailedEvent internal constructor( + val identifier: String, + val index: Int, + val errorCode: Int?, + val errorMessage: String? +) \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index bd5c9cdefe..619481dbff 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -63,13 +63,11 @@ include(":aws-logging-cloudwatch") // Events API include(":aws-appsync-core") -include(":aws-appsync-core-amplify") +include(":aws-appsync-amplify") include(":aws-appsync-events") -include(":aws-appsync-events-amplify") project(":aws-appsync-core").projectDir = file("appsync/aws-appsync-core") -project(":aws-appsync-core-amplify").projectDir = file("appsync/aws-appsync-core-amplify") +project(":aws-appsync-amplify").projectDir = file("appsync/aws-appsync-amplify") project(":aws-appsync-events").projectDir = file("appsync/aws-appsync-events") -project(":aws-appsync-events-amplify").projectDir = file("appsync/aws-appsync-events-amplify") // Apollo Extensions