Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ jobs:
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/trycourier/mobile-automation-tests/dispatches \
-d "$(jq -n \
--arg et 'iOS-Flutter' \
--arg prnum '${{ github.event.pull_request.number }}' \
--arg sh '${{ github.event.pull_request.head.sha }}' \
'{event_type:$et, client_payload:{pr:$prnum, sha:$sh}}')"
--arg et 'iOS-Flutter' \
--arg prnum '${{ github.event.pull_request.number }}' \
--arg sh '${{ github.event.pull_request.head.sha }}' \
'{event_type:$et, client_payload:{pr:$prnum, sha:$sh}}')"
curl -sS --fail-with-body -X POST \
-H "Authorization: token ${{ secrets.AUTOMATION_ACCESS_TOKEN }}" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/trycourier/mobile-automation-tests/dispatches \
-d "$(jq -n \
--arg et 'Android-Flutter' \
--arg prnum '${{ github.event.pull_request.number }}' \
--arg sh '${{ github.event.pull_request.head.sha }}' \
'{event_type:$et, client_payload:{pr:$prnum, sha:$sh}}')"
--arg et 'Android-Flutter' \
--arg prnum '${{ github.event.pull_request.number }}' \
--arg sh '${{ github.event.pull_request.head.sha }}' \
'{event_type:$et, client_payload:{pr:$prnum, sha:$sh}}')"

