Skip to content

Commit

Permalink
feat: add base url setting
Browse files Browse the repository at this point in the history
  • Loading branch information
Tienisto committed Aug 27, 2024
1 parent 0effdb1 commit 56e86b5
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 4 deletions.
4 changes: 4 additions & 0 deletions rhttp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.6.2

- feat: add `baseUrl` setting to `ClientSettings`

## 0.6.1

- feat: add `onSendProgress` and `onReceiveProgress`
Expand Down
12 changes: 12 additions & 0 deletions rhttp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,18 @@ await Rhttp.get(
);
```

### ➤ Base URL

Add a base URL to the client to avoid repeating the same URL or to change the base URL easily.

```dart
final client = await RhttpClient.create(
settings: const ClientSettings(
baseUrl: 'https://example.com',
),
);
```

### ➤ Interceptors

You can add interceptors to the client to modify requests / responses, handle errors, observe requests, etc.
Expand Down
7 changes: 7 additions & 0 deletions rhttp/lib/src/model/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import 'package:rhttp/src/rust/api/http.dart' as rust;

export 'package:rhttp/src/rust/api/client.dart' show TlsVersion;

const _keepBaseUrl = '__rhttp_keep__';
const _keepDuration = Duration(microseconds: -9999);
const _keepProxySettings = ProxySettings.noProxy();
const _keepTlsSettings = TlsSettings();

class ClientSettings {
/// Base URL to be prefixed to all requests.
final String? baseUrl;

/// The preferred HTTP version to use.
final HttpVersionPref httpVersionPref;

Expand All @@ -32,6 +36,7 @@ class ClientSettings {
final TlsSettings? tlsSettings;

const ClientSettings({
this.baseUrl,
this.httpVersionPref = HttpVersionPref.all,
this.timeout,
this.connectTimeout,
Expand All @@ -41,6 +46,7 @@ class ClientSettings {
});

ClientSettings copyWith({
String? baseUrl = _keepBaseUrl,
HttpVersionPref? httpVersionPref,
Duration? timeout = _keepDuration,
Duration? connectTimeout = _keepDuration,
Expand All @@ -49,6 +55,7 @@ class ClientSettings {
TlsSettings? tlsSettings = _keepTlsSettings,
}) {
return ClientSettings(
baseUrl: identical(baseUrl, _keepBaseUrl) ? this.baseUrl : baseUrl,
httpVersionPref: httpVersionPref ?? this.httpVersionPref,
timeout: identical(timeout, _keepDuration) ? this.timeout : timeout,
connectTimeout: identical(connectTimeout, _keepDuration)
Expand Down
9 changes: 7 additions & 2 deletions rhttp/lib/src/request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ Future<HttpResponse> requestInternalGeneric(HttpRequest request) async {
}

bool exceptionByInterceptor = false;
final url = switch (request.settings?.baseUrl) {
String baseUrl => baseUrl + request.url,
null => request.url,
};

try {
if (request.expectBody == HttpExpectBody.stream) {
final cancelRefCompleter = Completer<int>();
Expand All @@ -130,7 +135,7 @@ Future<HttpResponse> requestInternalGeneric(HttpRequest request) async {
clientAddress: request.client?.ref,
settings: request.settings?.toRustType(),
method: request.method._toRustType(),
url: request.url,
url: url,
query: request.query?.entries.map((e) => (e.key, e.value)).toList(),
headers: headers?._toRustType(),
body: request.body?._toRustType(),
Expand Down Expand Up @@ -207,7 +212,7 @@ Future<HttpResponse> requestInternalGeneric(HttpRequest request) async {
clientAddress: request.client?.ref,
settings: request.settings?.toRustType(),
method: request.method._toRustType(),
url: request.url,
url: url,
query: request.query?.entries.map((e) => (e.key, e.value)).toList(),
headers: headers?._toRustType(),
body: request.body?._toRustType(),
Expand Down
56 changes: 56 additions & 0 deletions rhttp/test/integration/base_url_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:rhttp/rhttp.dart';
import 'package:rhttp/src/rust/frb_generated.dart';

import '../mocks.dart';

void main() {
late MockRustLibApi mockApi;

setUpAll(() async {
mockApi = MockRustLibApi.createAndRegister();

RustLib.initMock(api: mockApi);
});

test('Should add base url on client request', () async {
mockApi.mockCreateClient();

String? observedUrl;
mockApi.mockDefaultResponse(
onAnswer: (requestUri) {
observedUrl = requestUri;
},
);

final client = await RhttpClient.create(
settings: const ClientSettings(
baseUrl: 'https://mydomain.com/abc',
),
);

await client.get('/def');

expect(observedUrl, 'https://mydomain.com/abc/def');
});

test('Should add base url on ad-hoc request', () async {
mockApi.mockCreateClient();

String? observedUrl;
mockApi.mockDefaultResponse(
onAnswer: (requestUri) {
observedUrl = requestUri;
},
);

await Rhttp.get(
'/456',
settings: const ClientSettings(
baseUrl: 'https://mydomain.com/123',
),
);

expect(observedUrl, 'https://mydomain.com/123/456');
});
}
20 changes: 18 additions & 2 deletions rhttp/test/mocks.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'dart:typed_data';

import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:mocktail/mocktail.dart';
import 'package:rhttp/src/model/response.dart';
import 'package:rhttp/src/rust/api/client.dart';
import 'package:rhttp/src/rust/frb_generated.dart';
import 'package:rhttp/src/rust/api/error.dart' as rust_error;
import 'package:rhttp/src/rust/api/http.dart' as rust_http;
Expand All @@ -10,6 +10,10 @@ class MockRustLibApi extends Mock implements RustLibApi {
MockRustLibApi.createAndRegister() {
registerFallbackValue(rust_http.HttpMethod.get_);
registerFallbackValue(rust_http.HttpExpectBody.text);
registerFallbackValue(const ClientSettings(
httpVersionPref: rust_http.HttpVersionPref.http11,
throwOnStatusCode: true,
));
}

void mockCustomResponse({
Expand Down Expand Up @@ -66,6 +70,8 @@ class MockRustLibApi extends Mock implements RustLibApi {
void mockDefaultResponse({void Function(String) onAnswer = _noop}) {
when<Future<rust_http.HttpResponse>>(
() => crateApiHttpMakeHttpRequest(
clientAddress: any(named: 'clientAddress'),
settings: any(named: 'settings'),
method: any(named: 'method'),
url: any(named: 'url'),
expectBody: any(named: 'expectBody'),
Expand Down Expand Up @@ -108,6 +114,16 @@ class MockRustLibApi extends Mock implements RustLibApi {
return onAnswer(invocation.namedArguments[#address]);
});
}

void mockCreateClient() {
when<Future<PlatformInt64>>(
() => crateApiHttpRegisterClient(
settings: any(named: 'settings'),
),
).thenAnswer((invocation) async {
return 1;
});
}
}

class FakeHttpResponse extends Fake implements HttpTextResponse {
Expand Down

0 comments on commit 56e86b5

Please sign in to comment.