Skip to content

Commit

Permalink
(DOCSP-32926 & DOCSP- 32216): Custom HTTP Headers for Kotlin (mongodb…
Browse files Browse the repository at this point in the history
…#3009)

## Pull Request Info

- Add info for custom HTTP headers 
- Update existing [Connect to App
Services](https://www.mongodb.com/docs/realm/sdk/kotlin/app-services/connect-to-app-services-backend/)
page

### Jira

- https://jira.mongodb.org/browse/DOCSP-32926
- https://jira.mongodb.org/browse/DOCSP-32216

### Staged Changes

- [Connect to Atlas App Services - Kotlin
SDK](https://docs-mongodbcom-staging.corp.mongodb.com/realm/docsworker-xlarge/docsp-32926-http-headers/sdk/kotlin/app-services/connect-to-app-services-backend/)

### Reminder Checklist

If your PR modifies the docs, you might need to also update some
corresponding
pages. Check if completed or N/A.

- [x] Create Jira ticket for corresponding docs-app-services update(s),
if any
- [x] Checked/updated Admin API
- [x] Checked/updated CLI reference

### Review Guidelines


[REVIEWING.md](https://github.com/mongodb/docs-realm/blob/master/REVIEWING.md)
  • Loading branch information
cbullinger authored Sep 19, 2023
1 parent 9ed8670 commit f970475
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,100 @@ package com.mongodb.realm.realmkmmapp

import io.realm.kotlin.internal.platform.runBlocking
import io.realm.kotlin.log.LogLevel
import io.realm.kotlin.log.RealmLog
import io.realm.kotlin.log.RealmLogger
import io.realm.kotlin.mongodb.App
import io.realm.kotlin.mongodb.AppConfiguration
import io.realm.kotlin.mongodb.Credentials
import io.realm.kotlin.mongodb.exceptions.ConnectionException
import io.realm.kotlin.mongodb.exceptions.InvalidCredentialsException
import io.realm.kotlin.mongodb.exceptions.ServiceException
import kotlinx.coroutines.channels.Channel
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue

class AppClientTest: RealmTest() {
@Test
fun initializeAppClientTest() {
fun initializeAndCloseAppClientTest() {
// :snippet-start: initialize-app-client
val app = App.create(YOUR_APP_ID)
// Creates an App with default configuration values
val app = App.create(YOUR_APP_ID) // Replace with your App ID
// :snippet-end:

// :snippet-start: close-app-client
app.close()
// :snippet-end:
}

@Test
fun configureAppClientTest() {
// :snippet-start: configure-app-client
App.create(
fun setCustomHttpHeadersTest() {
val myEncryptionKey = getEncryptionKey()
val config1 =
// :snippet-start: configure-app-client
// Creates an App with custom configuration values
AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID
.appName("my-app-name")
.encryptionKey(myEncryptionKey)
.build()
// :snippet-end:
val config2 =
// :snippet-start: set-custom-http-headers
AppConfiguration.Builder(YOUR_APP_ID)
.log(LogLevel.ALL)
.build())
.authorizationHeaderName("MyApp-Authorization")
.customRequestHeaders {
put("X-MyApp-Version", "1.0.0")
}
.build()
// :snippet-end:
assertEquals(config1.authorizationHeaderName, "Authorization")
assertEquals(config2.authorizationHeaderName, "MyApp-Authorization")
assertEquals(config2.customRequestHeaders["X-MyApp-Version"], "1.0.0")

runBlocking {
val app = App.create(config2)
val originalLevel = RealmLog.level
RealmLog.level = LogLevel.ALL
val channel = Channel<Boolean>(1)

val logger = object : RealmLogger {
override val level: LogLevel = LogLevel.DEBUG
override val tag: String = "LOGGER"

override fun log(
level: LogLevel,
throwable: Throwable?,
message: String?,
vararg args: Any?,
) {
if (level == LogLevel.DEBUG && message!!.contains("-> X-MyApp-Version: 1.0.0") && message.contains("MyApp-Authorization"))
{
channel.trySend(true)
}
}
}

try {
RealmLog.add(logger)
// Perform a network-related op that will fail because the server does not expect the custom header
assertFailsWith<ServiceException> {
app.login(Credentials.anonymous(reuseExisting = false))
}
assertTrue(channel.receiveOrFail())
} finally {
// Restore log status
RealmLog.remove(logger)
RealmLog.level = originalLevel
}
app.close()
}
}

@Test
fun testErrorHandlingTest() {
var email = getRandom()
var password = getRandom()
val email = getRandom()
val password = getRandom()
runBlocking {
// :snippet-start: handle-errors
val app = App.create(YOUR_APP_ID)
Expand Down Expand Up @@ -66,6 +131,7 @@ class AppClientTest: RealmTest() {
}
}
// :snippet-end:
app.close()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
app.close()
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
App.create(
AppConfiguration.Builder(YOUR_APP_ID)
.log(LogLevel.ALL)
.build())
// Creates an App with custom configuration values
AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID
.appName("my-app-name")
.encryptionKey(myEncryptionKey)
.build()
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
val app = App.create(YOUR_APP_ID)
// Creates an App with default configuration values
val app = App.create(YOUR_APP_ID) // Replace with your App ID
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
AppConfiguration.Builder(YOUR_APP_ID)
.authorizationHeaderName("MyApp-Authorization")
.customRequestHeaders {
put("X-MyApp-Version", "1.0.0")
}
.build()
2 changes: 1 addition & 1 deletion source/sdk/kotlin/app-services.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Atlas App Services - Kotlin SDK
.. toctree::
:titlesonly:

Connect to Atlas App Services </sdk/kotlin/app-services/connect-to-app-services-backend>
Connect to App Services </sdk/kotlin/app-services/connect-to-app-services-backend>
Call an Atlas Function </sdk/kotlin/app-services/call-function>
Handle App Errors </sdk/kotlin/app-services/handle-app-errors>

Expand Down
95 changes: 80 additions & 15 deletions source/sdk/kotlin/app-services/connect-to-app-services-backend.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,99 @@ Connect to Atlas App Services - Kotlin SDK
:depth: 2
:class: singlecol

The ``App`` class is the interface to App Services. This class provides
access to :ref:`authentication <kotlin-authenticate>` and Device Sync.
This page describes how to initialize your App client and connect to the Atlas
App Services backend using the Kotlin SDK.

The App client is the interface to the App Services backend. It provides
access to App Services functionality, including:

- :ref:`Authenticating <kotlin-authenticate>` app users
- :ref:`Synchronizing data <kotlin-sync>` between the Atlas backend and the
client app using Device Sync
- :ref:`Calling Atlas functions <kotlin-call-function>`

Each App client is associated with a single App ID.

Prerequisites
-------------

Before you can connect to Atlas App Services, you need an App Services App
with an App ID. To get started, refer to :ref:`Create an App <create-a-realm-app>`
in the App Services documentation.

To learn how to find your App ID in the App Services UI, refer to
:ref:`Find Your App ID <find-your-app-id>` in the App Services documentation.

.. _kotlin-access-the-app-client:

Access the App
--------------
Access the App Client
---------------------

The Kotlin SDK uses the
`App <{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app/index.html>`__
interface to access an ``App`` client.

Pass the App ID for your App, which you can :ref:`find in the App Services UI
<find-your-app-id>`.
You can initialize an App with default configuration values using
`App.create()
<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/-companion/create.html>`__.
You only need to pass the App ID for your App.

.. literalinclude:: /examples/generated/kotlin/AppClientTest.snippet.initialize-app-client.kt
:language: kotlin
:copyable: false

Once you have initialized the App, you can use the ``App`` instance to
access App Services functionality.

.. _kotlin-app-client-configuration:

Configuration
-------------
Configure the App Client
------------------------

You can add optional arguments to the ``AppConfiguration`` for more
granular control of your app connection details, such as custom
timeouts for connections and keys for local encryption.

For most use cases, you only need your application's App ID to connect
to App Services. For more granular control of the details of your app connection,
such as custom timeouts for connections and keys for local encryption,
use the `AppConfiguration.Builder
<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/-builder/index.html>`__ to control details of your ``App``:
To control the additional configuration options, use the
`AppConfiguration.Builder
<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/-builder/index.html>`__ and call the ``.build()`` method to pass a configuration
object:

.. literalinclude:: /examples/generated/kotlin/AppClientTest.snippet.configure-app-client.kt
:language: kotlin
:copyable: false

.. include:: /includes/multiple-app-client-details-and-app-config-cache.rst

Set Custom HTTP Headers
-----------------------

.. versionadded:: 1.11.0

If you use App Services or Device Sync with a proxy setup, you may need to set
custom HTTP headers. The Realm Kotlin SDK supports setting custom HTTP headers
on the App. These headers are appended to every request to the App Services
App, including :ref:`function calls <kotlin-call-function>`.

When you initialize the App, you can pass:

- the custom `authorizationHeaderName
<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/authorization-header-name.html>`__
``String`` value
- any `customRequestHeaders
<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/custom-request-headers.html>`__
in a map of ``String`` header keys and values (SDK accepts empty values but
not empty keys)

.. literalinclude:: /examples/generated/kotlin/AppClientTest.snippet.set-custom-http-headers.kt
:language: kotlin

Close the App Client
--------------------

You can manually close an App instance and release all underlying resources
using the `App.close()
<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app/close.html>`__ method:

.. literalinclude:: /examples/generated/kotlin/AppClientTest.snippet.close-app-client.kt
:language: kotlin

If not closed manually, resources are freed when the App instance is garbage collected.

0 comments on commit f970475

Please sign in to comment.