feat(code_push_client): use multipart file upload for artifacts (#1464)

This commit is contained in:
Felix Angelov
2023-11-07 16:12:54 -06:00
committed by GitHub
parent 843b0b3b90
commit 042ed5c537
4 changed files with 98 additions and 112 deletions
@@ -147,11 +147,12 @@ class CodePushClient {
json.decode(body) as Map<String, dynamic>,
);
final uploadResponse = await _httpClient.put(
Uri.parse(decoded.url),
body: File(artifactPath).readAsBytesSync(),
);
if (uploadResponse.statusCode != HttpStatus.ok) {
final uploadRequest = http.MultipartRequest('POST', Uri.parse(decoded.url))
..files.add(file);
final uploadResponse = await _httpClient.send(uploadRequest);
if (uploadResponse.statusCode != HttpStatus.noContent) {
throw CodePushException(
message:
'''Failed to upload artifact (${uploadResponse.reasonPhrase} '${uploadResponse.statusCode})''',
@@ -195,11 +196,12 @@ class CodePushClient {
json.decode(body) as Map<String, dynamic>,
);
final uploadResponse = await _httpClient.put(
Uri.parse(decoded.url),
body: File(artifactPath).readAsBytesSync(),
);
if (uploadResponse.statusCode != HttpStatus.ok) {
final uploadRequest = http.MultipartRequest('POST', Uri.parse(decoded.url))
..files.add(file);
final uploadResponse = await _httpClient.send(uploadRequest);
if (uploadResponse.statusCode != HttpStatus.noContent) {
throw CodePushException(
message:
'''Failed to upload artifact (${uploadResponse.reasonPhrase} '${uploadResponse.statusCode})''',
@@ -1,2 +1,2 @@
// Generated code. Do not modify.
const packageVersion = '0.6.0+1';
const packageVersion = '0.6.0+2';
@@ -1,6 +1,6 @@
name: shorebird_code_push_client
description: Library which allows Dart applications to interact with the ShoreBird CodePush API
version: 0.6.0+1
version: 0.6.0+2
repository: https://github.com/shorebirdtech/shorebird
publish_to: none
@@ -246,34 +246,30 @@ void main() {
test('throws an exception if the upload fails', () async {
const artifactId = 42;
const uploadUrl = 'https://example.com';
when(() => httpClient.send(any())).thenAnswer((invocation) async {
final request =
invocation.positionalArguments.first as http.BaseRequest;
if (request.method == 'POST') {
return http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
CreatePatchArtifactResponse(
id: artifactId,
patchId: patchId,
arch: arch,
platform: platform,
hash: hash,
size: size,
url: uploadUrl,
),
final responses = [
http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
CreatePatchArtifactResponse(
id: artifactId,
patchId: patchId,
arch: arch,
platform: platform,
hash: hash,
size: size,
url: uploadUrl,
),
),
),
HttpStatus.ok,
);
}
return http.StreamedResponse(
const Stream.empty(),
HttpStatus.badRequest,
);
});
),
HttpStatus.ok,
),
http.StreamedResponse(Stream.empty(), HttpStatus.badRequest),
];
when(() => httpClient.send(any())).thenAnswer(
(_) async => responses.removeAt(0),
);
final tempDir = Directory.systemTemp.createTempSync();
final fixture = File(path.join(tempDir.path, 'release.txt'))
@@ -301,7 +297,7 @@ void main() {
.last as http.BaseRequest;
expect(request.url, equals(Uri.parse(uploadUrl)));
expect(
request.contentLength,
(request as http.MultipartRequest).files.single.length,
equals(fixture.readAsBytesSync().lengthInBytes),
);
});
@@ -309,34 +305,30 @@ void main() {
test('completes when request succeeds', () async {
const artifactId = 42;
const uploadUrl = 'https://example.com';
when(() => httpClient.send(any())).thenAnswer((invocation) async {
final request =
invocation.positionalArguments.first as http.BaseRequest;
if (request.method == 'POST') {
return http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
CreatePatchArtifactResponse(
id: artifactId,
patchId: patchId,
arch: arch,
platform: platform,
hash: hash,
size: size,
url: uploadUrl,
),
final responses = [
http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
CreatePatchArtifactResponse(
id: artifactId,
patchId: patchId,
arch: arch,
platform: platform,
hash: hash,
size: size,
url: uploadUrl,
),
),
),
HttpStatus.ok,
);
}
return http.StreamedResponse(
const Stream.empty(),
),
HttpStatus.ok,
);
});
),
http.StreamedResponse(Stream.empty(), HttpStatus.noContent),
];
when(() => httpClient.send(any())).thenAnswer(
(_) async => responses.removeAt(0),
);
final tempDir = Directory.systemTemp.createTempSync();
final fixture = File(path.join(tempDir.path, 'release.txt'))
@@ -527,34 +519,30 @@ void main() {
test('throws an exception if the upload fails', () async {
const artifactId = 42;
const uploadUrl = 'https://example.com';
when(() => httpClient.send(any())).thenAnswer((invocation) async {
final request =
invocation.positionalArguments.first as http.BaseRequest;
if (request.method == 'POST') {
return http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
CreateReleaseArtifactResponse(
id: artifactId,
releaseId: releaseId,
arch: arch,
platform: platform,
hash: hash,
size: size,
url: uploadUrl,
),
final responses = [
http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
CreateReleaseArtifactResponse(
id: artifactId,
releaseId: releaseId,
arch: arch,
platform: platform,
hash: hash,
size: size,
url: uploadUrl,
),
),
),
HttpStatus.ok,
);
}
return http.StreamedResponse(
const Stream.empty(),
HttpStatus.badRequest,
);
});
),
HttpStatus.ok,
),
http.StreamedResponse(Stream.empty(), HttpStatus.badRequest),
];
when(() => httpClient.send(any())).thenAnswer(
(_) async => responses.removeAt(0),
);
final tempDir = Directory.systemTemp.createTempSync();
final fixture = File(path.join(tempDir.path, 'release.txt'))
@@ -583,7 +571,7 @@ void main() {
).captured.last as http.BaseRequest;
expect(request.url, equals(Uri.parse(uploadUrl)));
expect(
request.contentLength,
(request as http.MultipartRequest).files.single.length,
equals(fixture.readAsBytesSync().lengthInBytes),
);
});
@@ -591,34 +579,30 @@ void main() {
test('completes when request succeeds', () async {
const artifactId = 42;
const uploadUrl = 'https://example.com';
when(() => httpClient.send(any())).thenAnswer((invocation) async {
final request =
invocation.positionalArguments.first as http.BaseRequest;
if (request.method == 'POST') {
return http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
CreateReleaseArtifactResponse(
id: artifactId,
releaseId: releaseId,
arch: arch,
platform: platform,
hash: hash,
size: size,
url: uploadUrl,
),
final responses = [
http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
CreateReleaseArtifactResponse(
id: artifactId,
releaseId: releaseId,
arch: arch,
platform: platform,
hash: hash,
size: size,
url: uploadUrl,
),
),
),
HttpStatus.ok,
);
}
return http.StreamedResponse(
const Stream.empty(),
),
HttpStatus.ok,
);
});
),
http.StreamedResponse(Stream.empty(), HttpStatus.noContent),
];
when(() => httpClient.send(any())).thenAnswer(
(_) async => responses.removeAt(0),
);
final tempDir = Directory.systemTemp.createTempSync();
final fixture = File(path.join(tempDir.path, 'release.txt'))