tests-manual:
if: github.event_name == 'workflow_dispatch'
Expand Down
10 changes: 10 additions & 0 deletions Docs/1_Authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ final userId = "your_user_id";
await Courier.shared.signIn(userId: userId, accessToken: jwt);
```

For EU-hosted workspaces, pass the built-in EU endpoint preset:

```dart
await Courier.shared.signIn(
userId: userId,
accessToken: jwt,
apiUrls: CourierApiUrls.forRegion(CourierApiRegion.eu),
);
```

If the token is expired, you can generate a new one from your endpoint and call `Courier.shared.signIn(...)` again. You will need to check the token manually for expiration or generate a new one when the user views a specific screen in your app. It is up to you to handle token expiration and refresh based on your security needs.

## 4. Sign your user out
Expand Down
1 change: 1 addition & 0 deletions Docs/5_Client.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ final client = CourierClient(
userId: 'user_id',
connectionId: 'connection_id', // Optional. Used for inbox websocket
tenantId: 'tenant_id', // Optional. Used for scoping a client to a specific tenant
apiUrls: CourierApiUrls.forRegion(CourierApiRegion.eu), // Optional. Use for EU-hosted workspaces
showLogs: true, // Optional. Defaults to your current kDebugMode
);

Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ Your app must support at least gradle `8.4`

 

## EU endpoints

If your workspace uses EU-hosted Courier endpoints, pass the built-in EU preset through `apiUrls`.

```dart
await Courier.shared.signIn(
userId: "your_user_id",
accessToken: jwt,
apiUrls: CourierApiUrls.forRegion(CourierApiRegion.eu),
);
```

# Getting Started

These are all the available features of the SDK.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.courier.courier_flutter

import com.courier.android.Courier
import com.courier.android.client.CourierClient
import com.courier.android.models.CourierAuthenticationListener
import com.courier.android.models.CourierInboxListener
import com.courier.android.models.remove
Expand Down Expand Up @@ -63,7 +64,13 @@ internal class SharedMethodHandler(channel: CourierFlutterChannel, private val b
"userId" to options.userId,
"connectionId" to options.connectionId,
"tenantId" to options.tenantId,
"showLogs" to options.showLogs
"showLogs" to options.showLogs,
"apiUrls" to mapOf(
"rest" to options.apiUrls.rest,
"graphql" to options.apiUrls.graphql,
"inboxGraphql" to options.apiUrls.inboxGraphql,
"inboxWebSocket" to options.apiUrls.inboxWebSocket
)
)

result.success(client)
Expand Down Expand Up @@ -99,12 +106,14 @@ internal class SharedMethodHandler(channel: CourierFlutterChannel, private val b
val accessToken = params.extract("accessToken") as String
val clientKey = params["clientKey"] as? String
val showLogs = params.extract("showLogs") as Boolean
val apiUrls = (params["apiUrls"] as? HashMap<*, *>)?.toApiUrls()

Courier.shared.signIn(
userId = userId,
tenantId = tenantId,
accessToken = accessToken,
clientKey = clientKey,
apiUrls = apiUrls ?: CourierClient.ApiUrls(),
showLogs = showLogs,
)

Expand Down Expand Up @@ -507,4 +516,4 @@ internal class SharedMethodHandler(channel: CourierFlutterChannel, private val b

}

}
}
14 changes: 13 additions & 1 deletion android/src/main/kotlin/com/courier/courier_flutter/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,30 @@ internal fun HashMap<*, *>.toClient(): CourierClient {
val clientKey = options["clientKey"] as? String
val connectionId = options["connectionId"] as? String
val tenantId = options["tenantId"] as? String
val apiUrls = (options["apiUrls"] as? HashMap<*, *>)?.toApiUrls()

return CourierClient(
jwt = jwt,
clientKey = clientKey,
userId = userId,
connectionId = connectionId,
tenantId = tenantId,
apiUrls = apiUrls ?: CourierClient.ApiUrls(),
showLogs = showLogs
)

}

internal fun HashMap<*, *>.toApiUrls(): CourierClient.ApiUrls {
val defaults = CourierClient.ApiUrls()
return CourierClient.ApiUrls(
rest = this["rest"] as? String ?: defaults.rest,
graphql = this["graphql"] as? String ?: defaults.graphql,
inboxGraphql = this["inboxGraphql"] as? String ?: defaults.inboxGraphql,
inboxWebSocket = this["inboxWebSocket"] as? String ?: defaults.inboxWebSocket
)
}

// Stringify

internal fun Any.toJson(): String {
Expand All @@ -76,4 +88,4 @@ internal fun Any.toJson(): String {

internal inline fun <reified T> Map<*, *>.extract(key: String): T {
return this[key] as? T ?: throw MissingParameter(key)
}
}
10 changes: 9 additions & 1 deletion ios/Classes/CourierSharedMethodHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ internal class CourierSharedMethodHandler: CourierFlutterMethodHandler, FlutterP
"userId": options.userId,
"connectionId": options.connectionId,
"tenantId": options.tenantId,
"showLogs": options.showLogs
"showLogs": options.showLogs,
"apiUrls": [
"rest": options.apiUrls.rest,
"graphql": options.apiUrls.graphql,
"inboxGraphql": options.apiUrls.inboxGraphql,
"inboxWebSocket": options.apiUrls.inboxWebSocket
]
]

result(dict.compactMapValues { $0 })
Expand All @@ -75,12 +81,14 @@ internal class CourierSharedMethodHandler: CourierFlutterMethodHandler, FlutterP
let accessToken: String = try params.extract("accessToken")
let clientKey = params["clientKey"] as? String
let showLogs: Bool = try params.extract("showLogs")
let apiUrls = (params["apiUrls"] as? [String: Any])?.toCourierApiUrls()

await Courier.shared.signIn(
userId: userId,
tenantId: tenantId,
accessToken: accessToken,
clientKey: clientKey,
baseUrls: apiUrls ?? CourierClient.ApiUrls(),
showLogs: showLogs
)

Expand Down
16 changes: 16 additions & 0 deletions ios/Classes/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,15 @@ internal extension Dictionary<String, Any> {
let clientKey = options["clientKey"] as? String
let connectionId = options["connectionId"] as? String
let tenantId = options["tenantId"] as? String
let apiUrls = (options["apiUrls"] as? [String: Any])?.toCourierApiUrls()

return CourierClient(
jwt: jwt,
clientKey: clientKey,
userId: userId,
connectionId: connectionId,
tenantId: tenantId,
baseUrls: apiUrls ?? CourierClient.ApiUrls(),
showLogs: showLogs
)

Expand All @@ -145,6 +147,20 @@ internal extension Dictionary<String, Any> {

}

internal extension Dictionary where Key == String, Value == Any {

func toCourierApiUrls() -> CourierClient.ApiUrls {
let defaults = CourierClient.ApiUrls()
return CourierClient.ApiUrls(
rest: self["rest"] as? String ?? defaults.rest,
graphql: self["graphql"] as? String ?? defaults.graphql,
inboxGraphql: self["inboxGraphql"] as? String ?? defaults.inboxGraphql,
inboxWebSocket: self["inboxWebSocket"] as? String ?? defaults.inboxWebSocket
)
}

}

internal extension Error {

func toFlutterError() -> FlutterError {
Expand Down
53 changes: 53 additions & 0 deletions lib/client/courier_api_urls.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
enum CourierApiRegion { us, eu }

class CourierApiUrls {
final String rest;
final String graphql;
final String inboxGraphql;
final String inboxWebSocket;

const CourierApiUrls({
this.rest = 'https://api.courier.com',
this.graphql = 'https://api.courier.com/client/q',
this.inboxGraphql = 'https://inbox.courier.io/q',
this.inboxWebSocket = 'wss://realtime.courier.io',
});

const CourierApiUrls.us()
: rest = 'https://api.courier.com',
graphql = 'https://api.courier.com/client/q',
inboxGraphql = 'https://inbox.courier.io/q',
inboxWebSocket = 'wss://realtime.courier.io';

const CourierApiUrls.eu()
: rest = 'https://api.eu.courier.com',
graphql = 'https://api.eu.courier.com/client/q',
inboxGraphql = 'https://inbox.eu.courier.io/q',
inboxWebSocket = 'wss://realtime.eu.courier.io';

factory CourierApiUrls.forRegion(CourierApiRegion region) {
return region == CourierApiRegion.eu
? const CourierApiUrls.eu()
: const CourierApiUrls.us();
}

factory CourierApiUrls.fromJson(Map<dynamic, dynamic> json) {
return CourierApiUrls(
rest: json['rest'] as String? ?? const CourierApiUrls().rest,
graphql: json['graphql'] as String? ?? const CourierApiUrls().graphql,
inboxGraphql:
json['inboxGraphql'] as String? ?? const CourierApiUrls().inboxGraphql,
inboxWebSocket: json['inboxWebSocket'] as String? ??
const CourierApiUrls().inboxWebSocket,
);
}

Map<String, dynamic> toJson() {
return {
'rest': rest,
'graphql': graphql,
'inboxGraphql': inboxGraphql,
'inboxWebSocket': inboxWebSocket,
};
}
}
6 changes: 6 additions & 0 deletions lib/client/courier_client.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:courier_flutter/courier_flutter_channels.dart';
import 'package:courier_flutter/client/brand_client.dart';
import 'package:courier_flutter/client/courier_api_urls.dart';
import 'package:courier_flutter/client/inbox_client.dart';
import 'package:courier_flutter/client/preference_client.dart';
import 'package:courier_flutter/client/token_client.dart';
Expand All @@ -16,6 +17,7 @@ class CourierClientOptions {
final String? connectionId;
final String? tenantId;
final bool showLogs;
final CourierApiUrls? apiUrls;

CourierClientOptions({
required this.id,
Expand All @@ -25,6 +27,7 @@ class CourierClientOptions {
this.connectionId,
this.tenantId,
required this.showLogs,
this.apiUrls,
});

Map<String, dynamic> toJson() {
Expand All @@ -35,6 +38,7 @@ class CourierClientOptions {
'connectionId': connectionId,
'tenantId': tenantId,
'showLogs': showLogs,
'apiUrls': apiUrls?.toJson(),
};
}

Expand Down Expand Up @@ -88,6 +92,7 @@ class CourierClient {
required String userId,
String? connectionId,
String? tenantId,
CourierApiUrls? apiUrls,
bool? showLogs,
}) : options = CourierClientOptions(
id: const Uuid().v4(),
Expand All @@ -97,6 +102,7 @@ class CourierClient {
connectionId: connectionId,
tenantId: tenantId,
showLogs: showLogs ?? kDebugMode,
apiUrls: apiUrls,
);

Future add() async {
Expand Down
11 changes: 9 additions & 2 deletions lib/courier_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:io';

import 'package:courier_flutter/courier_flutter_channels.dart';
import 'package:courier_flutter/client/courier_api_urls.dart';
import 'package:courier_flutter/client/courier_client.dart';
import 'package:courier_flutter/courier_provider.dart';
import 'package:courier_flutter/ios_foreground_notification_presentation_options.dart';
Expand All @@ -18,6 +19,7 @@ import 'package:uuid/uuid.dart';
export 'models/inbox_message.dart';
export 'models/inbox_action.dart';
export 'ios_foreground_notification_presentation_options.dart';
export 'client/courier_api_urls.dart';

class Courier extends CourierChannelManager {

Expand Down Expand Up @@ -204,6 +206,9 @@ class Courier extends CourierChannelManager {
userId: options['userId'],
tenantId: options['tenantId'],
connectionId: options['connectionId'],
apiUrls: options['apiUrls'] == null
? null
: CourierApiUrls.fromJson(options['apiUrls']),
showLogs: options['showLogs'],
);
}
Expand Down Expand Up @@ -232,6 +237,7 @@ class Courier extends CourierChannelManager {
required String accessToken,
String? clientKey,
String? tenantId,
CourierApiUrls? apiUrls,
bool? showLogs
}) async {
_isDebugging = showLogs ?? kDebugMode;
Expand All @@ -241,6 +247,7 @@ class Courier extends CourierChannelManager {
'accessToken': accessToken,
'clientKey': clientKey,
'showLogs': _isDebugging,
'apiUrls': apiUrls?.toJson(),
});
}

Expand Down Expand Up @@ -521,7 +528,7 @@ abstract class CourierChannelManager extends PlatformInterface {
throw UnimplementedError('signOut() has not been implemented.');
}

Future signIn({ required String userId, required String accessToken, String? clientKey, String? tenantId, bool? showLogs }) async {
Future signIn({ required String userId, required String accessToken, String? clientKey, String? tenantId, CourierApiUrls? apiUrls, bool? showLogs }) async {
throw UnimplementedError('signIn() has not been implemented.');
}

Expand Down Expand Up @@ -640,4 +647,4 @@ abstract class CourierChannelManager extends PlatformInterface {

bool isUITestsActive = false;

}
}
Loading
Loading