Skip to content

Commit 1394f15

Browse files
committed
chore(core): Improve gRPC interop in CloudException
Adds `toGrpcError` and retain both the gRPC and HTTP status codes for each exception.
1 parent 4ab8838 commit 1394f15

File tree

5 files changed

+643
-16
lines changed

5 files changed

+643
-16
lines changed

packages/celest_core/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## 1.0.2-wip
22

33
- chore: Update license
4+
- chore: Improve gRPC interop
45

56
## 1.0.1
67

packages/celest_core/lib/src/exception/cloud_exception.dart

Lines changed: 132 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ import 'dart:core' hide UnimplementedError;
88
import 'package:celest_core/src/exception/celest_exception.dart';
99
import 'package:celest_core/src/http/http_error.dart';
1010
import 'package:celest_core/src/http/http_status.dart';
11+
import 'package:celest_core/src/proto/google/protobuf/struct.pb.dart';
1112
import 'package:celest_core/src/serialization/json_value.dart';
1213
import 'package:celest_core/src/util/json.dart';
1314
import 'package:grpc/grpc_or_grpcweb.dart' show StatusCode, GrpcError;
1415
import 'package:http/http.dart' as http show Response;
1516
import 'package:json_annotation/json_annotation.dart' show JsonKey;
1617
import 'package:meta/meta.dart';
18+
import 'package:protobuf/protobuf.dart';
19+
import 'package:protobuf/src/protobuf/json_parsing_context.dart';
20+
import 'package:protobuf/src/protobuf/mixins/well_known.dart';
1721

