Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

infra: adds SdkKeyDecoder class #17

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
37 changes: 37 additions & 0 deletions lib/src/sdk_key_decoder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'dart:convert';
import 'package:logging/logging.dart';

/// A class for decoding SDK keys and extracting configuration hostname
class SdkKeyDecoder {
static const String _paramName = 'ch';

static final Logger _logger = Logger('SdkKeyDecoder');

/// Decodes and returns the configuration hostname from the provided Eppo SDK key string.
/// If the SDK key doesn't contain the configuration hostname, or it's invalid, it returns null.
static String? decodeConfigurationHostname(String sdkKey) {
final parts = sdkKey.split('.');
if (parts.length < 2) return null;

final encodedPayload = parts[1];
if (encodedPayload.isEmpty) return null;

try {
final decodedPayload = utf8.decode(base64.decode(encodedPayload));
final params = Uri.splitQueryString(decodedPayload);
final hostname = params[_paramName];
if (hostname == null || hostname.isEmpty) return null;

if (!hostname.startsWith('http://') && !hostname.startsWith('https://')) {
// prefix hostname with https scheme if none present
return 'https://$hostname';
} else {
return hostname;
}
} catch (e) {
// Canonical Dart logging for `debug` level
_logger.fine('Error decoding configuration hostname: $e');
return null;
}
}
}
48 changes: 48 additions & 0 deletions test/sdk_key_decoder_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:eppo/src/sdk_key_decoder.dart';

String encodeSdkKey({String? configHostname, String? eventHostname}) {
final params = {
if (configHostname != null) 'ch': configHostname,
if (eventHostname != null) 'eh': eventHostname,
};
final queryString = Uri(queryParameters: params).query;
final encodedPayload = base64.encode(utf8.encode(queryString));
return 'zCsQuoHJxVPp895.$encodedPayload';
}

void main() {
group('SdkKeyDecoder', () {
test('should return null when SDK key has neither host', () {
final hostname = SdkKeyDecoder.decodeConfigurationHostname(
encodeSdkKey(),
);
expect(hostname, isNull);
});

test('should return null when SDK key has only event host', () {
final hostname = SdkKeyDecoder.decodeConfigurationHostname(
encodeSdkKey(eventHostname: '123456.e.testing.eppo.cloud'),
);
expect(hostname, isNull);
});

test('should decode configuration host when SDK key has both hosts', () {
final hostname = SdkKeyDecoder.decodeConfigurationHostname(
encodeSdkKey(
configHostname: 'https://123456.e.testing.fscdn.eppo.cloud',
eventHostname: 'https://123456.e.testing.eppo.cloud',
),
);
expect(hostname, equals('https://123456.e.testing.fscdn.eppo.cloud'));
});

test('should add https prefix to hostname without scheme', () {
final hostname = SdkKeyDecoder.decodeConfigurationHostname(
encodeSdkKey(configHostname: '123456.e.testing.fscdn.eppo.cloud'),
);
expect(hostname, equals('https://123456.e.testing.fscdn.eppo.cloud'));
});
});
}
Loading