Skip to content

Commit

Permalink
Added client headers that can optionally be passed in with a client.
Browse files Browse the repository at this point in the history
  • Loading branch information
larryaasen committed Apr 1, 2024
1 parent 8e830d2 commit c9e489f
Show file tree
Hide file tree
Showing 14 changed files with 103 additions and 32 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Next

- Added client headers that can optionally be passed in with a client. (Fix for [PR](https://github.com/larryaasen/upgrader/pull/386))

## 10.0.1

- Allowed for device_info_plus 10.0.0 and package_info_plus 6.0.0.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ Here are the custom parameters for `UpgradeCard`:
The `Upgrader` class can be customized by setting parameters in the constructor, and passing it

* client: an HTTP Client that can be replaced for mock testing, defaults to `http.Client()`.
* clientHeaders: Provide the HTTP headers used by `client`, which defaults to ```null```
* countryCode: the country code that will override the system locale, which defaults to ```null```
* debugDisplayAlways: always force the upgrade to be available, defaults to ```false```
* debugDisplayOnce: display the upgrade at least once, defaults to ```false```
Expand Down
5 changes: 4 additions & 1 deletion example/lib/main_appcast.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/*
* Copyright (c) 2019-2022 Larry Aasen. All rights reserved.
* Copyright (c) 2019-2024 Larry Aasen. All rights reserved.
*/

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:upgrader/upgrader.dart';

void main() async {
Expand All @@ -20,6 +21,8 @@ class MyApp extends StatelessWidget {
static const appcastURL =
'https://raw.githubusercontent.com/larryaasen/upgrader/master/test/testappcast.xml';
final upgrader = Upgrader(
client: http.Client(),
clientHeaders: {'header1': 'value1'},
storeController: UpgraderStoreController(
onAndroid: () => UpgraderAppcastStore(appcastURL: appcastURL),
oniOS: () => UpgraderAppcastStore(appcastURL: appcastURL),
Expand Down
7 changes: 6 additions & 1 deletion lib/src/appcast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class Appcast {
/// Provide an HTTP Client that can be replaced during testing.
final http.Client client;

/// Provide the HTTP headers used by [client].
final Map<String, String>? clientHeaders;

/// Provide [UpgraderOS] that can be replaced during testing.
final UpgraderOS upgraderOS;

Expand All @@ -30,6 +33,7 @@ class Appcast {

Appcast({
http.Client? client,
this.clientHeaders,
UpgraderOS? upgraderOS,
UpgraderDevice? upgraderDevice,
}) : client = client ?? http.Client(),
Expand Down Expand Up @@ -104,7 +108,8 @@ class Appcast {
Future<List<AppcastItem>?> parseAppcastItemsFromUri(String appCastURL) async {
http.Response response;
try {
response = await client.get(Uri.parse(appCastURL));
response =
await client.get(Uri.parse(appCastURL), headers: clientHeaders);
} catch (e) {
print('upgrader: parseAppcastItemsFromUri exception: $e');
return null;
Expand Down
9 changes: 7 additions & 2 deletions lib/src/itunes_search_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class ITunesSearchAPI {
/// Provide an HTTP Client that can be replaced for mock testing.
http.Client? client = http.Client();

/// Provide the HTTP headers used by [client].
Map<String, String>? clientHeaders;

/// Enable print statements for debugging.
bool debugLogging = false;

Expand All @@ -42,7 +45,8 @@ class ITunesSearchAPI {
}
try {
final response = await client!.get(Uri.parse(url));
final response =
await client!.get(Uri.parse(url), headers: clientHeaders);
if (debugLogging) {
print('upgrader: response statusCode: ${response.statusCode}');
}
Expand Down Expand Up @@ -73,7 +77,8 @@ class ITunesSearchAPI {
print('upgrader: download: $url');
}
try {
final response = await client!.get(Uri.parse(url));
final response =
await client!.get(Uri.parse(url), headers: clientHeaders);
final decodedResults = _decodeResults(response.body);
return decodedResults;
} catch (e) {
Expand Down
9 changes: 7 additions & 2 deletions lib/src/play_store_search_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ import 'package:http/http.dart' as http;
import 'package:version/version.dart';

class PlayStoreSearchAPI {
PlayStoreSearchAPI({http.Client? client}) : client = client ?? http.Client();
PlayStoreSearchAPI({http.Client? client, this.clientHeaders})
: client = client ?? http.Client();

/// Play Store Search Api URL
final String playStorePrefixURL = 'play.google.com';

/// Provide an HTTP Client that can be replaced for mock testing.
final http.Client? client;

/// Provide the HTTP headers used by [client].
final Map<String, String>? clientHeaders;

/// Enable print statements for debugging.
bool debugLogging = false;

Expand All @@ -34,7 +38,8 @@ class PlayStoreSearchAPI {
}

try {
final response = await client!.get(Uri.parse(url));
final response =
await client!.get(Uri.parse(url), headers: clientHeaders);
if (response.statusCode < 200 || response.statusCode >= 300) {
if (debugLogging) {
print(
Expand Down
7 changes: 7 additions & 0 deletions lib/src/upgrade_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class UpgraderState {
/// Creates an [Upgrader] state.
UpgraderState({
required this.client,
this.clientHeaders,
this.countryCodeOverride,
this.debugDisplayAlways = false,
this.debugDisplayOnce = false,
Expand All @@ -31,6 +32,9 @@ class UpgraderState {
/// Provide an HTTP Client that can be replaced during testing.
final http.Client client;

/// Provide the HTTP headers used by [client].
final Map<String, String>? clientHeaders;

/// The country code that will override the system locale. Optional.
final String? countryCodeOverride;

Expand Down Expand Up @@ -74,6 +78,7 @@ class UpgraderState {
/// Creates a new state object by copying existing data and modifying selected fields.
UpgraderState copyWith({
http.Client? client,
Map<String, String>? clientHeaders,
String? countryCodeOverride,
bool? debugDisplayAlways,
bool? debugDisplayOnce,
Expand All @@ -89,6 +94,7 @@ class UpgraderState {
}) {
return UpgraderState(
client: client ?? this.client,
clientHeaders: clientHeaders ?? this.clientHeaders,
countryCodeOverride: countryCodeOverride ?? this.countryCodeOverride,
debugDisplayAlways: debugDisplayAlways ?? this.debugDisplayAlways,
debugDisplayOnce: debugDisplayOnce ?? this.debugDisplayOnce,
Expand Down Expand Up @@ -117,6 +123,7 @@ class UpgraderState {
}) {
return UpgraderState(
client: client,
clientHeaders: clientHeaders,
countryCodeOverride:
countryCodeOverride == true ? null : this.countryCodeOverride,
debugDisplayAlways: debugDisplayAlways,
Expand Down
16 changes: 4 additions & 12 deletions lib/src/upgrade_store_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class UpgraderAppStore extends UpgraderStore {
final iTunes = ITunesSearchAPI();
iTunes.debugLogging = state.debugLogging;
iTunes.client = state.client;
iTunes.clientHeaders = state.clientHeaders;
final response = await (iTunes
.lookupByBundleId(state.packageInfo!.packageName, country: country));

Expand Down Expand Up @@ -86,7 +87,8 @@ class UpgraderPlayStore extends UpgraderStore {
required String? language}) async {
if (state.packageInfo == null) return UpgraderVersionInfo();
final id = state.packageInfo!.packageName;
final playStore = PlayStoreSearchAPI(client: state.client);
final playStore = PlayStoreSearchAPI(
client: state.client, clientHeaders: state.clientHeaders);
playStore.debugLogging = state.debugLogging;

String? appStoreListingURL;
Expand Down Expand Up @@ -149,21 +151,10 @@ class UpgraderAppcastStore extends UpgraderStore {
UpgraderAppcastStore({
required this.appcastURL,
this.appcast,
// this.client,
});

final String appcastURL;
final Appcast? appcast;
// final http.Client? client;

// /// Provide an HTTP Client that can be replaced during testing.
// final http.Client client;

// /// Provide [UpgraderOS] that can be replaced during testing.
// final UpgraderOS upgraderOS;

// /// Provide [UpgraderDevice] that ca be replaced during testing.
// final UpgraderDevice upgraderDevice;

@override
Future<UpgraderVersionInfo> getVersionInfo(
Expand All @@ -179,6 +170,7 @@ class UpgraderAppcastStore extends UpgraderStore {
final localAppcast = appcast ??
Appcast(
client: state.client,
clientHeaders: state.clientHeaders,
upgraderDevice: state.upgraderDevice,
upgraderOS: state.upgraderOS);
await localAppcast.parseAppcastItemsFromUri(appcastURL);
Expand Down
2 changes: 2 additions & 0 deletions lib/src/upgrader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class Upgrader with WidgetsBindingObserver {
/// trigger an alert or other UI to evaluate upgrading criteria.
Upgrader({
http.Client? client,
Map<String, String>? clientHeaders,
String? countryCode,
bool debugDisplayAlways = false,
bool debugDisplayOnce = false,
Expand All @@ -60,6 +61,7 @@ class Upgrader with WidgetsBindingObserver {
this.willDisplayUpgrade,
}) : _state = UpgraderState(
client: client ?? http.Client(),
clientHeaders: clientHeaders,
countryCodeOverride: countryCode,
debugDisplayAlways: debugDisplayAlways,
debugDisplayOnce: debugDisplayOnce,
Expand Down
4 changes: 3 additions & 1 deletion test/itunes_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ void main() {
});

test('testing lookupByBundleId', () async {
final client = MockITunesSearchClient.setupMockClient();
final client = MockITunesSearchClient.setupMockClient(
verifyHeaders: {'header1': 'value1'});
final iTunes = ITunesSearchAPI();
iTunes.client = client;
iTunes.clientHeaders = {'header1': 'value1'};

final response =
await iTunes.lookupByBundleId('com.google.Maps', useCacheBuster: false);
Expand Down
6 changes: 6 additions & 0 deletions test/mock_itunes_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:upgrader/upgrader.dart';
Expand All @@ -13,6 +14,7 @@ class MockITunesSearchClient {
static http.Client setupMockClient({
String country = 'US',
String description = '',
Map<String, String>? verifyHeaders,
}) {
final currency = country == 'US'
? 'USD'
Expand All @@ -21,6 +23,10 @@ class MockITunesSearchClient {
: '';

final client = MockClient((http.Request request) async {
if (verifyHeaders != null) {
assert(mapEquals(verifyHeaders, request.headers));
}

final resultsMap = {
'results': [
{
Expand Down
9 changes: 8 additions & 1 deletion test/mock_play_store_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:upgrader/upgrader.dart';
Expand All @@ -22,9 +23,15 @@ final _filenames = {
// Create a MockClient using the Mock class provided by the Mockito package.
// We will create a new instances of this class in each test.
class MockPlayStoreSearchClient {
static Future<http.Client> setupMockClient() async {
static Future<http.Client> setupMockClient(
{Map<String, String>? verifyHeaders}) async {
final client = MockClient((http.Request request) async {
var url = request.url.toString();

if (verifyHeaders != null) {
assert(mapEquals(verifyHeaders, request.headers));
}

final index = url.indexOf('_cb=');
if (index > 0) {
url = url.substring(0, index - 1);
Expand Down
6 changes: 4 additions & 2 deletions test/play_store_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ void main() {
}, skip: false);

test('testing lookupById', () async {
final client = await MockPlayStoreSearchClient.setupMockClient();
final playStore = PlayStoreSearchAPI(client: client);
final client = await MockPlayStoreSearchClient.setupMockClient(
verifyHeaders: {'header1': 'value1'});
final playStore = PlayStoreSearchAPI(
client: client, clientHeaders: {'header1': 'value1'});
expect(() async => await playStore.lookupById(''), throwsAssertionError);

final response = await playStore.lookupById('com.kotoko.express');
Expand Down
50 changes: 40 additions & 10 deletions test/upgrader_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -660,11 +660,15 @@ void main() {

testWidgets('test upgrader store version android',
(WidgetTester tester) async {
final client = await MockPlayStoreSearchClient.setupMockClient();
final client = await MockPlayStoreSearchClient.setupMockClient(
verifyHeaders: {'header1': 'value1'},
);
final upgrader = Upgrader(
upgraderOS: MockUpgraderOS(android: true),
client: client,
debugLogging: true);
upgraderOS: MockUpgraderOS(android: true),
client: client,
clientHeaders: {'header1': 'value1'},
debugLogging: true,
);

upgrader.installPackageInfo(
packageInfo: PackageInfo(
Expand Down Expand Up @@ -756,12 +760,10 @@ void main() {
expect(notCalled, true);
}, skip: false);

test('should use fake Appcast', () async {
test('should use Appcast', () async {
final fakeAppcast = FakeAppcast();
final client = MockITunesSearchClient.setupMockClient();
final upgrader = Upgrader(
upgraderOS: MockUpgraderOS(os: 'ios', ios: true),
client: client,
debugLogging: true,
storeController: UpgraderStoreController(
oniOS: () => UpgraderAppcastStore(
Expand All @@ -783,6 +785,31 @@ void main() {
expect(fakeAppcast.callCount, greaterThan(0));
}, skip: false);

test('should use Appcast headers', () async {
final upgrader = Upgrader(
upgraderOS: MockUpgraderOS(ios: true),
debugLogging: true,
client: MockITunesSearchClient.setupMockClient(
verifyHeaders: {'header1': 'value1'}),
clientHeaders: {'header1': 'value1'},
storeController: UpgraderStoreController(
oniOS: () => UpgraderAppcastStore(
appcastURL: 'https://sparkle-project.org/test/testappcast.xml',
),
),
upgraderDevice: MockUpgraderDevice(),
)..installPackageInfo(
packageInfo: PackageInfo(
appName: 'Upgrader',
packageName: 'com.larryaasen.upgrader',
version: '1.9.6',
buildNumber: '42',
),
);

await upgrader.initialize();
}, skip: false);

test('will use appcast critical version if exists', () async {
final upgraderOS = MockUpgraderOS(android: true);
final Client mockClient =
Expand Down Expand Up @@ -963,9 +990,12 @@ void main() {

test('should return true when version is below minAppVersion', () async {
final upgrader = Upgrader(
debugLogging: true,
upgraderOS: MockUpgraderOS(ios: true),
client: MockITunesSearchClient.setupMockClient())
debugLogging: true,
upgraderOS: MockUpgraderOS(ios: true),
client: MockITunesSearchClient.setupMockClient(
verifyHeaders: {'header1': 'value1'}),
clientHeaders: {'header1': 'value1'},
)
..minAppVersion = '2.0.0'
..installPackageInfo(
packageInfo: PackageInfo(
Expand Down

0 comments on commit c9e489f

Please sign in to comment.