1822
/// An exception thrown by a Cloud Widget.
1923
abstract mixin class CloudException implements CelestException {
@@ -150,82 +154,66 @@ abstract mixin class CloudException implements CelestException {
150154
StatusCode.aborted => AbortedException.of(
151155
message: message,
152156
details: details,
153-
code: code,
154157
),
155158
StatusCode.alreadyExists => AlreadyExistsException.of(
156159
message: message,
157160
details: details,
158-
code: code,
159161
),
160162
StatusCode.cancelled => CancelledException.of(
161163
message: message,
162164
details: details,
163-
code: code,
164165
),
165166
StatusCode.dataLoss => DataLossError.of(
166167
message: message,
167168
details: details,
168-
code: code,
169169
),
170170
StatusCode.deadlineExceeded => DeadlineExceededError.of(
171171
message: message,
172172
details: details,
173-
code: code,
174173
),
175174
StatusCode.failedPrecondition => FailedPreconditionException.of(
176175
message: message,
177176
details: details,
178-
code: code,
179177
),
180178
StatusCode.internal => InternalServerError.of(
181179
message: message,
182180
details: details,
183-
code: code,
184181
),
185182
StatusCode.invalidArgument => BadRequestException.of(
186183
message: message,
187184
details: details,
188-
code: code,
189185
),
190186
StatusCode.notFound => NotFoundException.of(
191187
message: message,
192188
details: details,
193-
code: code,
194189
),
195190
StatusCode.outOfRange => OutOfRangeException.of(
196191
message: message,
197192
details: details,
198-
code: code,
199193
),
200194
StatusCode.permissionDenied => PermissionDeniedException.of(
201195
message: message,
202196
details: details,
203-
code: code,
204197
),
205198
StatusCode.resourceExhausted => ResourceExhaustedException.of(
206199
message: message,
207200
details: details,
208-
code: code,
209201
),
210202
StatusCode.unauthenticated => UnauthorizedException.of(
211203
message: message,
212204
details: details,
213-
code: code,
214205
),
215206
StatusCode.unavailable => UnavailableError.of(
216207
message: message,
217208
details: details,
218-
code: code,
219209
),
220210
StatusCode.unimplemented => UnimplementedError.of(
221211
message: message,
222212
details: details,
223-
code: code,
224213
),
225214
_ => UnknownError.of(
226215
message: message,
227216
details: details,
228-
code: code,
229217
),
230218
};
231219
}
@@ -380,12 +368,44 @@ abstract mixin class CloudException implements CelestException {
380368
/// The code associated with the exception.
381369
int get code;
382370

371+
/// The gRPC status associated with the exception.
372+
int get grpcStatus => StatusCode.unknown;
373+
374+
/// The HTTP status associated with the exception.
375+
HttpStatus get httpStatus;
376+
383377
@override
384378
String get message;
385379

386380
/// Any details associated with the exception.
387381
JsonValue? get details;
388382

383+
/// Converts the exception to a gRPC error.
384+
GrpcError toGrpcError() {
385+
GeneratedMessage convert(Object? o) {
386+
final value = Value();
387+
ValueMixin.fromProto3JsonHelper(
388+
value,
389+
o,
390+
const TypeRegistry.empty(),
391+
JsonParsingContext(false, false, false),
392+
);
393+
return value;
394+
}
395+
396+
return GrpcError.custom(
397+
grpcStatus,
398+
message,
399+
switch (details) {
400+
null => null,
401+
final JsonList list => [
402+
for (final value in list) convert(value),
403+
],
404+
final JsonValue value => [convert(value)],
405+
},
406+
);
407+
}
408+
389409
@override
390410
String toString() {
391411
final buf = StringBuffer('$runtimeType: $message');
@@ -428,6 +448,12 @@ class CancelledException with CloudException {
428448

429449
@override
430450
final JsonValue? details;
451+
452+
@override
453+
int get grpcStatus => StatusCode.cancelled;
454+
455+
@override
456+
HttpStatus get httpStatus => HttpStatus.clientClosedRequest;
431457
}
432458

433459
/// {@template celest_core.exception.unknown_error}
@@ -459,6 +485,12 @@ class UnknownError extends Error with CloudException {
459485

460486
@override
461487
final JsonValue? details;
488+
489+
@override
490+
int get grpcStatus => StatusCode.unknown;
491+
492+
@override
493+
HttpStatus get httpStatus => HttpStatus.internalServerError;
462494
}
463495

464496
/// {@template celest_core.exception.bad_request_exception}
@@ -502,6 +534,12 @@ abstract base class BadRequestException extends CloudException {
502534

503535
@override
504536
final JsonValue? details;
537+
538+
@override
539+
int get grpcStatus => StatusCode.invalidArgument;
540+
541+
@override
542+
HttpStatus get httpStatus => HttpStatus.badRequest;
505543
}
506544

507545
final class _BadRequestException extends BadRequestException {
@@ -544,6 +582,12 @@ class UnauthorizedException with CloudException {
544582

545583
@override
546584
final JsonValue? details;
585+
586+
@override
587+
int get grpcStatus => StatusCode.unauthenticated;
588+
589+
@override
590+
HttpStatus get httpStatus => HttpStatus.unauthorized;
547591
}
548592

549593
/// {@template celest_core.exception.not_found_exception}
@@ -576,6 +620,12 @@ class NotFoundException with CloudException {
576620

577621
@override
578622
final JsonValue? details;
623+
624+
@override
625+
int get grpcStatus => StatusCode.notFound;
626+
627+
@override
628+
HttpStatus get httpStatus => HttpStatus.notFound;
579629
}
580630

581631
/// {@template celest_core.exception.already_exists_exception}
@@ -607,6 +657,12 @@ class AlreadyExistsException with CloudException {
607657

608658
@override
609659
final JsonValue? details;
660+
661+
@override
662+
int get grpcStatus => StatusCode.alreadyExists;
663+
664+
@override
665+
HttpStatus get httpStatus => HttpStatus.conflict;
610666
}
611667

612668
/// {@template celest_core.exception.permission_denied_exception}
@@ -642,6 +698,12 @@ class PermissionDeniedException with CloudException {
642698

643699
@override
644700
final JsonValue? details;
701+
702+
@override
703+
int get grpcStatus => StatusCode.permissionDenied;
704+
705+
@override
706+
HttpStatus get httpStatus => HttpStatus.forbidden;
645707
}
646708

647709
/// {@template celest_core.exception.resource_exhausted_exception}
@@ -676,6 +738,12 @@ class ResourceExhaustedException with CloudException {
676738

677739
@override
678740
final JsonValue? details;
741+
742+
@override
743+
int get grpcStatus => StatusCode.resourceExhausted;
744+
745+
@override
746+
HttpStatus get httpStatus => HttpStatus.tooManyRequests;
679747
}
680748

681749
/// {@template celest_core.exception.failed_precondition_exception}
@@ -711,6 +779,12 @@ class FailedPreconditionException with CloudException {
711779

712780
@override
713781
final JsonValue? details;
782+
783+
@override
784+
int get grpcStatus => StatusCode.failedPrecondition;
785+
786+
@override
787+
HttpStatus get httpStatus => HttpStatus.preconditionFailed;
714788
}
715789

716790
/// {@template celest_core.exception.aborted_exception}
@@ -742,6 +816,12 @@ class AbortedException with CloudException {
742816

743817
@override
744818
final JsonValue? details;
819+
820+
@override
821+
int get grpcStatus => StatusCode.aborted;
822+
823+
@override
824+
HttpStatus get httpStatus => HttpStatus.conflict;
745825
}
746826

747827
/// {@template celest_core.exception.out_of_range_exception}
@@ -773,6 +853,12 @@ class OutOfRangeException with CloudException {
773853

774854
@override
775855
final JsonValue? details;
856+
857+
@override
858+
int get grpcStatus => StatusCode.outOfRange;
859+
860+
@override
861+
HttpStatus get httpStatus => HttpStatus.requestedRangeNotSatisfiable;
776862
}
777863

778864
/// {@template celest_core.exception.unimplemented_error}
@@ -804,6 +890,12 @@ class UnimplementedError extends core.UnimplementedError with CloudException {
804890

805891
@override
806892
final JsonValue? details;
893+
894+
@override
895+
int get grpcStatus => StatusCode.unimplemented;
896+
897+
@override
898+
HttpStatus get httpStatus => HttpStatus.notImplemented;
807899
}
808900

809901
/// {@template celest_core.exception.internal_server_error}
@@ -836,6 +928,12 @@ class InternalServerError extends Error with CloudException {
836928

837929
@override
838930
final JsonValue? details;
931+
932+
@override
933+
int get grpcStatus => StatusCode.internal;
934+
935+
@override
936+
HttpStatus get httpStatus => HttpStatus.internalServerError;
839937
}
840938

841939
/// {@template celest_core.exception.unavailable_error}
@@ -867,6 +965,12 @@ class UnavailableError extends Error with CloudException {
867965

868966
@override
869967
final JsonValue? details;
968+
969+
@override
970+
int get grpcStatus => StatusCode.unavailable;
971+
972+
@override
973+
HttpStatus get httpStatus => HttpStatus.serviceUnavailable;
870974
}
871975

872976
/// {@template celest_core.exception.data_loss_error}
@@ -898,6 +1002,12 @@ class DataLossError extends Error with CloudException {
8981002

8991003
@override
9001004
final JsonValue? details;
1005+
1006+
@override
1007+
int get grpcStatus => StatusCode.dataLoss;
1008+
1009+
@override
1010+
HttpStatus get httpStatus => HttpStatus.internalServerError;
9011011
}
9021012

9031013
/// {@template celest_core.exception.deadline_exceeded_error}
@@ -929,4 +1039,10 @@ class DeadlineExceededError extends Error with CloudException {
9291039

9301040
@override
9311041
final JsonValue? details;
1042+
1043+
@override
1044+
int get grpcStatus => StatusCode.deadlineExceeded;
1045+
1046+
@override
1047+
HttpStatus get httpStatus => HttpStatus.gatewayTimeout;
9321048
}

0 commit comments

Comments
 (0)