feat: installModuleStreaming for live install progress
Wraps the new HubAdmin/InstallModuleStream: yields one update per phase and, during the download, as bytes arrive; completes on success and surfaces the hub's failure text as a stream error, so callers handle failures exactly as with the unary installModule. Generated stubs regenerated from the protos (additive only). Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
5759ce4c7a
commit
1d3d33d55a
4 changed files with 381 additions and 0 deletions
|
|
@ -3576,6 +3576,246 @@ class InstallModuleResponse extends $pb.GeneratedMessage {
|
|||
void clearCapabilityCount() => $_clearField(3);
|
||||
}
|
||||
|
||||
enum InstallProgressEvent_Event { update, finished, failed, notSet }
|
||||
|
||||
/// One message in an InstallModuleStream.
|
||||
class InstallProgressEvent extends $pb.GeneratedMessage {
|
||||
factory InstallProgressEvent({
|
||||
InstallProgressUpdate? update,
|
||||
InstallModuleResponse? finished,
|
||||
$core.String? failed,
|
||||
}) {
|
||||
final result = create();
|
||||
if (update != null) result.update = update;
|
||||
if (finished != null) result.finished = finished;
|
||||
if (failed != null) result.failed = failed;
|
||||
return result;
|
||||
}
|
||||
|
||||
InstallProgressEvent._();
|
||||
|
||||
factory InstallProgressEvent.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory InstallProgressEvent.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static const $core.Map<$core.int, InstallProgressEvent_Event>
|
||||
_InstallProgressEvent_EventByTag = {
|
||||
1: InstallProgressEvent_Event.update,
|
||||
2: InstallProgressEvent_Event.finished,
|
||||
3: InstallProgressEvent_Event.failed,
|
||||
0: InstallProgressEvent_Event.notSet
|
||||
};
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'InstallProgressEvent',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'chain.v1'),
|
||||
createEmptyInstance: create)
|
||||
..oo(0, [1, 2, 3])
|
||||
..aOM<InstallProgressUpdate>(1, _omitFieldNames ? '' : 'update',
|
||||
subBuilder: InstallProgressUpdate.create)
|
||||
..aOM<InstallModuleResponse>(2, _omitFieldNames ? '' : 'finished',
|
||||
subBuilder: InstallModuleResponse.create)
|
||||
..aOS(3, _omitFieldNames ? '' : 'failed')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
InstallProgressEvent clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
InstallProgressEvent copyWith(void Function(InstallProgressEvent) updates) =>
|
||||
super.copyWith((message) => updates(message as InstallProgressEvent))
|
||||
as InstallProgressEvent;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static InstallProgressEvent create() => InstallProgressEvent._();
|
||||
@$core.override
|
||||
InstallProgressEvent createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static InstallProgressEvent getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<InstallProgressEvent>(create);
|
||||
static InstallProgressEvent? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
@$pb.TagNumber(2)
|
||||
@$pb.TagNumber(3)
|
||||
InstallProgressEvent_Event whichEvent() =>
|
||||
_InstallProgressEvent_EventByTag[$_whichOneof(0)]!;
|
||||
@$pb.TagNumber(1)
|
||||
@$pb.TagNumber(2)
|
||||
@$pb.TagNumber(3)
|
||||
void clearEvent() => $_clearField($_whichOneof(0));
|
||||
|
||||
/// Progress moved, or a phase started or finished.
|
||||
@$pb.TagNumber(1)
|
||||
InstallProgressUpdate get update => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set update(InstallProgressUpdate value) => $_setField(1, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasUpdate() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearUpdate() => $_clearField(1);
|
||||
@$pb.TagNumber(1)
|
||||
InstallProgressUpdate ensureUpdate() => $_ensure(0);
|
||||
|
||||
/// The install succeeded; always the final message.
|
||||
@$pb.TagNumber(2)
|
||||
InstallModuleResponse get finished => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set finished(InstallModuleResponse value) => $_setField(2, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasFinished() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearFinished() => $_clearField(2);
|
||||
@$pb.TagNumber(2)
|
||||
InstallModuleResponse ensureFinished() => $_ensure(1);
|
||||
|
||||
/// The install failed; always the final message. Carries the same
|
||||
/// text the unary call would have returned as its status message.
|
||||
@$pb.TagNumber(3)
|
||||
$core.String get failed => $_getSZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set failed($core.String value) => $_setString(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasFailed() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearFailed() => $_clearField(3);
|
||||
}
|
||||
|
||||
/// A single progress observation.
|
||||
class InstallProgressUpdate extends $pb.GeneratedMessage {
|
||||
factory InstallProgressUpdate({
|
||||
$core.String? phase,
|
||||
$core.String? stage,
|
||||
$core.double? percent,
|
||||
$fixnum.Int64? bytesReceived,
|
||||
$fixnum.Int64? bytesTotal,
|
||||
$core.bool? phaseDone,
|
||||
}) {
|
||||
final result = create();
|
||||
if (phase != null) result.phase = phase;
|
||||
if (stage != null) result.stage = stage;
|
||||
if (percent != null) result.percent = percent;
|
||||
if (bytesReceived != null) result.bytesReceived = bytesReceived;
|
||||
if (bytesTotal != null) result.bytesTotal = bytesTotal;
|
||||
if (phaseDone != null) result.phaseDone = phaseDone;
|
||||
return result;
|
||||
}
|
||||
|
||||
InstallProgressUpdate._();
|
||||
|
||||
factory InstallProgressUpdate.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory InstallProgressUpdate.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'InstallProgressUpdate',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'chain.v1'),
|
||||
createEmptyInstance: create)
|
||||
..aOS(1, _omitFieldNames ? '' : 'phase')
|
||||
..aOS(2, _omitFieldNames ? '' : 'stage')
|
||||
..aD(3, _omitFieldNames ? '' : 'percent', fieldType: $pb.PbFieldType.OF)
|
||||
..a<$fixnum.Int64>(
|
||||
4, _omitFieldNames ? '' : 'bytesReceived', $pb.PbFieldType.OU6,
|
||||
defaultOrMaker: $fixnum.Int64.ZERO)
|
||||
..a<$fixnum.Int64>(
|
||||
5, _omitFieldNames ? '' : 'bytesTotal', $pb.PbFieldType.OU6,
|
||||
defaultOrMaker: $fixnum.Int64.ZERO)
|
||||
..aOB(6, _omitFieldNames ? '' : 'phaseDone')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
InstallProgressUpdate clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
InstallProgressUpdate copyWith(
|
||||
void Function(InstallProgressUpdate) updates) =>
|
||||
super.copyWith((message) => updates(message as InstallProgressUpdate))
|
||||
as InstallProgressUpdate;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static InstallProgressUpdate create() => InstallProgressUpdate._();
|
||||
@$core.override
|
||||
InstallProgressUpdate createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static InstallProgressUpdate getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<InstallProgressUpdate>(create);
|
||||
static InstallProgressUpdate? _defaultInstance;
|
||||
|
||||
/// Stable phase identifier: "resolve", "download" or "install".
|
||||
/// Clients map it to their own wording; it is not display text.
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get phase => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set phase($core.String value) => $_setString(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasPhase() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearPhase() => $_clearField(1);
|
||||
|
||||
/// English fallback label for the phase ("Downloading"). Clients with
|
||||
/// their own translations key off `phase` and ignore this.
|
||||
@$pb.TagNumber(2)
|
||||
$core.String get stage => $_getSZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set stage($core.String value) => $_setString(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasStage() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearStage() => $_clearField(2);
|
||||
|
||||
/// Overall progress across all phases, 0..100, monotonic. Absent
|
||||
/// means the extent is not knowable yet — show an indeterminate
|
||||
/// indicator, never a bar parked at zero.
|
||||
@$pb.TagNumber(3)
|
||||
$core.double get percent => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set percent($core.double value) => $_setFloat(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasPercent() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearPercent() => $_clearField(3);
|
||||
|
||||
/// Bytes received so far during the download phase.
|
||||
@$pb.TagNumber(4)
|
||||
$fixnum.Int64 get bytesReceived => $_getI64(3);
|
||||
@$pb.TagNumber(4)
|
||||
set bytesReceived($fixnum.Int64 value) => $_setInt64(3, value);
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasBytesReceived() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearBytesReceived() => $_clearField(4);
|
||||
|
||||
/// Total bytes the server announced. Zero means the server sent no
|
||||
/// content-length, so only `bytes_received` is meaningful.
|
||||
@$pb.TagNumber(5)
|
||||
$fixnum.Int64 get bytesTotal => $_getI64(4);
|
||||
@$pb.TagNumber(5)
|
||||
set bytesTotal($fixnum.Int64 value) => $_setInt64(4, value);
|
||||
@$pb.TagNumber(5)
|
||||
$core.bool hasBytesTotal() => $_has(4);
|
||||
@$pb.TagNumber(5)
|
||||
void clearBytesTotal() => $_clearField(5);
|
||||
|
||||
/// The named phase just finished.
|
||||
@$pb.TagNumber(6)
|
||||
$core.bool get phaseDone => $_getBF(5);
|
||||
@$pb.TagNumber(6)
|
||||
set phaseDone($core.bool value) => $_setBool(5, value);
|
||||
@$pb.TagNumber(6)
|
||||
$core.bool hasPhaseDone() => $_has(5);
|
||||
@$pb.TagNumber(6)
|
||||
void clearPhaseDone() => $_clearField(6);
|
||||
}
|
||||
|
||||
class UninstallModuleRequest extends $pb.GeneratedMessage {
|
||||
factory UninstallModuleRequest({
|
||||
$core.String? name,
|
||||
|
|
|
|||
|
|
@ -442,6 +442,21 @@ class HubAdminClient extends $grpc.Client {
|
|||
return $createUnaryCall(_$installModule, request, options: options);
|
||||
}
|
||||
|
||||
/// Same install, with live progress. Emits an update per phase and,
|
||||
/// during the download, as bytes arrive; the last message carries
|
||||
/// either the finished response or the error. Clients that only need
|
||||
/// the outcome keep using the unary InstallModule above — this exists
|
||||
/// so a waiting operator can see that a slow download is progressing
|
||||
/// rather than hung.
|
||||
$grpc.ResponseStream<$0.InstallProgressEvent> installModuleStream(
|
||||
$0.InstallModuleRequest request, {
|
||||
$grpc.CallOptions? options,
|
||||
}) {
|
||||
return $createStreamingCall(
|
||||
_$installModuleStream, $async.Stream.fromIterable([request]),
|
||||
options: options);
|
||||
}
|
||||
|
||||
/// Remove an installed module by name. Wipes the module
|
||||
/// directory and re-scans the registry; writes a
|
||||
/// `module.uninstalled` audit event. Surfaced by the Studio
|
||||
|
|
@ -956,6 +971,11 @@ class HubAdminClient extends $grpc.Client {
|
|||
'/chain.v1.HubAdmin/InstallModule',
|
||||
($0.InstallModuleRequest value) => value.writeToBuffer(),
|
||||
$0.InstallModuleResponse.fromBuffer);
|
||||
static final _$installModuleStream =
|
||||
$grpc.ClientMethod<$0.InstallModuleRequest, $0.InstallProgressEvent>(
|
||||
'/chain.v1.HubAdmin/InstallModuleStream',
|
||||
($0.InstallModuleRequest value) => value.writeToBuffer(),
|
||||
$0.InstallProgressEvent.fromBuffer);
|
||||
static final _$uninstallModule =
|
||||
$grpc.ClientMethod<$0.UninstallModuleRequest, $0.UninstallModuleResponse>(
|
||||
'/chain.v1.HubAdmin/UninstallModule',
|
||||
|
|
@ -1285,6 +1305,15 @@ abstract class HubAdminServiceBase extends $grpc.Service {
|
|||
($core.List<$core.int> value) =>
|
||||
$0.InstallModuleRequest.fromBuffer(value),
|
||||
($0.InstallModuleResponse value) => value.writeToBuffer()));
|
||||
$addMethod(
|
||||
$grpc.ServiceMethod<$0.InstallModuleRequest, $0.InstallProgressEvent>(
|
||||
'InstallModuleStream',
|
||||
installModuleStream_Pre,
|
||||
false,
|
||||
true,
|
||||
($core.List<$core.int> value) =>
|
||||
$0.InstallModuleRequest.fromBuffer(value),
|
||||
($0.InstallProgressEvent value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.UninstallModuleRequest,
|
||||
$0.UninstallModuleResponse>(
|
||||
'UninstallModule',
|
||||
|
|
@ -1737,6 +1766,15 @@ abstract class HubAdminServiceBase extends $grpc.Service {
|
|||
$async.Future<$0.InstallModuleResponse> installModule(
|
||||
$grpc.ServiceCall call, $0.InstallModuleRequest request);
|
||||
|
||||
$async.Stream<$0.InstallProgressEvent> installModuleStream_Pre(
|
||||
$grpc.ServiceCall $call,
|
||||
$async.Future<$0.InstallModuleRequest> $request) async* {
|
||||
yield* installModuleStream($call, await $request);
|
||||
}
|
||||
|
||||
$async.Stream<$0.InstallProgressEvent> installModuleStream(
|
||||
$grpc.ServiceCall call, $0.InstallModuleRequest request);
|
||||
|
||||
$async.Future<$0.UninstallModuleResponse> uninstallModule_Pre(
|
||||
$grpc.ServiceCall $call,
|
||||
$async.Future<$0.UninstallModuleRequest> $request) async {
|
||||
|
|
|
|||
|
|
@ -998,6 +998,73 @@ final $typed_data.Uint8List installModuleResponseDescriptor = $convert.base64Dec
|
|||
'IgASgJUgd2ZXJzaW9uEikKEGNhcGFiaWxpdHlfY291bnQYAyABKAVSD2NhcGFiaWxpdHlDb3Vu'
|
||||
'dA==');
|
||||
|
||||
@$core.Deprecated('Use installProgressEventDescriptor instead')
|
||||
const InstallProgressEvent$json = {
|
||||
'1': 'InstallProgressEvent',
|
||||
'2': [
|
||||
{
|
||||
'1': 'update',
|
||||
'3': 1,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.chain.v1.InstallProgressUpdate',
|
||||
'9': 0,
|
||||
'10': 'update'
|
||||
},
|
||||
{
|
||||
'1': 'finished',
|
||||
'3': 2,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.chain.v1.InstallModuleResponse',
|
||||
'9': 0,
|
||||
'10': 'finished'
|
||||
},
|
||||
{'1': 'failed', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'failed'},
|
||||
],
|
||||
'8': [
|
||||
{'1': 'event'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `InstallProgressEvent`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List installProgressEventDescriptor = $convert.base64Decode(
|
||||
'ChRJbnN0YWxsUHJvZ3Jlc3NFdmVudBI5CgZ1cGRhdGUYASABKAsyHy5jaGFpbi52MS5JbnN0YW'
|
||||
'xsUHJvZ3Jlc3NVcGRhdGVIAFIGdXBkYXRlEj0KCGZpbmlzaGVkGAIgASgLMh8uY2hhaW4udjEu'
|
||||
'SW5zdGFsbE1vZHVsZVJlc3BvbnNlSABSCGZpbmlzaGVkEhgKBmZhaWxlZBgDIAEoCUgAUgZmYW'
|
||||
'lsZWRCBwoFZXZlbnQ=');
|
||||
|
||||
@$core.Deprecated('Use installProgressUpdateDescriptor instead')
|
||||
const InstallProgressUpdate$json = {
|
||||
'1': 'InstallProgressUpdate',
|
||||
'2': [
|
||||
{'1': 'phase', '3': 1, '4': 1, '5': 9, '10': 'phase'},
|
||||
{'1': 'stage', '3': 2, '4': 1, '5': 9, '10': 'stage'},
|
||||
{
|
||||
'1': 'percent',
|
||||
'3': 3,
|
||||
'4': 1,
|
||||
'5': 2,
|
||||
'9': 0,
|
||||
'10': 'percent',
|
||||
'17': true
|
||||
},
|
||||
{'1': 'bytes_received', '3': 4, '4': 1, '5': 4, '10': 'bytesReceived'},
|
||||
{'1': 'bytes_total', '3': 5, '4': 1, '5': 4, '10': 'bytesTotal'},
|
||||
{'1': 'phase_done', '3': 6, '4': 1, '5': 8, '10': 'phaseDone'},
|
||||
],
|
||||
'8': [
|
||||
{'1': '_percent'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `InstallProgressUpdate`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List installProgressUpdateDescriptor = $convert.base64Decode(
|
||||
'ChVJbnN0YWxsUHJvZ3Jlc3NVcGRhdGUSFAoFcGhhc2UYASABKAlSBXBoYXNlEhQKBXN0YWdlGA'
|
||||
'IgASgJUgVzdGFnZRIdCgdwZXJjZW50GAMgASgCSABSB3BlcmNlbnSIAQESJQoOYnl0ZXNfcmVj'
|
||||
'ZWl2ZWQYBCABKARSDWJ5dGVzUmVjZWl2ZWQSHwoLYnl0ZXNfdG90YWwYBSABKARSCmJ5dGVzVG'
|
||||
'90YWwSHQoKcGhhc2VfZG9uZRgGIAEoCFIJcGhhc2VEb25lQgoKCF9wZXJjZW50');
|
||||
|
||||
@$core.Deprecated('Use uninstallModuleRequestDescriptor instead')
|
||||
const UninstallModuleRequest$json = {
|
||||
'1': 'UninstallModuleRequest',
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ typedef ProjectInfo = pb.ProjectInfo;
|
|||
typedef StoreEntry = pb.StoreEntry;
|
||||
typedef StoreSearchResponse = pb.StoreSearchResponse;
|
||||
typedef InstallModuleResponse = pb.InstallModuleResponse;
|
||||
typedef InstallProgressUpdate = pb.InstallProgressUpdate;
|
||||
typedef Payload = pb_common.Payload;
|
||||
typedef SubmitResponse = pb.SubmitResponse;
|
||||
// Live-stream (T2) + detached-invocation (T3) wire types, re-exported
|
||||
|
|
@ -831,6 +832,41 @@ class HubClient {
|
|||
));
|
||||
}
|
||||
|
||||
/// Install a module while watching it happen.
|
||||
///
|
||||
/// Emits an update per phase and, during the download, as bytes
|
||||
/// arrive. The stream completes when the install succeeds, and
|
||||
/// carries the hub's failure text as a stream error when it does
|
||||
/// not, so callers handle failures the same way as with
|
||||
/// [installModule]. Use [onFinished] to receive the installed
|
||||
/// module's name and version.
|
||||
///
|
||||
/// A caller that only needs the outcome should keep using the
|
||||
/// unary [installModule]; this exists so a waiting operator can
|
||||
/// see that a slow download is progressing rather than hung.
|
||||
Stream<InstallProgressUpdate> installModuleStreaming({
|
||||
required String source,
|
||||
String expectedSha256 = '',
|
||||
String version = '',
|
||||
void Function(InstallModuleResponse result)? onFinished,
|
||||
}) async* {
|
||||
final stream = _admin.installModuleStream(pb.InstallModuleRequest(
|
||||
source: source,
|
||||
expectedSha256: expectedSha256,
|
||||
version: version,
|
||||
));
|
||||
await for (final event in stream) {
|
||||
if (event.hasUpdate()) {
|
||||
yield event.update;
|
||||
} else if (event.hasFinished()) {
|
||||
onFinished?.call(event.finished);
|
||||
return;
|
||||
} else if (event.hasFailed()) {
|
||||
throw Exception(event.failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// All saved flows known to the hub. Each entry carries the
|
||||
/// flow name, on-disk path and byte size.
|
||||
Future<List<FlowSummary>> listFlows() async {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue