feat(code_push_client): adjust create artifact APIs to use uploadUrl (#586)

This commit is contained in:
Felix Angelov
2023-06-02 13:48:55 -07:00
committed by GitHub
parent 95bbd7b42d
commit e0d19ee23b
4 changed files with 153 additions and 41 deletions
@@ -29,7 +29,7 @@ Future<void> main() async {
);
// Create a release artifact.
final releaseArtifact = await client.createReleaseArtifact(
await client.createReleaseArtifact(
releaseId: release.id,
artifactPath: '<PATH TO ARTIFACT>', // e.g. 'libapp.so'
platform: '<PLATFORM>', // e.g. 'android'
@@ -41,7 +41,7 @@ Future<void> main() async {
final patch = await client.createPatch(releaseId: release.id);
// Create a patch artifact.
final patchArtifact = await client.createPatchArtifact(
await client.createPatchArtifact(
patchId: patch.id,
artifactPath: '<PATH TO ARTIFACT>', // e.g. 'libapp.so'
platform: '<PLATFORM>', // e.g. 'android'
@@ -82,7 +82,7 @@ class CodePushClient {
}
/// Create a new artifact for a specific [patchId].
Future<PatchArtifact> createPatchArtifact({
Future<void> createPatchArtifact({
required String artifactPath,
required int patchId,
required String arch,
@@ -94,7 +94,6 @@ class CodePushClient {
Uri.parse('$_v1/patches/$patchId/artifacts'),
);
final file = await http.MultipartFile.fromPath('file', artifactPath);
request.files.add(file);
request.fields.addAll({
'arch': arch,
'platform': platform,
@@ -108,7 +107,20 @@ class CodePushClient {
throw _parseErrorResponse(response.statusCode, body);
}
return PatchArtifact.fromJson(json.decode(body) as Map<String, dynamic>);
final decoded = CreatePatchArtifactResponse.fromJson(
json.decode(body) as Map<String, dynamic>,
);
final uploadResponse = await _httpClient.put(
Uri.parse(decoded.uploadUrl),
body: File(artifactPath).readAsBytesSync(),
);
if (uploadResponse.statusCode != HttpStatus.ok) {
throw CodePushException(
message:
'''Failed to upload artifact (${uploadResponse.reasonPhrase} '${uploadResponse.statusCode})''',
);
}
}
/// Generates a Stripe payment link for the current user.
@@ -127,7 +139,7 @@ class CodePushClient {
}
/// Create a new artifact for a specific [releaseId].
Future<ReleaseArtifact> createReleaseArtifact({
Future<void> createReleaseArtifact({
required String artifactPath,
required int releaseId,
required String arch,
@@ -139,7 +151,6 @@ class CodePushClient {
Uri.parse('$_v1/releases/$releaseId/artifacts'),
);
final file = await http.MultipartFile.fromPath('file', artifactPath);
request.files.add(file);
request.fields.addAll({
'arch': arch,
'platform': platform,
@@ -153,7 +164,20 @@ class CodePushClient {
throw _parseErrorResponse(response.statusCode, body);
}
return ReleaseArtifact.fromJson(json.decode(body) as Map<String, dynamic>);
final decoded = CreateReleaseArtifactResponse.fromJson(
json.decode(body) as Map<String, dynamic>,
);
final uploadResponse = await _httpClient.put(
Uri.parse(decoded.uploadUrl),
body: File(artifactPath).readAsBytesSync(),
);
if (uploadResponse.statusCode != HttpStatus.ok) {
throw CodePushException(
message:
'''Failed to upload artifact (${uploadResponse.reasonPhrase} '${uploadResponse.statusCode})''',
);
}
}
/// Create a new app with the provided [displayName].
@@ -244,22 +244,19 @@ void main() {
);
});
test('completes when request succeeds', () async {
const artifactId = 0;
const artifactUrl = 'https://example.com/artifact.zip';
test('throws an exception if the upload fails', () async {
const uploadUrl = 'https://example.com/artifact.zip';
when(() => httpClient.send(any())).thenAnswer((_) async {
return http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
PatchArtifact(
id: artifactId,
url: artifactUrl,
patchId: patchId,
CreatePatchArtifactResponse(
arch: arch,
platform: platform,
hash: hash,
size: size,
uploadUrl: uploadUrl,
),
),
),
@@ -268,6 +265,10 @@ void main() {
);
});
when(
() => httpClient.put(any(), body: any(named: 'body')),
).thenAnswer((_) async => http.Response('', HttpStatus.badRequest));
final tempDir = Directory.systemTemp.createTempSync();
final fixture = File(path.join(tempDir.path, 'release.txt'))
..createSync();
@@ -280,18 +281,61 @@ void main() {
platform: platform,
hash: hash,
),
completion(
equals(
isA<PatchArtifact>()
.having((a) => a.id, 'id', artifactId)
.having((a) => a.patchId, 'patchId', patchId)
.having((a) => a.arch, 'arch', arch)
.having((a) => a.platform, 'platform', platform)
.having((a) => a.hash, 'hash', hash)
.having((a) => a.url, 'artifactUrl', artifactUrl),
throwsA(
isA<CodePushException>().having(
(e) => e.message,
'message',
contains('Failed to upload artifact'),
),
),
);
verify(
() => httpClient.put(
Uri.parse(uploadUrl),
body: fixture.readAsBytesSync(),
),
).called(1);
});
test('completes when request succeeds', () async {
const uploadUrl = 'https://example.com/artifact.zip';
when(() => httpClient.send(any())).thenAnswer((_) async {
return http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
CreatePatchArtifactResponse(
arch: arch,
platform: platform,
hash: hash,
size: size,
uploadUrl: uploadUrl,
),
),
),
),
HttpStatus.ok,
);
});
when(
() => httpClient.put(any(), body: any(named: 'body')),
).thenAnswer((_) async => http.Response('', HttpStatus.ok));
final tempDir = Directory.systemTemp.createTempSync();
final fixture = File(path.join(tempDir.path, 'release.txt'))
..createSync();
await expectLater(
codePushClient.createPatchArtifact(
artifactPath: fixture.path,
patchId: patchId,
arch: arch,
platform: platform,
hash: hash,
),
completes,
);
final request = verify(() => httpClient.send(captureAny()))
.captured
@@ -442,22 +486,19 @@ void main() {
);
});
test('completes when request succeeds', () async {
const artifactId = 0;
const artifactUrl = 'https://example.com/artifact.zip';
test('throws an exception if the upload fails', () async {
const uploadUrl = 'https://example.com/artifact.zip';
when(() => httpClient.send(any())).thenAnswer((_) async {
return http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
ReleaseArtifact(
id: artifactId,
url: artifactUrl,
releaseId: releaseId,
CreateReleaseArtifactResponse(
arch: arch,
platform: platform,
hash: hash,
size: size,
uploadUrl: uploadUrl,
),
),
),
@@ -466,6 +507,10 @@ void main() {
);
});
when(
() => httpClient.put(any(), body: any(named: 'body')),
).thenAnswer((_) async => http.Response('', HttpStatus.badRequest));
final tempDir = Directory.systemTemp.createTempSync();
final fixture = File(path.join(tempDir.path, 'release.txt'))
..createSync();
@@ -478,18 +523,61 @@ void main() {
platform: platform,
hash: hash,
),
completion(
equals(
isA<ReleaseArtifact>()
.having((a) => a.id, 'id', artifactId)
.having((a) => a.releaseId, 'releaseId', releaseId)
.having((a) => a.arch, 'arch', arch)
.having((a) => a.platform, 'platform', platform)
.having((a) => a.hash, 'hash', hash)
.having((a) => a.url, 'artifactUrl', artifactUrl),
throwsA(
isA<CodePushException>().having(
(e) => e.message,
'message',
contains('Failed to upload artifact'),
),
),
);
verify(
() => httpClient.put(
Uri.parse(uploadUrl),
body: fixture.readAsBytesSync(),
),
).called(1);
});
test('completes when request succeeds', () async {
const uploadUrl = 'https://example.com/artifact.zip';
when(() => httpClient.send(any())).thenAnswer((_) async {
return http.StreamedResponse(
Stream.value(
utf8.encode(
json.encode(
CreateReleaseArtifactResponse(
arch: arch,
platform: platform,
hash: hash,
size: size,
uploadUrl: uploadUrl,
),
),
),
),
HttpStatus.ok,
);
});
when(
() => httpClient.put(any(), body: any(named: 'body')),
).thenAnswer((_) async => http.Response('', HttpStatus.ok));
final tempDir = Directory.systemTemp.createTempSync();
final fixture = File(path.join(tempDir.path, 'release.txt'))
..createSync();
await expectLater(
codePushClient.createReleaseArtifact(
artifactPath: fixture.path,
releaseId: releaseId,
arch: arch,
platform: platform,
hash: hash,
),
completes,
);
final request = verify(() => httpClient.send(captureAny()))
.captured
@@ -2,9 +2,9 @@ export 'cancel_subscription/cancel_subscription_response.dart';
export 'check_for_patches/check_for_patches.dart';
export 'create_app/create_app.dart';
export 'create_app_collaborator/create_app_collaborator.dart';
export 'create_patch_artifact/create_patch_artifact.dart';
export 'create_channel/create_channel.dart';
export 'create_patch/create_patch.dart';
export 'create_patch_artifact/create_patch_artifact.dart';
export 'create_payment_link/create_payment_link.dart';
export 'create_release/create_release.dart';
export 'create_release_artifact/create_release_artifact.dart';