feat(code_push_client): add createReleaseArtifact (#185)
This commit is contained in:
@@ -31,6 +31,15 @@ Future<void> main() async {
|
||||
displayName: '<DISPLAY NAME>', // e.g. 'v1.0.0'
|
||||
);
|
||||
|
||||
// Create a release artifact.
|
||||
final releaseArtifact = await client.createReleaseArtifact(
|
||||
releaseId: release.id,
|
||||
artifactPath: '<PATH TO ARTIFACT>', // e.g. 'libapp.so'
|
||||
platform: '<PLATFORM>', // e.g. 'android'
|
||||
arch: '<ARCHITECTURE>', // e.g. 'aarch64'
|
||||
hash: '<HASH>', // 'sha256 hash of the artifact'
|
||||
);
|
||||
|
||||
// Create a new patch.
|
||||
final patch = await client.createPatch(releaseId: release.id);
|
||||
|
||||
|
||||
@@ -75,6 +75,35 @@ class CodePushClient {
|
||||
return PatchArtifact.fromJson(json.decode(body) as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
/// Create a new artifact for a specific [releaseId].
|
||||
Future<ReleaseArtifact> createReleaseArtifact({
|
||||
required String artifactPath,
|
||||
required int releaseId,
|
||||
required String arch,
|
||||
required String platform,
|
||||
required String hash,
|
||||
}) async {
|
||||
final request = http.MultipartRequest(
|
||||
'POST',
|
||||
Uri.parse('$hostedUri/api/v1/releases/$releaseId/artifacts'),
|
||||
);
|
||||
final file = await http.MultipartFile.fromPath('file', artifactPath);
|
||||
request.files.add(file);
|
||||
request.fields.addAll({
|
||||
'arch': arch,
|
||||
'platform': platform,
|
||||
'hash': hash,
|
||||
'size': '${file.length}',
|
||||
});
|
||||
request.headers.addAll(_apiKeyHeader);
|
||||
final response = await _httpClient.send(request);
|
||||
final body = await response.stream.bytesToString();
|
||||
|
||||
if (response.statusCode != HttpStatus.ok) throw _parseErrorResponse(body);
|
||||
|
||||
return ReleaseArtifact.fromJson(json.decode(body) as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
/// Create a new app with the provided [displayName].
|
||||
/// Returns the newly created app.
|
||||
Future<App> createApp({required String displayName}) async {
|
||||
|
||||
@@ -185,6 +185,135 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('createReleaseArtifact', () {
|
||||
const releaseId = 0;
|
||||
const arch = 'aarch64';
|
||||
const platform = 'android';
|
||||
const hash = 'test-hash';
|
||||
const size = 42;
|
||||
|
||||
test('throws an exception if the http request fails (unknown)', () async {
|
||||
when(() => httpClient.send(any())).thenAnswer((_) async {
|
||||
return http.StreamedResponse(
|
||||
Stream.empty(),
|
||||
HttpStatus.failedDependency,
|
||||
);
|
||||
});
|
||||
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final fixture = File(path.join(tempDir.path, 'release.txt'))
|
||||
..createSync();
|
||||
|
||||
expect(
|
||||
codePushClient.createReleaseArtifact(
|
||||
artifactPath: fixture.path,
|
||||
releaseId: releaseId,
|
||||
arch: arch,
|
||||
platform: platform,
|
||||
hash: hash,
|
||||
),
|
||||
throwsA(
|
||||
isA<CodePushException>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
CodePushClient.unknownErrorMessage,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('throws an exception if the http request fails', () async {
|
||||
when(() => httpClient.send(any())).thenAnswer((_) async {
|
||||
return http.StreamedResponse(
|
||||
Stream.value(utf8.encode(json.encode(errorResponse.toJson()))),
|
||||
HttpStatus.failedDependency,
|
||||
);
|
||||
});
|
||||
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final fixture = File(path.join(tempDir.path, 'release.txt'))
|
||||
..createSync();
|
||||
|
||||
expect(
|
||||
codePushClient.createReleaseArtifact(
|
||||
artifactPath: fixture.path,
|
||||
releaseId: releaseId,
|
||||
arch: arch,
|
||||
platform: platform,
|
||||
hash: hash,
|
||||
),
|
||||
throwsA(
|
||||
isA<CodePushException>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
errorResponse.message,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('completes when request succeeds', () async {
|
||||
const artifactId = 0;
|
||||
const artifactUrl = '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,
|
||||
arch: arch,
|
||||
platform: platform,
|
||||
hash: hash,
|
||||
size: size,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
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),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final request = verify(() => httpClient.send(captureAny()))
|
||||
.captured
|
||||
.single as http.MultipartRequest;
|
||||
|
||||
expect(
|
||||
request.url,
|
||||
codePushClient.hostedUri.replace(
|
||||
path: '/api/v1/releases/$releaseId/artifacts',
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('createApp', () {
|
||||
test('throws an exception if the http request fails (unknown)', () async {
|
||||
when(
|
||||
|
||||
Reference in New Issue
Block a user