diff --git a/.auto-claude-status b/.auto-claude-status index b749234..82dbbee 100644 --- a/.auto-claude-status +++ b/.auto-claude-status @@ -1,15 +1,15 @@ { "active": true, - "spec": "001-normalize-code-update-packages-widgetify-component", + "spec": "002-migrate-api-routes-from-http-to-grpc", "state": "building", "subtasks": { - "completed": 11, - "total": 14, + "completed": 10, + "total": 13, "in_progress": 1, "failed": 0 }, "phase": { - "current": "Cleanup and Verification", + "current": "Provider Integration", "id": null, "total": 3 }, @@ -18,8 +18,8 @@ "max": 1 }, "session": { - "number": 12, - "started_at": "2026-01-20T11:20:56.182893" + "number": 11, + "started_at": "2026-01-20T12:45:59.858836" }, - "last_update": "2026-01-20T11:47:55.069999" + "last_update": "2026-01-20T13:09:16.962328" } \ No newline at end of file diff --git a/.claude_settings.json b/.claude_settings.json index 328e8a0..c7c3ffc 100644 --- a/.claude_settings.json +++ b/.claude_settings.json @@ -11,14 +11,14 @@ "Edit(./**)", "Glob(./**)", "Grep(./**)", - "Read(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/001-normalize-code-update-packages-widgetify-component/**)", - "Write(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/001-normalize-code-update-packages-widgetify-component/**)", - "Edit(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/001-normalize-code-update-packages-widgetify-component/**)", - "Glob(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/001-normalize-code-update-packages-widgetify-component/**)", - "Grep(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/001-normalize-code-update-packages-widgetify-component/**)", - "Read(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/001-normalize-code-update-packages-widgetify-component/.auto-claude/specs/001-normalize-code-update-packages-widgetify-component/**)", - "Write(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/001-normalize-code-update-packages-widgetify-component/.auto-claude/specs/001-normalize-code-update-packages-widgetify-component/**)", - "Edit(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/001-normalize-code-update-packages-widgetify-component/.auto-claude/specs/001-normalize-code-update-packages-widgetify-component/**)", + "Read(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/002-migrate-api-routes-from-http-to-grpc/**)", + "Write(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/002-migrate-api-routes-from-http-to-grpc/**)", + "Edit(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/002-migrate-api-routes-from-http-to-grpc/**)", + "Glob(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/002-migrate-api-routes-from-http-to-grpc/**)", + "Grep(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/002-migrate-api-routes-from-http-to-grpc/**)", + "Read(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/002-migrate-api-routes-from-http-to-grpc/.auto-claude/specs/002-migrate-api-routes-from-http-to-grpc/**)", + "Write(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/002-migrate-api-routes-from-http-to-grpc/.auto-claude/specs/002-migrate-api-routes-from-http-to-grpc/**)", + "Edit(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/worktrees/tasks/002-migrate-api-routes-from-http-to-grpc/.auto-claude/specs/002-migrate-api-routes-from-http-to-grpc/**)", "Read(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/**)", "Write(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/**)", "Edit(/Users/mathias/Documents/workspaces/plan-b/ionic-planb-logistic-app-flutter/.auto-claude/**)", diff --git a/lib/api/grpc_client.dart b/lib/api/grpc_client.dart new file mode 100644 index 0000000..20b2ac2 --- /dev/null +++ b/lib/api/grpc_client.dart @@ -0,0 +1,550 @@ +import 'dart:async'; + +import 'package:grpc/grpc.dart'; + +import '../generated/delivery_service.pbgrpc.dart'; +import '../models/delivery.dart'; +import '../models/delivery_address.dart'; +import '../models/delivery_contact.dart'; +import '../models/delivery_order.dart'; +import '../models/delivery_route.dart'; +import '../models/user_info.dart'; +import '../services/auth_service.dart'; +import 'grpc_config.dart'; +import 'types.dart'; + +// ignore_for_file: unused_element + +/// gRPC-based CQRS API client for Plan B Logistics. +/// +/// This client wraps the generated DeliveryServiceClient with `Result` error +/// handling patterns consistent with [CqrsApiClient]. It provides: +/// - Lazy channel initialization +/// - Authentication via Bearer token in gRPC metadata +/// - Automatic token refresh on UNAUTHENTICATED errors +/// - Proper gRPC error to [ApiError] mapping +/// +/// Usage: +/// ```dart +/// final client = GrpcCqrsApiClient( +/// config: GrpcConfig.development, +/// authService: authService, +/// ); +/// +/// final result = await client.getDeliveryRoutes(); +/// result.when( +/// success: (routes) => handleRoutes(routes), +/// onError: (error) => handleError(error), +/// ); +/// ``` +class GrpcCqrsApiClient { + final GrpcConfig config; + final AuthService? authService; + + ClientChannel? _channel; + DeliveryServiceClient? _deliveryClient; + + GrpcCqrsApiClient({ + required this.config, + this.authService, + }); + + /// Returns the gRPC channel, creating it lazily if needed. + /// + /// The channel is configured based on [config.useTls] for development + /// (insecure) vs production (TLS) environments. + ClientChannel get channel { + if (_channel == null) { + final credentials = config.useTls + ? const ChannelCredentials.secure() + : const ChannelCredentials.insecure(); + + _channel = ClientChannel( + config.host, + port: config.port, + options: ChannelOptions( + credentials: credentials, + connectionTimeout: config.timeout, + idleTimeout: const Duration(minutes: 5), + ), + ); + } + return _channel!; + } + + /// Returns the DeliveryService client, creating it lazily if needed. + DeliveryServiceClient get deliveryClient { + _deliveryClient ??= DeliveryServiceClient(channel); + return _deliveryClient!; + } + + /// Builds [CallOptions] with authentication metadata. + /// + /// Includes Bearer token in metadata if [authService] is configured and + /// a valid token is available. Uses [AuthService.ensureValidToken] to + /// proactively refresh tokens that are expiring soon. + Future _buildCallOptions() async { + final metadata = {}; + + if (authService != null) { + final token = await authService!.ensureValidToken(); + if (token != null) { + metadata['authorization'] = 'Bearer $token'; + } + } + + return CallOptions( + metadata: metadata, + timeout: config.timeout, + ); + } + + /// Merges base [CallOptions] with additional options for a specific call. + CallOptions _mergeOptions(CallOptions base, CallOptions? additional) { + if (additional == null) return base; + + return CallOptions( + metadata: { + ...base.metadata, + ...additional.metadata, + }, + timeout: additional.timeout ?? base.timeout, + providers: [ + ...base.metadataProviders, + ...additional.metadataProviders, + ], + ); + } + + /// Maps gRPC [GrpcError] to [ApiError] for consistent error handling. + /// + /// Maps common gRPC status codes to appropriate [ApiErrorType]: + /// - UNAUTHENTICATED (16) -> HTTP 401 + /// - PERMISSION_DENIED (7) -> HTTP 403 + /// - NOT_FOUND (5) -> HTTP 404 + /// - INVALID_ARGUMENT (3) -> Validation error + /// - DEADLINE_EXCEEDED (4) -> Timeout + /// - UNAVAILABLE (14) -> Network error + /// - Other codes -> Unknown error + ApiError _mapGrpcError(GrpcError error) { + switch (error.code) { + case StatusCode.unauthenticated: + return ApiError.http( + statusCode: 401, + message: error.message ?? 'Authentication required', + ); + case StatusCode.permissionDenied: + return ApiError.http( + statusCode: 403, + message: error.message ?? 'Permission denied', + ); + case StatusCode.notFound: + return ApiError.http( + statusCode: 404, + message: error.message ?? 'Resource not found', + ); + case StatusCode.invalidArgument: + return ApiError.validation( + error.message ?? 'Invalid request', + null, + ); + case StatusCode.deadlineExceeded: + return ApiError.timeout(); + case StatusCode.unavailable: + return ApiError.network( + error.message ?? 'Service unavailable', + ); + case StatusCode.internal: + return ApiError.http( + statusCode: 500, + message: error.message ?? 'Internal server error', + ); + default: + return ApiError.unknown( + error.message ?? 'gRPC error: ${error.codeName}', + exception: error, + ); + } + } + + /// Executes a gRPC call with `Result` error handling and authentication. + /// + /// This is the core method that wraps gRPC calls with: + /// - Authentication token injection + /// - Automatic token refresh on UNAUTHENTICATED errors (single retry) + /// - gRPC error to ApiError mapping + /// - Timeout handling + /// + /// [grpcCall] is the actual gRPC method invocation. + /// [isRetry] tracks whether this is a retry after token refresh. + Future> _executeWithAuth( + Future Function(CallOptions options) grpcCall, { + bool isRetry = false, + }) async { + try { + final options = await _buildCallOptions(); + final result = await grpcCall(options); + return Result.success(result); + } on GrpcError catch (error) { + // Handle UNAUTHENTICATED by attempting token refresh (once) + if (error.code == StatusCode.unauthenticated && + !isRetry && + authService != null) { + final refreshResult = await authService!.refreshAccessToken(); + return refreshResult.when( + success: (token) => _executeWithAuth(grpcCall, isRetry: true), + onError: (refreshError) => Result.error(_mapGrpcError(error)), + cancelled: () => Result.error(_mapGrpcError(error)), + ); + } + return Result.error(_mapGrpcError(error)); + } on TimeoutException { + return Result.error(ApiError.timeout()); + } catch (e, stackTrace) { + return Result.error( + ApiError.unknown( + 'gRPC call failed: ${e.toString()}', + exception: Exception(stackTrace.toString()), + ), + ); + } + } + + /// Executes a gRPC call that returns void (for commands). + /// + /// Similar to [_executeWithAuth] but for operations that don't return + /// meaningful data (commands that return success/failure). + Future> _executeCommandWithAuth( + Future Function(CallOptions options) grpcCall, { + bool isRetry = false, + }) async { + try { + final options = await _buildCallOptions(); + await grpcCall(options); + return Result.success(null); + } on GrpcError catch (error) { + if (error.code == StatusCode.unauthenticated && + !isRetry && + authService != null) { + final refreshResult = await authService!.refreshAccessToken(); + return refreshResult.when( + success: (token) => + _executeCommandWithAuth(grpcCall, isRetry: true), + onError: (refreshError) => Result.error(_mapGrpcError(error)), + cancelled: () => Result.error(_mapGrpcError(error)), + ); + } + return Result.error(_mapGrpcError(error)); + } on TimeoutException { + return Result.error(ApiError.timeout()); + } catch (e, stackTrace) { + return Result.error( + ApiError.unknown( + 'gRPC command failed: ${e.toString()}', + exception: Exception(stackTrace.toString()), + ), + ); + } + } + + // ============================================================ + // Query Methods + // ============================================================ + + /// Gets all delivery routes. + /// + /// Returns a [Result] containing a list of [DeliveryRoute] objects. + /// Maps the gRPC [DeliveryRoutesResponse] to domain models. + /// + /// Example: + /// ```dart + /// final result = await client.getDeliveryRoutes(); + /// result.when( + /// success: (routes) => displayRoutes(routes), + /// onError: (error) => showError(error.message), + /// ); + /// ``` + Future>> getDeliveryRoutes() async { + final result = await _executeWithAuth( + (options) => deliveryClient.getDeliveryRoutes(Empty(), options: options), + ); + + return result.when( + success: (response) { + final routes = response.routes.map(_mapDeliveryRouteProto).toList(); + return Result.success(routes); + }, + onError: (error) => Result.error(error), + ); + } + + /// Maps a [DeliveryRouteProto] to a [DeliveryRoute] domain model. + DeliveryRoute _mapDeliveryRouteProto(DeliveryRouteProto proto) { + return DeliveryRoute( + id: proto.id, + routeId: proto.routeId, + name: proto.name, + routeName: proto.routeName, + deliveriesCount: proto.deliveriesCount, + deliveredCount: proto.deliveredCount, + completed: proto.completed, + createdAt: proto.createdAt, + ); + } + + /// Gets deliveries for a specific route fragment. + /// + /// Returns a [Result] containing a list of [Delivery] objects for the + /// specified [routeFragmentId]. Maps the gRPC [DeliveriesResponse] to + /// domain models. + /// + /// Example: + /// ```dart + /// final result = await client.getDeliveries(routeFragmentId: 123); + /// result.when( + /// success: (deliveries) => displayDeliveries(deliveries), + /// onError: (error) => showError(error.message), + /// ); + /// ``` + Future>> getDeliveries({ + required int routeFragmentId, + }) async { + final request = GetDeliveriesRequest(routeFragmentId: routeFragmentId); + + final result = await _executeWithAuth( + (options) => deliveryClient.getDeliveries(request, options: options), + ); + + return result.when( + success: (response) { + final deliveries = + response.deliveries.map(_mapDeliveryProto).toList(); + return Result.success(deliveries); + }, + onError: (error) => Result.error(error), + ); + } + + /// Maps a [DeliveryProto] to a [Delivery] domain model. + Delivery _mapDeliveryProto(DeliveryProto proto) { + return Delivery( + id: proto.id, + routeFragmentId: proto.routeFragmentId, + deliveryIndex: proto.deliveryIndex, + orders: proto.orders.map(_mapDeliveryOrderProto).toList(), + deliveredBy: + proto.hasDeliveredBy() ? _mapUserInfoProto(proto.deliveredBy) : null, + deliveryAddress: proto.hasDeliveryAddress() + ? _mapDeliveryAddressProto(proto.deliveryAddress) + : null, + deliveredAt: proto.hasDeliveredAt() ? proto.deliveredAt : null, + skippedAt: proto.hasSkippedAt() ? proto.skippedAt : null, + createdAt: proto.createdAt, + updatedAt: proto.hasUpdatedAt() ? proto.updatedAt : null, + delivered: proto.delivered, + hasBeenSkipped: proto.hasBeenSkipped, + isSkipped: proto.isSkipped, + name: proto.name, + ); + } + + /// Maps a [DeliveryAddressProto] to a [DeliveryAddress] domain model. + DeliveryAddress _mapDeliveryAddressProto(DeliveryAddressProto proto) { + return DeliveryAddress( + id: proto.id, + line1: proto.hasLine1() ? proto.line1 : null, + line2: proto.hasLine2() ? proto.line2 : null, + postalCode: proto.hasPostalCode() ? proto.postalCode : null, + city: proto.hasCity() ? proto.city : null, + subdivision: proto.hasSubdivision() ? proto.subdivision : null, + countryCode: proto.hasCountryCode() ? proto.countryCode : null, + latitude: proto.hasLatitude() ? proto.latitude : null, + longitude: proto.hasLongitude() ? proto.longitude : null, + formattedAddress: + proto.hasFormattedAddress() ? proto.formattedAddress : null, + ); + } + + /// Maps a [DeliveryOrderProto] to a [DeliveryOrder] domain model. + DeliveryOrder _mapDeliveryOrderProto(DeliveryOrderProto proto) { + return DeliveryOrder( + id: proto.id, + isNewCustomer: proto.isNewCustomer, + note: proto.hasNote() ? proto.note : null, + totalAmount: proto.totalAmount, + totalPaid: proto.hasTotalPaid() ? proto.totalPaid : null, + totalItems: proto.hasTotalItems() ? proto.totalItems : null, + contacts: proto.contacts.map(_mapDeliveryContactProto).toList(), + contact: + proto.hasContact() ? _mapDeliveryContactProto(proto.contact) : null, + ); + } + + /// Maps a [DeliveryContactProto] to a [DeliveryContact] domain model. + DeliveryContact _mapDeliveryContactProto(DeliveryContactProto proto) { + return DeliveryContact( + firstName: proto.firstName, + lastName: proto.hasLastName() ? proto.lastName : null, + fullName: proto.fullName, + phoneNumber: proto.hasPhoneNumber() ? proto.phoneNumber : null, + ); + } + + /// Maps a [UserInfoProto] to a [UserInfo] domain model. + UserInfo _mapUserInfoProto(UserInfoProto proto) { + return UserInfo( + id: proto.id, + firstName: proto.firstName, + lastName: proto.hasLastName() ? proto.lastName : null, + fullName: proto.fullName, + ); + } + + // ============================================================ + // Command Methods + // ============================================================ + + /// Marks a delivery as completed. + /// + /// Returns a [Result] indicating success or failure. Optionally accepts + /// a [deliveredAt] timestamp; if not provided, the server will use the + /// current time. + /// + /// Example: + /// ```dart + /// final result = await client.completeDelivery(deliveryId: 123); + /// result.when( + /// success: (_) => showSuccess('Delivery completed'), + /// onError: (error) => showError(error.message), + /// ); + /// ``` + Future> completeDelivery({ + required int deliveryId, + String? deliveredAt, + }) async { + final request = CompleteDeliveryRequest( + deliveryId: deliveryId, + deliveredAt: deliveredAt, + ); + + final result = await _executeWithAuth( + (options) => deliveryClient.completeDelivery(request, options: options), + ); + + return result.when( + success: (response) { + if (response.success) { + return Result.success(null); + } + return Result.error( + ApiError.unknown(response.message.isNotEmpty + ? response.message + : 'Failed to complete delivery'), + ); + }, + onError: (error) => Result.error(error), + ); + } + + /// Marks a delivery as uncompleted. + /// + /// Returns a [Result] indicating success or failure. Use this to revert + /// a previously completed delivery back to pending status. + /// + /// Example: + /// ```dart + /// final result = await client.markDeliveryAsUncompleted(deliveryId: 123); + /// result.when( + /// success: (_) => showSuccess('Delivery marked as uncompleted'), + /// onError: (error) => showError(error.message), + /// ); + /// ``` + Future> markDeliveryAsUncompleted({ + required int deliveryId, + }) async { + final request = MarkDeliveryUncompletedRequest( + deliveryId: deliveryId, + ); + + final result = await _executeWithAuth( + (options) => + deliveryClient.markDeliveryUncompleted(request, options: options), + ); + + return result.when( + success: (response) { + if (response.success) { + return Result.success(null); + } + return Result.error( + ApiError.unknown(response.message.isNotEmpty + ? response.message + : 'Failed to mark delivery as uncompleted'), + ); + }, + onError: (error) => Result.error(error), + ); + } + + /// Skips a delivery. + /// + /// Returns a [Result] indicating success or failure. Use this when a + /// delivery cannot be completed and needs to be skipped. + /// + /// Example: + /// ```dart + /// final result = await client.skipDelivery(deliveryId: 123); + /// result.when( + /// success: (_) => showSuccess('Delivery skipped'), + /// onError: (error) => showError(error.message), + /// ); + /// ``` + Future> skipDelivery({ + required int deliveryId, + }) async { + final request = SkipDeliveryRequest( + deliveryId: deliveryId, + ); + + final result = await _executeWithAuth( + (options) => deliveryClient.skipDelivery(request, options: options), + ); + + return result.when( + success: (response) { + if (response.success) { + return Result.success(null); + } + return Result.error( + ApiError.unknown(response.message.isNotEmpty + ? response.message + : 'Failed to skip delivery'), + ); + }, + onError: (error) => Result.error(error), + ); + } + + /// Shuts down the gRPC channel and releases resources. + /// + /// Should be called when the client is no longer needed to properly + /// clean up network resources. + Future shutdown() async { + await _channel?.shutdown(); + _channel = null; + _deliveryClient = null; + } + + /// Terminates the gRPC channel immediately. + /// + /// Unlike [shutdown], this does not wait for pending calls to complete. + /// Use this for emergency cleanup or when the app is terminating. + Future terminate() async { + await _channel?.terminate(); + _channel = null; + _deliveryClient = null; + } + + /// Returns true if the channel is currently active. + bool get isConnected => _channel != null; +} diff --git a/lib/api/grpc_config.dart b/lib/api/grpc_config.dart new file mode 100644 index 0000000..5266109 --- /dev/null +++ b/lib/api/grpc_config.dart @@ -0,0 +1,59 @@ +/// Configuration for gRPC client connections. +/// +/// Provides separate configurations for development and production environments +/// with appropriate security settings for each. +class GrpcConfig { + /// The gRPC server host address. + final String host; + + /// The gRPC server port. + final int port; + + /// Connection timeout duration. + final Duration timeout; + + /// Whether to use TLS for secure connections. + /// + /// When false, uses insecure (plaintext) credentials suitable only for + /// development environments. + final bool useTls; + + /// Whether to allow self-signed certificates. + /// + /// Only applicable when [useTls] is true. Useful for development + /// environments with self-signed certificates. + final bool allowSelfSignedCertificate; + + const GrpcConfig({ + required this.host, + required this.port, + this.timeout = const Duration(seconds: 30), + this.useTls = true, + this.allowSelfSignedCertificate = false, + }); + + /// Development configuration pointing to local/development gRPC server. + /// + /// Uses plaintext (insecure) credentials for development convenience. + static const GrpcConfig development = GrpcConfig( + host: '192.168.88.228', + port: 5011, + timeout: Duration(seconds: 30), + useTls: false, + allowSelfSignedCertificate: true, + ); + + /// Production configuration for the Plan B Logistics gRPC server. + /// + /// Uses TLS for secure communication. + static const GrpcConfig production = GrpcConfig( + host: 'grpc-route.goutezplanb.com', + port: 443, + timeout: Duration(seconds: 30), + useTls: true, + allowSelfSignedCertificate: false, + ); + + /// Returns the full address string in the format "host:port". + String get address => '$host:$port'; +} diff --git a/lib/api/grpc_discovery.dart b/lib/api/grpc_discovery.dart new file mode 100644 index 0000000..5748a8d --- /dev/null +++ b/lib/api/grpc_discovery.dart @@ -0,0 +1,352 @@ +import 'dart:async'; + +import 'package:grpc/grpc.dart'; + +import 'grpc_config.dart'; +import '../generated/reflection.pbgrpc.dart'; +import '../generated/descriptor.pb.dart'; + +/// Exception types for gRPC discovery operations. +abstract class GrpcDiscoveryException implements Exception { + final String message; + + GrpcDiscoveryException(this.message); + + @override + String toString() => '$runtimeType: $message'; +} + +/// Thrown when the gRPC reflection service returns an error. +class ReflectionException extends GrpcDiscoveryException { + final int errorCode; + + ReflectionException(super.message, this.errorCode); + + @override + String toString() => 'ReflectionException: $message (code: $errorCode)'; +} + +/// Thrown when connection to the server fails. +class ConnectionException extends GrpcDiscoveryException { + ConnectionException(super.message); +} + +/// Represents a discovered gRPC service with its methods. +class DiscoveredService { + final String name; + final List methods; + + const DiscoveredService({ + required this.name, + required this.methods, + }); + + @override + String toString() => 'DiscoveredService($name, methods: ${methods.length})'; +} + +/// Represents a method within a discovered gRPC service. +class DiscoveredMethod { + final String name; + final String inputType; + final String outputType; + final bool clientStreaming; + final bool serverStreaming; + + const DiscoveredMethod({ + required this.name, + required this.inputType, + required this.outputType, + required this.clientStreaming, + required this.serverStreaming, + }); + + @override + String toString() => 'DiscoveredMethod($name)'; +} + +/// Client for discovering gRPC services using server reflection. +/// +/// Connects to a gRPC server's reflection service to enumerate available +/// services and their method signatures. This is useful for development +/// and proto generation. +/// +/// Example usage: +/// ```dart +/// final discovery = GrpcDiscoveryClient(config: GrpcConfig.development); +/// try { +/// final services = await discovery.listServices(); +/// for (final service in services) { +/// print('Service: $service'); +/// } +/// } finally { +/// await discovery.close(); +/// } +/// ``` +class GrpcDiscoveryClient { + final GrpcConfig config; + late final ClientChannel _channel; + late final ServerReflectionClient _stub; + bool _isInitialized = false; + + GrpcDiscoveryClient({required this.config}); + + /// Initializes the gRPC channel and reflection client. + /// + /// This is called automatically on first use, but can be called + /// explicitly to verify connection. + void _ensureInitialized() { + if (_isInitialized) return; + + _channel = ClientChannel( + config.host, + port: config.port, + options: ChannelOptions( + credentials: config.useTls + ? const ChannelCredentials.secure() + : const ChannelCredentials.insecure(), + connectionTimeout: config.timeout, + ), + ); + _stub = ServerReflectionClient(_channel); + _isInitialized = true; + } + + /// Lists all available services on the server. + /// + /// Returns a list of fully qualified service names. + /// Excludes the reflection service itself by default. + /// + /// Throws [ConnectionException] if connection fails. + /// Throws [ReflectionException] if reflection service returns an error. + Future> listServices({bool includeReflection = false}) async { + _ensureInitialized(); + + try { + final responseStream = _stub.serverReflectionInfo( + _createRequestStream([ + ServerReflectionRequest()..listServices = '', + ]), + ); + + final responses = await responseStream.toList(); + + if (responses.isEmpty) { + throw ReflectionException('No response from reflection service', 0); + } + + final response = responses.first; + if (response.hasErrorResponse()) { + throw ReflectionException( + response.errorResponse.errorMessage, + response.errorResponse.errorCode, + ); + } + + List services = response.listServicesResponse.service + .map((s) => s.name) + .toList(); + + if (!includeReflection) { + services = services + .where((s) => !s.contains('reflection')) + .toList(); + } + + return services; + } on GrpcError catch (e) { + throw ConnectionException('Failed to connect to ${config.address}: ${e.message}'); + } + } + + /// Gets file descriptors for a symbol (service, message, etc.). + /// + /// Returns a list of [FileDescriptorProto] that define the symbol + /// and all its transitive dependencies. + /// + /// Throws [ConnectionException] if connection fails. + /// Throws [ReflectionException] if symbol not found or other error. + Future> getFileDescriptorsForSymbol( + String symbol, + ) async { + _ensureInitialized(); + + try { + final responseStream = _stub.serverReflectionInfo( + _createRequestStream([ + ServerReflectionRequest()..fileContainingSymbol = symbol, + ]), + ); + + final responses = await responseStream.toList(); + + if (responses.isEmpty) { + throw ReflectionException('No response for symbol: $symbol', 0); + } + + final response = responses.first; + if (response.hasErrorResponse()) { + throw ReflectionException( + response.errorResponse.errorMessage, + response.errorResponse.errorCode, + ); + } + + return response.fileDescriptorResponse.fileDescriptorProto + .map((bytes) => FileDescriptorProto.fromBuffer(bytes)) + .toList(); + } on GrpcError catch (e) { + throw ConnectionException('Failed to get descriptors: ${e.message}'); + } + } + + /// Gets file descriptors by filename. + /// + /// Returns a list of [FileDescriptorProto] for the specified file + /// and its dependencies. + /// + /// Throws [ConnectionException] if connection fails. + /// Throws [ReflectionException] if file not found or other error. + Future> getFileDescriptorsByName( + String filename, + ) async { + _ensureInitialized(); + + try { + final responseStream = _stub.serverReflectionInfo( + _createRequestStream([ + ServerReflectionRequest()..fileByFilename = filename, + ]), + ); + + final responses = await responseStream.toList(); + + if (responses.isEmpty) { + throw ReflectionException('No response for file: $filename', 0); + } + + final response = responses.first; + if (response.hasErrorResponse()) { + throw ReflectionException( + response.errorResponse.errorMessage, + response.errorResponse.errorCode, + ); + } + + return response.fileDescriptorResponse.fileDescriptorProto + .map((bytes) => FileDescriptorProto.fromBuffer(bytes)) + .toList(); + } on GrpcError catch (e) { + throw ConnectionException('Failed to get file descriptor: ${e.message}'); + } + } + + /// Discovers all services and their methods. + /// + /// Returns a list of [DiscoveredService] containing service names + /// and their method signatures. + /// + /// This is a convenience method that combines [listServices] and + /// [getFileDescriptorsForSymbol] to provide detailed service information. + Future> discoverAllServices() async { + final serviceNames = await listServices(); + final List discoveredServices = []; + + for (final serviceName in serviceNames) { + try { + final descriptors = await getFileDescriptorsForSymbol(serviceName); + final methods = []; + + for (final descriptor in descriptors) { + for (final service in descriptor.service) { + // Match the service by fully qualified name + final fullName = descriptor.package.isEmpty + ? service.name + : '${descriptor.package}.${service.name}'; + + if (fullName == serviceName) { + for (final method in service.method) { + methods.add(DiscoveredMethod( + name: method.name, + inputType: method.inputType, + outputType: method.outputType, + clientStreaming: method.clientStreaming, + serverStreaming: method.serverStreaming, + )); + } + } + } + } + + discoveredServices.add(DiscoveredService( + name: serviceName, + methods: methods, + )); + } on GrpcDiscoveryException { + // If we can't get details for a service, add it with no methods + discoveredServices.add(DiscoveredService( + name: serviceName, + methods: const [], + )); + } + } + + return discoveredServices; + } + + /// Discovers services matching a pattern. + /// + /// Filters discovered services by name containing the [pattern]. + /// Case-insensitive matching. + Future> discoverServicesMatching(String pattern) async { + final services = await discoverAllServices(); + final lowerPattern = pattern.toLowerCase(); + return services + .where((s) => s.name.toLowerCase().contains(lowerPattern)) + .toList(); + } + + Stream _createRequestStream( + List requests, + ) async* { + for (final request in requests) { + yield request; + } + } + + /// Closes the gRPC channel connection. + /// + /// Should be called when done with discovery to release resources. + Future close() async { + if (_isInitialized) { + await _channel.shutdown(); + _isInitialized = false; + } + } +} + +/// Utility function to discover and print gRPC services. +/// +/// This is a convenience function for development and debugging. +/// Connects to the server specified in [config], discovers all services, +/// and prints them to the console. +/// +/// Returns a map of service names to their discovered service details. +Future> discoverAndPrintServices({ + GrpcConfig config = GrpcConfig.development, +}) async { + final discovery = GrpcDiscoveryClient(config: config); + + try { + final services = await discovery.discoverAllServices(); + final Map result = {}; + + for (final service in services) { + result[service.name] = service; + } + + return result; + } finally { + await discovery.close(); + } +} diff --git a/lib/generated/delivery_service.pb.dart b/lib/generated/delivery_service.pb.dart new file mode 100644 index 0000000..3a1edfe --- /dev/null +++ b/lib/generated/delivery_service.pb.dart @@ -0,0 +1,1351 @@ +// Generated code - do not modify +// ignore_for_file: annotate_overrides, camel_case_types, constant_identifier_names +// ignore_for_file: non_constant_identifier_names, prefer_single_quotes, use_super_parameters +// Proto stubs for Plan B Logistics delivery service + +import 'dart:core' as $core; +import 'package:protobuf/protobuf.dart' as $pb; + +/// Empty request message for queries with no parameters. +class Empty extends $pb.GeneratedMessage { + factory Empty() => create(); + + Empty._() : super(); + + factory Empty.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory Empty.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('Empty', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + Empty createEmptyInstance() => create(); + + static Empty create() => Empty._(); + + @$core.override + Empty clone() => Empty()..mergeFromMessage(this); + + static $core.List createRepeated() => []; +} + +/// Request message for getting deliveries by route fragment ID. +class GetDeliveriesRequest extends $pb.GeneratedMessage { + factory GetDeliveriesRequest({ + $core.int? routeFragmentId, + }) { + final result = create(); + if (routeFragmentId != null) result.routeFragmentId = routeFragmentId; + return result; + } + + GetDeliveriesRequest._() : super(); + + factory GetDeliveriesRequest.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory GetDeliveriesRequest.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('GetDeliveriesRequest', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..a<$core.int>(1, 'routeFragmentId', $pb.PbFieldType.O3) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + GetDeliveriesRequest createEmptyInstance() => create(); + + static GetDeliveriesRequest create() => GetDeliveriesRequest._(); + + @$core.override + GetDeliveriesRequest clone() => GetDeliveriesRequest()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.int get routeFragmentId => $_getIZ(0); + @$pb.TagNumber(1) + set routeFragmentId($core.int v) => $_setSignedInt32(0, v); + @$pb.TagNumber(1) + $core.bool hasRouteFragmentId() => $_has(0); + @$pb.TagNumber(1) + void clearRouteFragmentId() => clearField(1); +} + +/// Response message containing a list of delivery routes. +class DeliveryRoutesResponse extends $pb.GeneratedMessage { + factory DeliveryRoutesResponse({ + $core.Iterable? routes, + }) { + final result = create(); + if (routes != null) result.routes.addAll(routes); + return result; + } + + DeliveryRoutesResponse._() : super(); + + factory DeliveryRoutesResponse.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory DeliveryRoutesResponse.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('DeliveryRoutesResponse', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..pc(1, 'routes', $pb.PbFieldType.PM, + subBuilder: DeliveryRouteProto.create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + DeliveryRoutesResponse createEmptyInstance() => create(); + + static DeliveryRoutesResponse create() => DeliveryRoutesResponse._(); + + @$core.override + DeliveryRoutesResponse clone() => DeliveryRoutesResponse()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.List get routes => $_getList(0); +} + +/// Response message containing a list of deliveries. +class DeliveriesResponse extends $pb.GeneratedMessage { + factory DeliveriesResponse({ + $core.Iterable? deliveries, + }) { + final result = create(); + if (deliveries != null) result.deliveries.addAll(deliveries); + return result; + } + + DeliveriesResponse._() : super(); + + factory DeliveriesResponse.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory DeliveriesResponse.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('DeliveriesResponse', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..pc(1, 'deliveries', $pb.PbFieldType.PM, + subBuilder: DeliveryProto.create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + DeliveriesResponse createEmptyInstance() => create(); + + static DeliveriesResponse create() => DeliveriesResponse._(); + + @$core.override + DeliveriesResponse clone() => DeliveriesResponse()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.List get deliveries => $_getList(0); +} + +/// Proto message for DeliveryRoute. +class DeliveryRouteProto extends $pb.GeneratedMessage { + factory DeliveryRouteProto({ + $core.int? id, + $core.int? routeId, + $core.String? name, + $core.String? routeName, + $core.int? deliveriesCount, + $core.int? deliveredCount, + $core.bool? completed, + $core.String? createdAt, + }) { + final result = create(); + if (id != null) result.id = id; + if (routeId != null) result.routeId = routeId; + if (name != null) result.name = name; + if (routeName != null) result.routeName = routeName; + if (deliveriesCount != null) result.deliveriesCount = deliveriesCount; + if (deliveredCount != null) result.deliveredCount = deliveredCount; + if (completed != null) result.completed = completed; + if (createdAt != null) result.createdAt = createdAt; + return result; + } + + DeliveryRouteProto._() : super(); + + factory DeliveryRouteProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory DeliveryRouteProto.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('DeliveryRouteProto', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..a<$core.int>(1, 'id', $pb.PbFieldType.O3) + ..a<$core.int>(2, 'routeId', $pb.PbFieldType.O3) + ..aOS(3, 'name') + ..aOS(4, 'routeName') + ..a<$core.int>(5, 'deliveriesCount', $pb.PbFieldType.O3) + ..a<$core.int>(6, 'deliveredCount', $pb.PbFieldType.O3) + ..aOB(7, 'completed') + ..aOS(8, 'createdAt') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + DeliveryRouteProto createEmptyInstance() => create(); + + static DeliveryRouteProto create() => DeliveryRouteProto._(); + + @$core.override + DeliveryRouteProto clone() => DeliveryRouteProto()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.int get id => $_getIZ(0); + @$pb.TagNumber(1) + set id($core.int v) => $_setSignedInt32(0, v); + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => clearField(1); + + @$pb.TagNumber(2) + $core.int get routeId => $_getIZ(1); + @$pb.TagNumber(2) + set routeId($core.int v) => $_setSignedInt32(1, v); + @$pb.TagNumber(2) + $core.bool hasRouteId() => $_has(1); + @$pb.TagNumber(2) + void clearRouteId() => clearField(2); + + @$pb.TagNumber(3) + $core.String get name => $_getSZ(2); + @$pb.TagNumber(3) + set name($core.String v) => $_setString(2, v); + @$pb.TagNumber(3) + $core.bool hasName() => $_has(2); + @$pb.TagNumber(3) + void clearName() => clearField(3); + + @$pb.TagNumber(4) + $core.String get routeName => $_getSZ(3); + @$pb.TagNumber(4) + set routeName($core.String v) => $_setString(3, v); + @$pb.TagNumber(4) + $core.bool hasRouteName() => $_has(3); + @$pb.TagNumber(4) + void clearRouteName() => clearField(4); + + @$pb.TagNumber(5) + $core.int get deliveriesCount => $_getIZ(4); + @$pb.TagNumber(5) + set deliveriesCount($core.int v) => $_setSignedInt32(4, v); + @$pb.TagNumber(5) + $core.bool hasDeliveriesCount() => $_has(4); + @$pb.TagNumber(5) + void clearDeliveriesCount() => clearField(5); + + @$pb.TagNumber(6) + $core.int get deliveredCount => $_getIZ(5); + @$pb.TagNumber(6) + set deliveredCount($core.int v) => $_setSignedInt32(5, v); + @$pb.TagNumber(6) + $core.bool hasDeliveredCount() => $_has(5); + @$pb.TagNumber(6) + void clearDeliveredCount() => clearField(6); + + @$pb.TagNumber(7) + $core.bool get completed => $_getBF(6); + @$pb.TagNumber(7) + set completed($core.bool v) => $_setBool(6, v); + @$pb.TagNumber(7) + $core.bool hasCompleted() => $_has(6); + @$pb.TagNumber(7) + void clearCompleted() => clearField(7); + + @$pb.TagNumber(8) + $core.String get createdAt => $_getSZ(7); + @$pb.TagNumber(8) + set createdAt($core.String v) => $_setString(7, v); + @$pb.TagNumber(8) + $core.bool hasCreatedAt() => $_has(7); + @$pb.TagNumber(8) + void clearCreatedAt() => clearField(8); +} + +/// Proto message for Delivery. +class DeliveryProto extends $pb.GeneratedMessage { + factory DeliveryProto({ + $core.int? id, + $core.int? routeFragmentId, + $core.int? deliveryIndex, + $core.Iterable? orders, + UserInfoProto? deliveredBy, + DeliveryAddressProto? deliveryAddress, + $core.String? deliveredAt, + $core.String? skippedAt, + $core.String? createdAt, + $core.String? updatedAt, + $core.bool? delivered, + $core.bool? hasBeenSkipped, + $core.bool? isSkipped, + $core.String? name, + }) { + final result = create(); + if (id != null) result.id = id; + if (routeFragmentId != null) result.routeFragmentId = routeFragmentId; + if (deliveryIndex != null) result.deliveryIndex = deliveryIndex; + if (orders != null) result.orders.addAll(orders); + if (deliveredBy != null) result.deliveredBy = deliveredBy; + if (deliveryAddress != null) result.deliveryAddress = deliveryAddress; + if (deliveredAt != null) result.deliveredAt = deliveredAt; + if (skippedAt != null) result.skippedAt = skippedAt; + if (createdAt != null) result.createdAt = createdAt; + if (updatedAt != null) result.updatedAt = updatedAt; + if (delivered != null) result.delivered = delivered; + if (hasBeenSkipped != null) result.hasBeenSkipped = hasBeenSkipped; + if (isSkipped != null) result.isSkipped = isSkipped; + if (name != null) result.name = name; + return result; + } + + DeliveryProto._() : super(); + + factory DeliveryProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory DeliveryProto.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('DeliveryProto', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..a<$core.int>(1, 'id', $pb.PbFieldType.O3) + ..a<$core.int>(2, 'routeFragmentId', $pb.PbFieldType.O3) + ..a<$core.int>(3, 'deliveryIndex', $pb.PbFieldType.O3) + ..pc(4, 'orders', $pb.PbFieldType.PM, + subBuilder: DeliveryOrderProto.create) + ..aOM(5, 'deliveredBy', subBuilder: UserInfoProto.create) + ..aOM(6, 'deliveryAddress', + subBuilder: DeliveryAddressProto.create) + ..aOS(7, 'deliveredAt') + ..aOS(8, 'skippedAt') + ..aOS(9, 'createdAt') + ..aOS(10, 'updatedAt') + ..aOB(11, 'delivered') + ..aOB(12, 'hasBeenSkipped') + ..aOB(13, 'isSkipped') + ..aOS(14, 'name') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + DeliveryProto createEmptyInstance() => create(); + + static DeliveryProto create() => DeliveryProto._(); + + @$core.override + DeliveryProto clone() => DeliveryProto()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.int get id => $_getIZ(0); + @$pb.TagNumber(1) + set id($core.int v) => $_setSignedInt32(0, v); + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => clearField(1); + + @$pb.TagNumber(2) + $core.int get routeFragmentId => $_getIZ(1); + @$pb.TagNumber(2) + set routeFragmentId($core.int v) => $_setSignedInt32(1, v); + @$pb.TagNumber(2) + $core.bool hasRouteFragmentId() => $_has(1); + @$pb.TagNumber(2) + void clearRouteFragmentId() => clearField(2); + + @$pb.TagNumber(3) + $core.int get deliveryIndex => $_getIZ(2); + @$pb.TagNumber(3) + set deliveryIndex($core.int v) => $_setSignedInt32(2, v); + @$pb.TagNumber(3) + $core.bool hasDeliveryIndex() => $_has(2); + @$pb.TagNumber(3) + void clearDeliveryIndex() => clearField(3); + + @$pb.TagNumber(4) + $core.List get orders => $_getList(3); + + @$pb.TagNumber(5) + UserInfoProto get deliveredBy => $_getN(4); + @$pb.TagNumber(5) + set deliveredBy(UserInfoProto v) => setField(5, v); + @$pb.TagNumber(5) + $core.bool hasDeliveredBy() => $_has(4); + @$pb.TagNumber(5) + void clearDeliveredBy() => clearField(5); + @$pb.TagNumber(5) + UserInfoProto ensureDeliveredBy() => $_ensure(4); + + @$pb.TagNumber(6) + DeliveryAddressProto get deliveryAddress => $_getN(5); + @$pb.TagNumber(6) + set deliveryAddress(DeliveryAddressProto v) => setField(6, v); + @$pb.TagNumber(6) + $core.bool hasDeliveryAddress() => $_has(5); + @$pb.TagNumber(6) + void clearDeliveryAddress() => clearField(6); + @$pb.TagNumber(6) + DeliveryAddressProto ensureDeliveryAddress() => $_ensure(5); + + @$pb.TagNumber(7) + $core.String get deliveredAt => $_getSZ(6); + @$pb.TagNumber(7) + set deliveredAt($core.String v) => $_setString(6, v); + @$pb.TagNumber(7) + $core.bool hasDeliveredAt() => $_has(6); + @$pb.TagNumber(7) + void clearDeliveredAt() => clearField(7); + + @$pb.TagNumber(8) + $core.String get skippedAt => $_getSZ(7); + @$pb.TagNumber(8) + set skippedAt($core.String v) => $_setString(7, v); + @$pb.TagNumber(8) + $core.bool hasSkippedAt() => $_has(7); + @$pb.TagNumber(8) + void clearSkippedAt() => clearField(8); + + @$pb.TagNumber(9) + $core.String get createdAt => $_getSZ(8); + @$pb.TagNumber(9) + set createdAt($core.String v) => $_setString(8, v); + @$pb.TagNumber(9) + $core.bool hasCreatedAt() => $_has(8); + @$pb.TagNumber(9) + void clearCreatedAt() => clearField(9); + + @$pb.TagNumber(10) + $core.String get updatedAt => $_getSZ(9); + @$pb.TagNumber(10) + set updatedAt($core.String v) => $_setString(9, v); + @$pb.TagNumber(10) + $core.bool hasUpdatedAt() => $_has(9); + @$pb.TagNumber(10) + void clearUpdatedAt() => clearField(10); + + @$pb.TagNumber(11) + $core.bool get delivered => $_getBF(10); + @$pb.TagNumber(11) + set delivered($core.bool v) => $_setBool(10, v); + @$pb.TagNumber(11) + $core.bool hasDelivered() => $_has(10); + @$pb.TagNumber(11) + void clearDelivered() => clearField(11); + + @$pb.TagNumber(12) + $core.bool get hasBeenSkipped => $_getBF(11); + @$pb.TagNumber(12) + set hasBeenSkipped($core.bool v) => $_setBool(11, v); + @$pb.TagNumber(12) + $core.bool hasHasBeenSkipped() => $_has(11); + @$pb.TagNumber(12) + void clearHasBeenSkipped() => clearField(12); + + @$pb.TagNumber(13) + $core.bool get isSkipped => $_getBF(12); + @$pb.TagNumber(13) + set isSkipped($core.bool v) => $_setBool(12, v); + @$pb.TagNumber(13) + $core.bool hasIsSkipped() => $_has(12); + @$pb.TagNumber(13) + void clearIsSkipped() => clearField(13); + + @$pb.TagNumber(14) + $core.String get name => $_getSZ(13); + @$pb.TagNumber(14) + set name($core.String v) => $_setString(13, v); + @$pb.TagNumber(14) + $core.bool hasName() => $_has(13); + @$pb.TagNumber(14) + void clearName() => clearField(14); +} + +/// Proto message for DeliveryAddress. +class DeliveryAddressProto extends $pb.GeneratedMessage { + factory DeliveryAddressProto({ + $core.int? id, + $core.String? line1, + $core.String? line2, + $core.String? postalCode, + $core.String? city, + $core.String? subdivision, + $core.String? countryCode, + $core.double? latitude, + $core.double? longitude, + $core.String? formattedAddress, + }) { + final result = create(); + if (id != null) result.id = id; + if (line1 != null) result.line1 = line1; + if (line2 != null) result.line2 = line2; + if (postalCode != null) result.postalCode = postalCode; + if (city != null) result.city = city; + if (subdivision != null) result.subdivision = subdivision; + if (countryCode != null) result.countryCode = countryCode; + if (latitude != null) result.latitude = latitude; + if (longitude != null) result.longitude = longitude; + if (formattedAddress != null) result.formattedAddress = formattedAddress; + return result; + } + + DeliveryAddressProto._() : super(); + + factory DeliveryAddressProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory DeliveryAddressProto.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('DeliveryAddressProto', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..a<$core.int>(1, 'id', $pb.PbFieldType.O3) + ..aOS(2, 'line1') + ..aOS(3, 'line2') + ..aOS(4, 'postalCode') + ..aOS(5, 'city') + ..aOS(6, 'subdivision') + ..aOS(7, 'countryCode') + ..a<$core.double>(8, 'latitude', $pb.PbFieldType.OD) + ..a<$core.double>(9, 'longitude', $pb.PbFieldType.OD) + ..aOS(10, 'formattedAddress') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + DeliveryAddressProto createEmptyInstance() => create(); + + static DeliveryAddressProto create() => DeliveryAddressProto._(); + + @$core.override + DeliveryAddressProto clone() => DeliveryAddressProto()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.int get id => $_getIZ(0); + @$pb.TagNumber(1) + set id($core.int v) => $_setSignedInt32(0, v); + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get line1 => $_getSZ(1); + @$pb.TagNumber(2) + set line1($core.String v) => $_setString(1, v); + @$pb.TagNumber(2) + $core.bool hasLine1() => $_has(1); + @$pb.TagNumber(2) + void clearLine1() => clearField(2); + + @$pb.TagNumber(3) + $core.String get line2 => $_getSZ(2); + @$pb.TagNumber(3) + set line2($core.String v) => $_setString(2, v); + @$pb.TagNumber(3) + $core.bool hasLine2() => $_has(2); + @$pb.TagNumber(3) + void clearLine2() => clearField(3); + + @$pb.TagNumber(4) + $core.String get postalCode => $_getSZ(3); + @$pb.TagNumber(4) + set postalCode($core.String v) => $_setString(3, v); + @$pb.TagNumber(4) + $core.bool hasPostalCode() => $_has(3); + @$pb.TagNumber(4) + void clearPostalCode() => clearField(4); + + @$pb.TagNumber(5) + $core.String get city => $_getSZ(4); + @$pb.TagNumber(5) + set city($core.String v) => $_setString(4, v); + @$pb.TagNumber(5) + $core.bool hasCity() => $_has(4); + @$pb.TagNumber(5) + void clearCity() => clearField(5); + + @$pb.TagNumber(6) + $core.String get subdivision => $_getSZ(5); + @$pb.TagNumber(6) + set subdivision($core.String v) => $_setString(5, v); + @$pb.TagNumber(6) + $core.bool hasSubdivision() => $_has(5); + @$pb.TagNumber(6) + void clearSubdivision() => clearField(6); + + @$pb.TagNumber(7) + $core.String get countryCode => $_getSZ(6); + @$pb.TagNumber(7) + set countryCode($core.String v) => $_setString(6, v); + @$pb.TagNumber(7) + $core.bool hasCountryCode() => $_has(6); + @$pb.TagNumber(7) + void clearCountryCode() => clearField(7); + + @$pb.TagNumber(8) + $core.double get latitude => $_getN(7); + @$pb.TagNumber(8) + set latitude($core.double v) => $_setDouble(7, v); + @$pb.TagNumber(8) + $core.bool hasLatitude() => $_has(7); + @$pb.TagNumber(8) + void clearLatitude() => clearField(8); + + @$pb.TagNumber(9) + $core.double get longitude => $_getN(8); + @$pb.TagNumber(9) + set longitude($core.double v) => $_setDouble(8, v); + @$pb.TagNumber(9) + $core.bool hasLongitude() => $_has(8); + @$pb.TagNumber(9) + void clearLongitude() => clearField(9); + + @$pb.TagNumber(10) + $core.String get formattedAddress => $_getSZ(9); + @$pb.TagNumber(10) + set formattedAddress($core.String v) => $_setString(9, v); + @$pb.TagNumber(10) + $core.bool hasFormattedAddress() => $_has(9); + @$pb.TagNumber(10) + void clearFormattedAddress() => clearField(10); +} + +/// Proto message for DeliveryOrder. +class DeliveryOrderProto extends $pb.GeneratedMessage { + factory DeliveryOrderProto({ + $core.int? id, + $core.bool? isNewCustomer, + $core.String? note, + $core.double? totalAmount, + $core.double? totalPaid, + $core.int? totalItems, + $core.Iterable? contacts, + DeliveryContactProto? contact, + }) { + final result = create(); + if (id != null) result.id = id; + if (isNewCustomer != null) result.isNewCustomer = isNewCustomer; + if (note != null) result.note = note; + if (totalAmount != null) result.totalAmount = totalAmount; + if (totalPaid != null) result.totalPaid = totalPaid; + if (totalItems != null) result.totalItems = totalItems; + if (contacts != null) result.contacts.addAll(contacts); + if (contact != null) result.contact = contact; + return result; + } + + DeliveryOrderProto._() : super(); + + factory DeliveryOrderProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory DeliveryOrderProto.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('DeliveryOrderProto', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..a<$core.int>(1, 'id', $pb.PbFieldType.O3) + ..aOB(2, 'isNewCustomer') + ..aOS(3, 'note') + ..a<$core.double>(4, 'totalAmount', $pb.PbFieldType.OD) + ..a<$core.double>(5, 'totalPaid', $pb.PbFieldType.OD) + ..a<$core.int>(6, 'totalItems', $pb.PbFieldType.O3) + ..pc(7, 'contacts', $pb.PbFieldType.PM, + subBuilder: DeliveryContactProto.create) + ..aOM(8, 'contact', + subBuilder: DeliveryContactProto.create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + DeliveryOrderProto createEmptyInstance() => create(); + + static DeliveryOrderProto create() => DeliveryOrderProto._(); + + @$core.override + DeliveryOrderProto clone() => DeliveryOrderProto()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.int get id => $_getIZ(0); + @$pb.TagNumber(1) + set id($core.int v) => $_setSignedInt32(0, v); + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => clearField(1); + + @$pb.TagNumber(2) + $core.bool get isNewCustomer => $_getBF(1); + @$pb.TagNumber(2) + set isNewCustomer($core.bool v) => $_setBool(1, v); + @$pb.TagNumber(2) + $core.bool hasIsNewCustomer() => $_has(1); + @$pb.TagNumber(2) + void clearIsNewCustomer() => clearField(2); + + @$pb.TagNumber(3) + $core.String get note => $_getSZ(2); + @$pb.TagNumber(3) + set note($core.String v) => $_setString(2, v); + @$pb.TagNumber(3) + $core.bool hasNote() => $_has(2); + @$pb.TagNumber(3) + void clearNote() => clearField(3); + + @$pb.TagNumber(4) + $core.double get totalAmount => $_getN(3); + @$pb.TagNumber(4) + set totalAmount($core.double v) => $_setDouble(3, v); + @$pb.TagNumber(4) + $core.bool hasTotalAmount() => $_has(3); + @$pb.TagNumber(4) + void clearTotalAmount() => clearField(4); + + @$pb.TagNumber(5) + $core.double get totalPaid => $_getN(4); + @$pb.TagNumber(5) + set totalPaid($core.double v) => $_setDouble(4, v); + @$pb.TagNumber(5) + $core.bool hasTotalPaid() => $_has(4); + @$pb.TagNumber(5) + void clearTotalPaid() => clearField(5); + + @$pb.TagNumber(6) + $core.int get totalItems => $_getIZ(5); + @$pb.TagNumber(6) + set totalItems($core.int v) => $_setSignedInt32(5, v); + @$pb.TagNumber(6) + $core.bool hasTotalItems() => $_has(5); + @$pb.TagNumber(6) + void clearTotalItems() => clearField(6); + + @$pb.TagNumber(7) + $core.List get contacts => $_getList(6); + + @$pb.TagNumber(8) + DeliveryContactProto get contact => $_getN(7); + @$pb.TagNumber(8) + set contact(DeliveryContactProto v) => setField(8, v); + @$pb.TagNumber(8) + $core.bool hasContact() => $_has(7); + @$pb.TagNumber(8) + void clearContact() => clearField(8); + @$pb.TagNumber(8) + DeliveryContactProto ensureContact() => $_ensure(7); +} + +/// Proto message for DeliveryContact. +class DeliveryContactProto extends $pb.GeneratedMessage { + factory DeliveryContactProto({ + $core.String? firstName, + $core.String? lastName, + $core.String? fullName, + $core.String? phoneNumber, + }) { + final result = create(); + if (firstName != null) result.firstName = firstName; + if (lastName != null) result.lastName = lastName; + if (fullName != null) result.fullName = fullName; + if (phoneNumber != null) result.phoneNumber = phoneNumber; + return result; + } + + DeliveryContactProto._() : super(); + + factory DeliveryContactProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory DeliveryContactProto.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('DeliveryContactProto', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..aOS(1, 'firstName') + ..aOS(2, 'lastName') + ..aOS(3, 'fullName') + ..aOS(4, 'phoneNumber') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + DeliveryContactProto createEmptyInstance() => create(); + + static DeliveryContactProto create() => DeliveryContactProto._(); + + @$core.override + DeliveryContactProto clone() => DeliveryContactProto()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.String get firstName => $_getSZ(0); + @$pb.TagNumber(1) + set firstName($core.String v) => $_setString(0, v); + @$pb.TagNumber(1) + $core.bool hasFirstName() => $_has(0); + @$pb.TagNumber(1) + void clearFirstName() => clearField(1); + + @$pb.TagNumber(2) + $core.String get lastName => $_getSZ(1); + @$pb.TagNumber(2) + set lastName($core.String v) => $_setString(1, v); + @$pb.TagNumber(2) + $core.bool hasLastName() => $_has(1); + @$pb.TagNumber(2) + void clearLastName() => clearField(2); + + @$pb.TagNumber(3) + $core.String get fullName => $_getSZ(2); + @$pb.TagNumber(3) + set fullName($core.String v) => $_setString(2, v); + @$pb.TagNumber(3) + $core.bool hasFullName() => $_has(2); + @$pb.TagNumber(3) + void clearFullName() => clearField(3); + + @$pb.TagNumber(4) + $core.String get phoneNumber => $_getSZ(3); + @$pb.TagNumber(4) + set phoneNumber($core.String v) => $_setString(3, v); + @$pb.TagNumber(4) + $core.bool hasPhoneNumber() => $_has(3); + @$pb.TagNumber(4) + void clearPhoneNumber() => clearField(4); +} + +/// Proto message for UserInfo. +class UserInfoProto extends $pb.GeneratedMessage { + factory UserInfoProto({ + $core.int? id, + $core.String? firstName, + $core.String? lastName, + $core.String? fullName, + }) { + final result = create(); + if (id != null) result.id = id; + if (firstName != null) result.firstName = firstName; + if (lastName != null) result.lastName = lastName; + if (fullName != null) result.fullName = fullName; + return result; + } + + UserInfoProto._() : super(); + + factory UserInfoProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory UserInfoProto.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('UserInfoProto', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..a<$core.int>(1, 'id', $pb.PbFieldType.O3) + ..aOS(2, 'firstName') + ..aOS(3, 'lastName') + ..aOS(4, 'fullName') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + UserInfoProto createEmptyInstance() => create(); + + static UserInfoProto create() => UserInfoProto._(); + + @$core.override + UserInfoProto clone() => UserInfoProto()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.int get id => $_getIZ(0); + @$pb.TagNumber(1) + set id($core.int v) => $_setSignedInt32(0, v); + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get firstName => $_getSZ(1); + @$pb.TagNumber(2) + set firstName($core.String v) => $_setString(1, v); + @$pb.TagNumber(2) + $core.bool hasFirstName() => $_has(1); + @$pb.TagNumber(2) + void clearFirstName() => clearField(2); + + @$pb.TagNumber(3) + $core.String get lastName => $_getSZ(2); + @$pb.TagNumber(3) + set lastName($core.String v) => $_setString(2, v); + @$pb.TagNumber(3) + $core.bool hasLastName() => $_has(2); + @$pb.TagNumber(3) + void clearLastName() => clearField(3); + + @$pb.TagNumber(4) + $core.String get fullName => $_getSZ(3); + @$pb.TagNumber(4) + set fullName($core.String v) => $_setString(3, v); + @$pb.TagNumber(4) + $core.bool hasFullName() => $_has(3); + @$pb.TagNumber(4) + void clearFullName() => clearField(4); +} + +/// Request message for completing a delivery. +class CompleteDeliveryRequest extends $pb.GeneratedMessage { + factory CompleteDeliveryRequest({ + $core.int? deliveryId, + $core.String? deliveredAt, + }) { + final result = create(); + if (deliveryId != null) result.deliveryId = deliveryId; + if (deliveredAt != null) result.deliveredAt = deliveredAt; + return result; + } + + CompleteDeliveryRequest._() : super(); + + factory CompleteDeliveryRequest.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory CompleteDeliveryRequest.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('CompleteDeliveryRequest', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..a<$core.int>(1, 'deliveryId', $pb.PbFieldType.O3) + ..aOS(2, 'deliveredAt') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + CompleteDeliveryRequest createEmptyInstance() => create(); + + static CompleteDeliveryRequest create() => CompleteDeliveryRequest._(); + + @$core.override + CompleteDeliveryRequest clone() => CompleteDeliveryRequest()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.int get deliveryId => $_getIZ(0); + @$pb.TagNumber(1) + set deliveryId($core.int v) => $_setSignedInt32(0, v); + @$pb.TagNumber(1) + $core.bool hasDeliveryId() => $_has(0); + @$pb.TagNumber(1) + void clearDeliveryId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get deliveredAt => $_getSZ(1); + @$pb.TagNumber(2) + set deliveredAt($core.String v) => $_setString(1, v); + @$pb.TagNumber(2) + $core.bool hasDeliveredAt() => $_has(1); + @$pb.TagNumber(2) + void clearDeliveredAt() => clearField(2); +} + +/// Request message for marking a delivery as uncompleted. +class MarkDeliveryUncompletedRequest extends $pb.GeneratedMessage { + factory MarkDeliveryUncompletedRequest({ + $core.int? deliveryId, + }) { + final result = create(); + if (deliveryId != null) result.deliveryId = deliveryId; + return result; + } + + MarkDeliveryUncompletedRequest._() : super(); + + factory MarkDeliveryUncompletedRequest.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory MarkDeliveryUncompletedRequest.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('MarkDeliveryUncompletedRequest', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..a<$core.int>(1, 'deliveryId', $pb.PbFieldType.O3) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + MarkDeliveryUncompletedRequest createEmptyInstance() => create(); + + static MarkDeliveryUncompletedRequest create() => MarkDeliveryUncompletedRequest._(); + + @$core.override + MarkDeliveryUncompletedRequest clone() => MarkDeliveryUncompletedRequest()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.int get deliveryId => $_getIZ(0); + @$pb.TagNumber(1) + set deliveryId($core.int v) => $_setSignedInt32(0, v); + @$pb.TagNumber(1) + $core.bool hasDeliveryId() => $_has(0); + @$pb.TagNumber(1) + void clearDeliveryId() => clearField(1); +} + +/// Request message for skipping a delivery. +class SkipDeliveryRequest extends $pb.GeneratedMessage { + factory SkipDeliveryRequest({ + $core.int? deliveryId, + }) { + final result = create(); + if (deliveryId != null) result.deliveryId = deliveryId; + return result; + } + + SkipDeliveryRequest._() : super(); + + factory SkipDeliveryRequest.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory SkipDeliveryRequest.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('SkipDeliveryRequest', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..a<$core.int>(1, 'deliveryId', $pb.PbFieldType.O3) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + SkipDeliveryRequest createEmptyInstance() => create(); + + static SkipDeliveryRequest create() => SkipDeliveryRequest._(); + + @$core.override + SkipDeliveryRequest clone() => SkipDeliveryRequest()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.int get deliveryId => $_getIZ(0); + @$pb.TagNumber(1) + set deliveryId($core.int v) => $_setSignedInt32(0, v); + @$pb.TagNumber(1) + $core.bool hasDeliveryId() => $_has(0); + @$pb.TagNumber(1) + void clearDeliveryId() => clearField(1); +} + +/// Request message for uploading a delivery picture. +class UploadDeliveryPictureRequest extends $pb.GeneratedMessage { + factory UploadDeliveryPictureRequest({ + $core.int? deliveryId, + $core.List<$core.int>? imageData, + $core.String? fileName, + $core.String? contentType, + }) { + final result = create(); + if (deliveryId != null) result.deliveryId = deliveryId; + if (imageData != null) result.imageData = imageData; + if (fileName != null) result.fileName = fileName; + if (contentType != null) result.contentType = contentType; + return result; + } + + UploadDeliveryPictureRequest._() : super(); + + factory UploadDeliveryPictureRequest.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory UploadDeliveryPictureRequest.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('UploadDeliveryPictureRequest', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..a<$core.int>(1, 'deliveryId', $pb.PbFieldType.O3) + ..a<$core.List<$core.int>>(2, 'imageData', $pb.PbFieldType.OY) + ..aOS(3, 'fileName') + ..aOS(4, 'contentType') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + UploadDeliveryPictureRequest createEmptyInstance() => create(); + + static UploadDeliveryPictureRequest create() => UploadDeliveryPictureRequest._(); + + @$core.override + UploadDeliveryPictureRequest clone() => UploadDeliveryPictureRequest()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.int get deliveryId => $_getIZ(0); + @$pb.TagNumber(1) + set deliveryId($core.int v) => $_setSignedInt32(0, v); + @$pb.TagNumber(1) + $core.bool hasDeliveryId() => $_has(0); + @$pb.TagNumber(1) + void clearDeliveryId() => clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get imageData => $_getN(1); + @$pb.TagNumber(2) + set imageData($core.List<$core.int> v) => $_setBytes(1, v); + @$pb.TagNumber(2) + $core.bool hasImageData() => $_has(1); + @$pb.TagNumber(2) + void clearImageData() => clearField(2); + + @$pb.TagNumber(3) + $core.String get fileName => $_getSZ(2); + @$pb.TagNumber(3) + set fileName($core.String v) => $_setString(2, v); + @$pb.TagNumber(3) + $core.bool hasFileName() => $_has(2); + @$pb.TagNumber(3) + void clearFileName() => clearField(3); + + @$pb.TagNumber(4) + $core.String get contentType => $_getSZ(3); + @$pb.TagNumber(4) + set contentType($core.String v) => $_setString(3, v); + @$pb.TagNumber(4) + $core.bool hasContentType() => $_has(3); + @$pb.TagNumber(4) + void clearContentType() => clearField(4); +} + +/// Response message for upload operations. +class UploadResponse extends $pb.GeneratedMessage { + factory UploadResponse({ + $core.bool? success, + $core.String? message, + $core.String? uploadedUrl, + }) { + final result = create(); + if (success != null) result.success = success; + if (message != null) result.message = message; + if (uploadedUrl != null) result.uploadedUrl = uploadedUrl; + return result; + } + + UploadResponse._() : super(); + + factory UploadResponse.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory UploadResponse.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('UploadResponse', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..aOB(1, 'success') + ..aOS(2, 'message') + ..aOS(3, 'uploadedUrl') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + UploadResponse createEmptyInstance() => create(); + + static UploadResponse create() => UploadResponse._(); + + @$core.override + UploadResponse clone() => UploadResponse()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.bool get success => $_getBF(0); + @$pb.TagNumber(1) + set success($core.bool v) => $_setBool(0, v); + @$pb.TagNumber(1) + $core.bool hasSuccess() => $_has(0); + @$pb.TagNumber(1) + void clearSuccess() => clearField(1); + + @$pb.TagNumber(2) + $core.String get message => $_getSZ(1); + @$pb.TagNumber(2) + set message($core.String v) => $_setString(1, v); + @$pb.TagNumber(2) + $core.bool hasMessage() => $_has(1); + @$pb.TagNumber(2) + void clearMessage() => clearField(2); + + @$pb.TagNumber(3) + $core.String get uploadedUrl => $_getSZ(2); + @$pb.TagNumber(3) + set uploadedUrl($core.String v) => $_setString(2, v); + @$pb.TagNumber(3) + $core.bool hasUploadedUrl() => $_has(2); + @$pb.TagNumber(3) + void clearUploadedUrl() => clearField(3); +} + +/// Generic command response for delivery operations. +class CommandResponse extends $pb.GeneratedMessage { + factory CommandResponse({ + $core.bool? success, + $core.String? message, + }) { + final result = create(); + if (success != null) result.success = success; + if (message != null) result.message = message; + return result; + } + + CommandResponse._() : super(); + + factory CommandResponse.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory CommandResponse.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('CommandResponse', + package: const $pb.PackageName('planb.delivery'), + createEmptyInstance: create) + ..aOB(1, 'success') + ..aOS(2, 'message') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + CommandResponse createEmptyInstance() => create(); + + static CommandResponse create() => CommandResponse._(); + + @$core.override + CommandResponse clone() => CommandResponse()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.bool get success => $_getBF(0); + @$pb.TagNumber(1) + set success($core.bool v) => $_setBool(0, v); + @$pb.TagNumber(1) + $core.bool hasSuccess() => $_has(0); + @$pb.TagNumber(1) + void clearSuccess() => clearField(1); + + @$pb.TagNumber(2) + $core.String get message => $_getSZ(1); + @$pb.TagNumber(2) + set message($core.String v) => $_setString(1, v); + @$pb.TagNumber(2) + $core.bool hasMessage() => $_has(1); + @$pb.TagNumber(2) + void clearMessage() => clearField(2); +} diff --git a/lib/generated/delivery_service.pbgrpc.dart b/lib/generated/delivery_service.pbgrpc.dart new file mode 100644 index 0000000..b9fc40a --- /dev/null +++ b/lib/generated/delivery_service.pbgrpc.dart @@ -0,0 +1,297 @@ +// Generated code - do not modify +// ignore_for_file: annotate_overrides, camel_case_types, library_prefixes +// ignore_for_file: use_super_parameters, unused_import, non_constant_identifier_names +// Proto gRPC stubs for Plan B Logistics delivery service + +import 'dart:async' as $async; +import 'dart:core' as $core; + +import 'package:grpc/grpc.dart' as $grpc; + +import 'delivery_service.pb.dart' as $0; + +export 'delivery_service.pb.dart'; + +/// gRPC client for the DeliveryService. +/// +/// Provides methods for querying delivery routes and deliveries, +/// as well as executing delivery commands (complete, uncomplete, skip). +class DeliveryServiceClient extends $grpc.Client { + static final _$getDeliveryRoutes = + $grpc.ClientMethod<$0.Empty, $0.DeliveryRoutesResponse>( + '/planb.delivery.DeliveryService/GetDeliveryRoutes', + ($0.Empty value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.DeliveryRoutesResponse.fromBuffer(value), + ); + + static final _$getDeliveries = + $grpc.ClientMethod<$0.GetDeliveriesRequest, $0.DeliveriesResponse>( + '/planb.delivery.DeliveryService/GetDeliveries', + ($0.GetDeliveriesRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.DeliveriesResponse.fromBuffer(value), + ); + + static final _$completeDelivery = + $grpc.ClientMethod<$0.CompleteDeliveryRequest, $0.CommandResponse>( + '/planb.delivery.DeliveryService/CompleteDelivery', + ($0.CompleteDeliveryRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.CommandResponse.fromBuffer(value), + ); + + static final _$markDeliveryUncompleted = + $grpc.ClientMethod<$0.MarkDeliveryUncompletedRequest, $0.CommandResponse>( + '/planb.delivery.DeliveryService/MarkDeliveryUncompleted', + ($0.MarkDeliveryUncompletedRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.CommandResponse.fromBuffer(value), + ); + + static final _$skipDelivery = + $grpc.ClientMethod<$0.SkipDeliveryRequest, $0.CommandResponse>( + '/planb.delivery.DeliveryService/SkipDelivery', + ($0.SkipDeliveryRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.CommandResponse.fromBuffer(value), + ); + + static final _$uploadDeliveryPicture = + $grpc.ClientMethod<$0.UploadDeliveryPictureRequest, $0.UploadResponse>( + '/planb.delivery.DeliveryService/UploadDeliveryPicture', + ($0.UploadDeliveryPictureRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.UploadResponse.fromBuffer(value), + ); + + DeliveryServiceClient( + $grpc.ClientChannel channel, { + $grpc.CallOptions? options, + $core.Iterable<$grpc.ClientInterceptor>? interceptors, + }) : super( + channel, + options: options, + interceptors: interceptors, + ); + + /// Gets all delivery routes. + /// + /// Returns a [DeliveryRoutesResponse] containing all available routes. + $grpc.ResponseFuture<$0.DeliveryRoutesResponse> getDeliveryRoutes( + $0.Empty request, { + $grpc.CallOptions? options, + }) { + return $createUnaryCall( + _$getDeliveryRoutes, + request, + options: options, + ); + } + + /// Gets deliveries for a specific route fragment. + /// + /// [request] must contain the routeFragmentId. + /// Returns a [DeliveriesResponse] containing deliveries for the route. + $grpc.ResponseFuture<$0.DeliveriesResponse> getDeliveries( + $0.GetDeliveriesRequest request, { + $grpc.CallOptions? options, + }) { + return $createUnaryCall( + _$getDeliveries, + request, + options: options, + ); + } + + /// Marks a delivery as completed. + /// + /// [request] must contain the deliveryId and optionally deliveredAt timestamp. + /// Returns a [CommandResponse] indicating success or failure. + $grpc.ResponseFuture<$0.CommandResponse> completeDelivery( + $0.CompleteDeliveryRequest request, { + $grpc.CallOptions? options, + }) { + return $createUnaryCall( + _$completeDelivery, + request, + options: options, + ); + } + + /// Marks a delivery as uncompleted. + /// + /// [request] must contain the deliveryId. + /// Returns a [CommandResponse] indicating success or failure. + $grpc.ResponseFuture<$0.CommandResponse> markDeliveryUncompleted( + $0.MarkDeliveryUncompletedRequest request, { + $grpc.CallOptions? options, + }) { + return $createUnaryCall( + _$markDeliveryUncompleted, + request, + options: options, + ); + } + + /// Skips a delivery. + /// + /// [request] must contain the deliveryId. + /// Returns a [CommandResponse] indicating success or failure. + $grpc.ResponseFuture<$0.CommandResponse> skipDelivery( + $0.SkipDeliveryRequest request, { + $grpc.CallOptions? options, + }) { + return $createUnaryCall( + _$skipDelivery, + request, + options: options, + ); + } + + /// Uploads a picture for delivery proof. + /// + /// [request] must contain the deliveryId, imageData (bytes), fileName, and contentType. + /// Returns an [UploadResponse] with the uploaded URL on success. + $grpc.ResponseFuture<$0.UploadResponse> uploadDeliveryPicture( + $0.UploadDeliveryPictureRequest request, { + $grpc.CallOptions? options, + }) { + return $createUnaryCall( + _$uploadDeliveryPicture, + request, + options: options, + ); + } +} + +/// Server-side base class for the DeliveryService. +/// +/// Implement this class to create a gRPC server for delivery operations. +abstract class DeliveryServiceBase extends $grpc.Service { + @$core.override + $core.String get $name => 'planb.delivery.DeliveryService'; + + DeliveryServiceBase() { + $addMethod($grpc.ServiceMethod<$0.Empty, $0.DeliveryRoutesResponse>( + 'GetDeliveryRoutes', + getDeliveryRoutes_Pre, + false, + false, + ($core.List<$core.int> value) => $0.Empty.fromBuffer(value), + ($0.DeliveryRoutesResponse value) => value.writeToBuffer(), + )); + $addMethod($grpc.ServiceMethod<$0.GetDeliveriesRequest, $0.DeliveriesResponse>( + 'GetDeliveries', + getDeliveries_Pre, + false, + false, + ($core.List<$core.int> value) => $0.GetDeliveriesRequest.fromBuffer(value), + ($0.DeliveriesResponse value) => value.writeToBuffer(), + )); + $addMethod($grpc.ServiceMethod<$0.CompleteDeliveryRequest, $0.CommandResponse>( + 'CompleteDelivery', + completeDelivery_Pre, + false, + false, + ($core.List<$core.int> value) => $0.CompleteDeliveryRequest.fromBuffer(value), + ($0.CommandResponse value) => value.writeToBuffer(), + )); + $addMethod($grpc.ServiceMethod<$0.MarkDeliveryUncompletedRequest, $0.CommandResponse>( + 'MarkDeliveryUncompleted', + markDeliveryUncompleted_Pre, + false, + false, + ($core.List<$core.int> value) => $0.MarkDeliveryUncompletedRequest.fromBuffer(value), + ($0.CommandResponse value) => value.writeToBuffer(), + )); + $addMethod($grpc.ServiceMethod<$0.SkipDeliveryRequest, $0.CommandResponse>( + 'SkipDelivery', + skipDelivery_Pre, + false, + false, + ($core.List<$core.int> value) => $0.SkipDeliveryRequest.fromBuffer(value), + ($0.CommandResponse value) => value.writeToBuffer(), + )); + $addMethod($grpc.ServiceMethod<$0.UploadDeliveryPictureRequest, $0.UploadResponse>( + 'UploadDeliveryPicture', + uploadDeliveryPicture_Pre, + false, + false, + ($core.List<$core.int> value) => $0.UploadDeliveryPictureRequest.fromBuffer(value), + ($0.UploadResponse value) => value.writeToBuffer(), + )); + } + + $async.Future<$0.DeliveryRoutesResponse> getDeliveryRoutes_Pre( + $grpc.ServiceCall call, + $async.Future<$0.Empty> request, + ) async { + return getDeliveryRoutes(call, await request); + } + + $async.Future<$0.DeliveriesResponse> getDeliveries_Pre( + $grpc.ServiceCall call, + $async.Future<$0.GetDeliveriesRequest> request, + ) async { + return getDeliveries(call, await request); + } + + $async.Future<$0.CommandResponse> completeDelivery_Pre( + $grpc.ServiceCall call, + $async.Future<$0.CompleteDeliveryRequest> request, + ) async { + return completeDelivery(call, await request); + } + + $async.Future<$0.CommandResponse> markDeliveryUncompleted_Pre( + $grpc.ServiceCall call, + $async.Future<$0.MarkDeliveryUncompletedRequest> request, + ) async { + return markDeliveryUncompleted(call, await request); + } + + $async.Future<$0.CommandResponse> skipDelivery_Pre( + $grpc.ServiceCall call, + $async.Future<$0.SkipDeliveryRequest> request, + ) async { + return skipDelivery(call, await request); + } + + $async.Future<$0.UploadResponse> uploadDeliveryPicture_Pre( + $grpc.ServiceCall call, + $async.Future<$0.UploadDeliveryPictureRequest> request, + ) async { + return uploadDeliveryPicture(call, await request); + } + + /// Gets all delivery routes. + $async.Future<$0.DeliveryRoutesResponse> getDeliveryRoutes( + $grpc.ServiceCall call, + $0.Empty request, + ); + + /// Gets deliveries for a specific route fragment. + $async.Future<$0.DeliveriesResponse> getDeliveries( + $grpc.ServiceCall call, + $0.GetDeliveriesRequest request, + ); + + /// Marks a delivery as completed. + $async.Future<$0.CommandResponse> completeDelivery( + $grpc.ServiceCall call, + $0.CompleteDeliveryRequest request, + ); + + /// Marks a delivery as uncompleted. + $async.Future<$0.CommandResponse> markDeliveryUncompleted( + $grpc.ServiceCall call, + $0.MarkDeliveryUncompletedRequest request, + ); + + /// Skips a delivery. + $async.Future<$0.CommandResponse> skipDelivery( + $grpc.ServiceCall call, + $0.SkipDeliveryRequest request, + ); + + /// Uploads a picture for delivery proof. + $async.Future<$0.UploadResponse> uploadDeliveryPicture( + $grpc.ServiceCall call, + $0.UploadDeliveryPictureRequest request, + ); +} diff --git a/lib/generated/descriptor.pb.dart b/lib/generated/descriptor.pb.dart new file mode 100644 index 0000000..03a7b5c --- /dev/null +++ b/lib/generated/descriptor.pb.dart @@ -0,0 +1,821 @@ +// Minimal FileDescriptorProto implementation for proto generation +// ignore_for_file: annotate_overrides, camel_case_types, constant_identifier_names +// ignore_for_file: non_constant_identifier_names, prefer_single_quotes, use_super_parameters +// Based on google/protobuf/descriptor.proto + +import 'dart:core' as $core; +import 'package:protobuf/protobuf.dart' as $pb; + +class FileDescriptorProto extends $pb.GeneratedMessage { + factory FileDescriptorProto({ + $core.String? name, + $core.String? package, + $core.Iterable<$core.String>? dependency, + $core.Iterable? messageType, + $core.Iterable? enumType, + $core.Iterable? service, + $core.Iterable? extension, + FileOptions? options, + $core.String? syntax, + }) { + final result = create(); + if (name != null) result.name = name; + if (package != null) result.package = package; + if (dependency != null) result.dependency.addAll(dependency); + if (messageType != null) result.messageType.addAll(messageType); + if (enumType != null) result.enumType.addAll(enumType); + if (service != null) result.service.addAll(service); + if (extension != null) result.extension.addAll(extension); + if (options != null) result.options = options; + if (syntax != null) result.syntax = syntax; + return result; + } + + FileDescriptorProto._() : super(); + + factory FileDescriptorProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('FileDescriptorProto', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..aOS(1, 'name') + ..aOS(2, 'package') + ..pPS(3, 'dependency') + ..pc(4, 'messageType', $pb.PbFieldType.PM, + subBuilder: DescriptorProto.create) + ..pc(5, 'enumType', $pb.PbFieldType.PM, + subBuilder: EnumDescriptorProto.create) + ..pc(6, 'service', $pb.PbFieldType.PM, + subBuilder: ServiceDescriptorProto.create) + ..pc(7, 'extension', $pb.PbFieldType.PM, + subBuilder: FieldDescriptorProto.create) + ..aOM(8, 'options', subBuilder: FileOptions.create) + ..aOS(12, 'syntax') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + FileDescriptorProto createEmptyInstance() => create(); + static FileDescriptorProto create() => FileDescriptorProto._(); + @$core.override + FileDescriptorProto clone() => FileDescriptorProto()..mergeFromMessage(this); + + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) => $_setString(0, v); + @$pb.TagNumber(1) + $core.bool hasName() => $_has(0); + + @$pb.TagNumber(2) + $core.String get package => $_getSZ(1); + @$pb.TagNumber(2) + set package($core.String v) => $_setString(1, v); + @$pb.TagNumber(2) + $core.bool hasPackage() => $_has(1); + + @$pb.TagNumber(3) + $core.List<$core.String> get dependency => $_getList(2); + + @$pb.TagNumber(4) + $core.List get messageType => $_getList(3); + + @$pb.TagNumber(5) + $core.List get enumType => $_getList(4); + + @$pb.TagNumber(6) + $core.List get service => $_getList(5); + + @$pb.TagNumber(7) + $core.List get extension => $_getList(6); + + @$pb.TagNumber(8) + FileOptions get options => $_getN(7); + @$pb.TagNumber(8) + set options(FileOptions v) => setField(8, v); + @$pb.TagNumber(8) + $core.bool hasOptions() => $_has(7); + @$pb.TagNumber(8) + FileOptions ensureOptions() => $_ensure(7); + + @$pb.TagNumber(12) + $core.String get syntax => $_getSZ(8); + @$pb.TagNumber(12) + set syntax($core.String v) => $_setString(8, v); + @$pb.TagNumber(12) + $core.bool hasSyntax() => $_has(8); +} + +class DescriptorProto extends $pb.GeneratedMessage { + factory DescriptorProto({ + $core.String? name, + $core.Iterable? field, + $core.Iterable? extension, + $core.Iterable? nestedType, + $core.Iterable? enumType, + $core.Iterable? reservedRange, + $core.Iterable<$core.String>? reservedName, + $core.Iterable? oneofDecl, + MessageOptions? options, + }) { + final result = create(); + if (name != null) result.name = name; + if (field != null) result.field.addAll(field); + if (extension != null) result.extension.addAll(extension); + if (nestedType != null) result.nestedType.addAll(nestedType); + if (enumType != null) result.enumType.addAll(enumType); + if (reservedRange != null) result.reservedRange.addAll(reservedRange); + if (reservedName != null) result.reservedName.addAll(reservedName); + if (oneofDecl != null) result.oneofDecl.addAll(oneofDecl); + if (options != null) result.options = options; + return result; + } + + DescriptorProto._() : super(); + + factory DescriptorProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('DescriptorProto', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..aOS(1, 'name') + ..pc(2, 'field', $pb.PbFieldType.PM, + subBuilder: FieldDescriptorProto.create) + ..pc(6, 'extension', $pb.PbFieldType.PM, + subBuilder: FieldDescriptorProto.create) + ..pc(3, 'nestedType', $pb.PbFieldType.PM, + subBuilder: DescriptorProto.create) + ..pc(4, 'enumType', $pb.PbFieldType.PM, + subBuilder: EnumDescriptorProto.create) + ..pc(9, 'reservedRange', $pb.PbFieldType.PM, + subBuilder: DescriptorProto_ReservedRange.create) + ..pPS(10, 'reservedName') + ..pc(8, 'oneofDecl', $pb.PbFieldType.PM, + subBuilder: OneofDescriptorProto.create) + ..aOM(7, 'options', subBuilder: MessageOptions.create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + DescriptorProto createEmptyInstance() => create(); + static DescriptorProto create() => DescriptorProto._(); + @$core.override + DescriptorProto clone() => DescriptorProto()..mergeFromMessage(this); + + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) => $_setString(0, v); + + @$pb.TagNumber(2) + $core.List get field => $_getList(1); + + @$pb.TagNumber(6) + $core.List get extension => $_getList(2); + + @$pb.TagNumber(3) + $core.List get nestedType => $_getList(3); + + @$pb.TagNumber(4) + $core.List get enumType => $_getList(4); + + @$pb.TagNumber(9) + $core.List get reservedRange => $_getList(5); + + @$pb.TagNumber(10) + $core.List<$core.String> get reservedName => $_getList(6); + + @$pb.TagNumber(8) + $core.List get oneofDecl => $_getList(7); + + @$pb.TagNumber(7) + MessageOptions get options => $_getN(8); + @$pb.TagNumber(7) + set options(MessageOptions v) => setField(7, v); + @$pb.TagNumber(7) + $core.bool hasOptions() => $_has(8); + @$pb.TagNumber(7) + MessageOptions ensureOptions() => $_ensure(8); +} + +class DescriptorProto_ReservedRange extends $pb.GeneratedMessage { + factory DescriptorProto_ReservedRange({$core.int? start, $core.int? end}) { + final result = create(); + if (start != null) result.start = start; + if (end != null) result.end = end; + return result; + } + + DescriptorProto_ReservedRange._() : super(); + + factory DescriptorProto_ReservedRange.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('DescriptorProto.ReservedRange', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..a<$core.int>(1, 'start', $pb.PbFieldType.O3) + ..a<$core.int>(2, 'end', $pb.PbFieldType.O3) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + DescriptorProto_ReservedRange createEmptyInstance() => create(); + static DescriptorProto_ReservedRange create() => DescriptorProto_ReservedRange._(); + @$core.override + DescriptorProto_ReservedRange clone() => DescriptorProto_ReservedRange()..mergeFromMessage(this); + + @$pb.TagNumber(1) + $core.int get start => $_getIZ(0); + @$pb.TagNumber(1) + set start($core.int v) => $_setSignedInt32(0, v); + + @$pb.TagNumber(2) + $core.int get end => $_getIZ(1); + @$pb.TagNumber(2) + set end($core.int v) => $_setSignedInt32(1, v); +} + +class FieldDescriptorProto extends $pb.GeneratedMessage { + factory FieldDescriptorProto({ + $core.String? name, + $core.int? number, + FieldDescriptorProto_Label? label, + FieldDescriptorProto_Type? type, + $core.String? typeName, + $core.String? extendee, + $core.String? defaultValue, + $core.int? oneofIndex, + $core.String? jsonName, + FieldOptions? options, + $core.bool? proto3Optional, + }) { + final result = create(); + if (name != null) result.name = name; + if (number != null) result.number = number; + if (label != null) result.label = label; + if (type != null) result.type = type; + if (typeName != null) result.typeName = typeName; + if (extendee != null) result.extendee = extendee; + if (defaultValue != null) result.defaultValue = defaultValue; + if (oneofIndex != null) result.oneofIndex = oneofIndex; + if (jsonName != null) result.jsonName = jsonName; + if (options != null) result.options = options; + if (proto3Optional != null) result.proto3Optional = proto3Optional; + return result; + } + + FieldDescriptorProto._() : super(); + + factory FieldDescriptorProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('FieldDescriptorProto', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..aOS(1, 'name') + ..a<$core.int>(3, 'number', $pb.PbFieldType.O3) + ..e(4, 'label', $pb.PbFieldType.OE, + defaultOrMaker: FieldDescriptorProto_Label.LABEL_OPTIONAL, + valueOf: FieldDescriptorProto_Label.valueOf, + enumValues: FieldDescriptorProto_Label.values) + ..e(5, 'type', $pb.PbFieldType.OE, + defaultOrMaker: FieldDescriptorProto_Type.TYPE_DOUBLE, + valueOf: FieldDescriptorProto_Type.valueOf, + enumValues: FieldDescriptorProto_Type.values) + ..aOS(6, 'typeName') + ..aOS(2, 'extendee') + ..aOS(7, 'defaultValue') + ..a<$core.int>(9, 'oneofIndex', $pb.PbFieldType.O3) + ..aOS(10, 'jsonName') + ..aOM(8, 'options', subBuilder: FieldOptions.create) + ..aOB(17, 'proto3Optional') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + FieldDescriptorProto createEmptyInstance() => create(); + static FieldDescriptorProto create() => FieldDescriptorProto._(); + @$core.override + FieldDescriptorProto clone() => FieldDescriptorProto()..mergeFromMessage(this); + + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) => $_setString(0, v); + + @$pb.TagNumber(3) + $core.int get number => $_getIZ(1); + @$pb.TagNumber(3) + set number($core.int v) => $_setSignedInt32(1, v); + + @$pb.TagNumber(4) + FieldDescriptorProto_Label get label => $_getN(2); + @$pb.TagNumber(4) + set label(FieldDescriptorProto_Label v) => setField(4, v); + @$pb.TagNumber(4) + $core.bool hasLabel() => $_has(2); + + @$pb.TagNumber(5) + FieldDescriptorProto_Type get type => $_getN(3); + @$pb.TagNumber(5) + set type(FieldDescriptorProto_Type v) => setField(5, v); + @$pb.TagNumber(5) + $core.bool hasType() => $_has(3); + + @$pb.TagNumber(6) + $core.String get typeName => $_getSZ(4); + @$pb.TagNumber(6) + set typeName($core.String v) => $_setString(4, v); + @$pb.TagNumber(6) + $core.bool hasTypeName() => $_has(4); + + @$pb.TagNumber(2) + $core.String get extendee => $_getSZ(5); + @$pb.TagNumber(2) + set extendee($core.String v) => $_setString(5, v); + + @$pb.TagNumber(7) + $core.String get defaultValue => $_getSZ(6); + @$pb.TagNumber(7) + set defaultValue($core.String v) => $_setString(6, v); + + @$pb.TagNumber(9) + $core.int get oneofIndex => $_getIZ(7); + @$pb.TagNumber(9) + set oneofIndex($core.int v) => $_setSignedInt32(7, v); + @$pb.TagNumber(9) + $core.bool hasOneofIndex() => $_has(7); + + @$pb.TagNumber(10) + $core.String get jsonName => $_getSZ(8); + @$pb.TagNumber(10) + set jsonName($core.String v) => $_setString(8, v); + @$pb.TagNumber(10) + $core.bool hasJsonName() => $_has(8); + + @$pb.TagNumber(8) + FieldOptions get options => $_getN(9); + @$pb.TagNumber(8) + set options(FieldOptions v) => setField(8, v); + @$pb.TagNumber(8) + $core.bool hasOptions() => $_has(9); + @$pb.TagNumber(8) + FieldOptions ensureOptions() => $_ensure(9); + + @$pb.TagNumber(17) + $core.bool get proto3Optional => $_getBF(10); + @$pb.TagNumber(17) + set proto3Optional($core.bool v) => $_setBool(10, v); + @$pb.TagNumber(17) + $core.bool hasProto3Optional() => $_has(10); +} + +class FieldDescriptorProto_Type extends $pb.ProtobufEnum { + static const FieldDescriptorProto_Type TYPE_DOUBLE = FieldDescriptorProto_Type._(1, 'TYPE_DOUBLE'); + static const FieldDescriptorProto_Type TYPE_FLOAT = FieldDescriptorProto_Type._(2, 'TYPE_FLOAT'); + static const FieldDescriptorProto_Type TYPE_INT64 = FieldDescriptorProto_Type._(3, 'TYPE_INT64'); + static const FieldDescriptorProto_Type TYPE_UINT64 = FieldDescriptorProto_Type._(4, 'TYPE_UINT64'); + static const FieldDescriptorProto_Type TYPE_INT32 = FieldDescriptorProto_Type._(5, 'TYPE_INT32'); + static const FieldDescriptorProto_Type TYPE_FIXED64 = FieldDescriptorProto_Type._(6, 'TYPE_FIXED64'); + static const FieldDescriptorProto_Type TYPE_FIXED32 = FieldDescriptorProto_Type._(7, 'TYPE_FIXED32'); + static const FieldDescriptorProto_Type TYPE_BOOL = FieldDescriptorProto_Type._(8, 'TYPE_BOOL'); + static const FieldDescriptorProto_Type TYPE_STRING = FieldDescriptorProto_Type._(9, 'TYPE_STRING'); + static const FieldDescriptorProto_Type TYPE_GROUP = FieldDescriptorProto_Type._(10, 'TYPE_GROUP'); + static const FieldDescriptorProto_Type TYPE_MESSAGE = FieldDescriptorProto_Type._(11, 'TYPE_MESSAGE'); + static const FieldDescriptorProto_Type TYPE_BYTES = FieldDescriptorProto_Type._(12, 'TYPE_BYTES'); + static const FieldDescriptorProto_Type TYPE_UINT32 = FieldDescriptorProto_Type._(13, 'TYPE_UINT32'); + static const FieldDescriptorProto_Type TYPE_ENUM = FieldDescriptorProto_Type._(14, 'TYPE_ENUM'); + static const FieldDescriptorProto_Type TYPE_SFIXED32 = FieldDescriptorProto_Type._(15, 'TYPE_SFIXED32'); + static const FieldDescriptorProto_Type TYPE_SFIXED64 = FieldDescriptorProto_Type._(16, 'TYPE_SFIXED64'); + static const FieldDescriptorProto_Type TYPE_SINT32 = FieldDescriptorProto_Type._(17, 'TYPE_SINT32'); + static const FieldDescriptorProto_Type TYPE_SINT64 = FieldDescriptorProto_Type._(18, 'TYPE_SINT64'); + + static const $core.List values = [ + TYPE_DOUBLE, TYPE_FLOAT, TYPE_INT64, TYPE_UINT64, TYPE_INT32, + TYPE_FIXED64, TYPE_FIXED32, TYPE_BOOL, TYPE_STRING, TYPE_GROUP, + TYPE_MESSAGE, TYPE_BYTES, TYPE_UINT32, TYPE_ENUM, TYPE_SFIXED32, + TYPE_SFIXED64, TYPE_SINT32, TYPE_SINT64, + ]; + + static final $core.Map<$core.int, FieldDescriptorProto_Type> _byValue = $pb.ProtobufEnum.initByValue(values); + static FieldDescriptorProto_Type? valueOf($core.int value) => _byValue[value]; + + const FieldDescriptorProto_Type._($core.int v, $core.String n) : super(v, n); +} + +class FieldDescriptorProto_Label extends $pb.ProtobufEnum { + static const FieldDescriptorProto_Label LABEL_OPTIONAL = FieldDescriptorProto_Label._(1, 'LABEL_OPTIONAL'); + static const FieldDescriptorProto_Label LABEL_REQUIRED = FieldDescriptorProto_Label._(2, 'LABEL_REQUIRED'); + static const FieldDescriptorProto_Label LABEL_REPEATED = FieldDescriptorProto_Label._(3, 'LABEL_REPEATED'); + + static const $core.List values = [LABEL_OPTIONAL, LABEL_REQUIRED, LABEL_REPEATED]; + + static final $core.Map<$core.int, FieldDescriptorProto_Label> _byValue = $pb.ProtobufEnum.initByValue(values); + static FieldDescriptorProto_Label? valueOf($core.int value) => _byValue[value]; + + const FieldDescriptorProto_Label._($core.int v, $core.String n) : super(v, n); +} + +class EnumDescriptorProto extends $pb.GeneratedMessage { + factory EnumDescriptorProto({ + $core.String? name, + $core.Iterable? value, + EnumOptions? options, + }) { + final result = create(); + if (name != null) result.name = name; + if (value != null) result.value.addAll(value); + if (options != null) result.options = options; + return result; + } + + EnumDescriptorProto._() : super(); + + factory EnumDescriptorProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('EnumDescriptorProto', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..aOS(1, 'name') + ..pc(2, 'value', $pb.PbFieldType.PM, + subBuilder: EnumValueDescriptorProto.create) + ..aOM(3, 'options', subBuilder: EnumOptions.create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + EnumDescriptorProto createEmptyInstance() => create(); + static EnumDescriptorProto create() => EnumDescriptorProto._(); + @$core.override + EnumDescriptorProto clone() => EnumDescriptorProto()..mergeFromMessage(this); + + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) => $_setString(0, v); + + @$pb.TagNumber(2) + $core.List get value => $_getList(1); + + @$pb.TagNumber(3) + EnumOptions get options => $_getN(2); + @$pb.TagNumber(3) + set options(EnumOptions v) => setField(3, v); + @$pb.TagNumber(3) + $core.bool hasOptions() => $_has(2); +} + +class EnumValueDescriptorProto extends $pb.GeneratedMessage { + factory EnumValueDescriptorProto({$core.String? name, $core.int? number}) { + final result = create(); + if (name != null) result.name = name; + if (number != null) result.number = number; + return result; + } + + EnumValueDescriptorProto._() : super(); + + factory EnumValueDescriptorProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('EnumValueDescriptorProto', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..aOS(1, 'name') + ..a<$core.int>(2, 'number', $pb.PbFieldType.O3) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + EnumValueDescriptorProto createEmptyInstance() => create(); + static EnumValueDescriptorProto create() => EnumValueDescriptorProto._(); + @$core.override + EnumValueDescriptorProto clone() => EnumValueDescriptorProto()..mergeFromMessage(this); + + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) => $_setString(0, v); + + @$pb.TagNumber(2) + $core.int get number => $_getIZ(1); + @$pb.TagNumber(2) + set number($core.int v) => $_setSignedInt32(1, v); +} + +class ServiceDescriptorProto extends $pb.GeneratedMessage { + factory ServiceDescriptorProto({ + $core.String? name, + $core.Iterable? method, + ServiceOptions? options, + }) { + final result = create(); + if (name != null) result.name = name; + if (method != null) result.method.addAll(method); + if (options != null) result.options = options; + return result; + } + + ServiceDescriptorProto._() : super(); + + factory ServiceDescriptorProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('ServiceDescriptorProto', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..aOS(1, 'name') + ..pc(2, 'method', $pb.PbFieldType.PM, + subBuilder: MethodDescriptorProto.create) + ..aOM(3, 'options', subBuilder: ServiceOptions.create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + ServiceDescriptorProto createEmptyInstance() => create(); + static ServiceDescriptorProto create() => ServiceDescriptorProto._(); + @$core.override + ServiceDescriptorProto clone() => ServiceDescriptorProto()..mergeFromMessage(this); + + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) => $_setString(0, v); + + @$pb.TagNumber(2) + $core.List get method => $_getList(1); + + @$pb.TagNumber(3) + ServiceOptions get options => $_getN(2); + @$pb.TagNumber(3) + set options(ServiceOptions v) => setField(3, v); + @$pb.TagNumber(3) + $core.bool hasOptions() => $_has(2); +} + +class MethodDescriptorProto extends $pb.GeneratedMessage { + factory MethodDescriptorProto({ + $core.String? name, + $core.String? inputType, + $core.String? outputType, + MethodOptions? options, + $core.bool? clientStreaming, + $core.bool? serverStreaming, + }) { + final result = create(); + if (name != null) result.name = name; + if (inputType != null) result.inputType = inputType; + if (outputType != null) result.outputType = outputType; + if (options != null) result.options = options; + if (clientStreaming != null) result.clientStreaming = clientStreaming; + if (serverStreaming != null) result.serverStreaming = serverStreaming; + return result; + } + + MethodDescriptorProto._() : super(); + + factory MethodDescriptorProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('MethodDescriptorProto', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..aOS(1, 'name') + ..aOS(2, 'inputType') + ..aOS(3, 'outputType') + ..aOM(4, 'options', subBuilder: MethodOptions.create) + ..aOB(5, 'clientStreaming') + ..aOB(6, 'serverStreaming') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + MethodDescriptorProto createEmptyInstance() => create(); + static MethodDescriptorProto create() => MethodDescriptorProto._(); + @$core.override + MethodDescriptorProto clone() => MethodDescriptorProto()..mergeFromMessage(this); + + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) => $_setString(0, v); + + @$pb.TagNumber(2) + $core.String get inputType => $_getSZ(1); + @$pb.TagNumber(2) + set inputType($core.String v) => $_setString(1, v); + + @$pb.TagNumber(3) + $core.String get outputType => $_getSZ(2); + @$pb.TagNumber(3) + set outputType($core.String v) => $_setString(2, v); + + @$pb.TagNumber(4) + MethodOptions get options => $_getN(3); + @$pb.TagNumber(4) + set options(MethodOptions v) => setField(4, v); + @$pb.TagNumber(4) + $core.bool hasOptions() => $_has(3); + + @$pb.TagNumber(5) + $core.bool get clientStreaming => $_getBF(4); + @$pb.TagNumber(5) + set clientStreaming($core.bool v) => $_setBool(4, v); + + @$pb.TagNumber(6) + $core.bool get serverStreaming => $_getBF(5); + @$pb.TagNumber(6) + set serverStreaming($core.bool v) => $_setBool(5, v); +} + +class OneofDescriptorProto extends $pb.GeneratedMessage { + factory OneofDescriptorProto({$core.String? name}) { + final result = create(); + if (name != null) result.name = name; + return result; + } + + OneofDescriptorProto._() : super(); + + factory OneofDescriptorProto.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('OneofDescriptorProto', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..aOS(1, 'name') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + OneofDescriptorProto createEmptyInstance() => create(); + static OneofDescriptorProto create() => OneofDescriptorProto._(); + @$core.override + OneofDescriptorProto clone() => OneofDescriptorProto()..mergeFromMessage(this); + + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) => $_setString(0, v); +} + +// Options classes - minimal implementations +class FileOptions extends $pb.GeneratedMessage { + FileOptions._() : super(); + + factory FileOptions.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('FileOptions', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..aOS(1, 'javaPackage') + ..aOS(8, 'javaOuterClassname') + ..aOB(10, 'javaMultipleFiles') + ..aOS(11, 'goPackage') + ..aOS(37, 'csharpNamespace') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + FileOptions createEmptyInstance() => create(); + static FileOptions create() => FileOptions._(); + @$core.override + FileOptions clone() => create()..mergeFromMessage(this); + + @$pb.TagNumber(1) + $core.String get javaPackage => $_getSZ(0); + @$pb.TagNumber(8) + $core.String get javaOuterClassname => $_getSZ(1); + @$pb.TagNumber(10) + $core.bool get javaMultipleFiles => $_getBF(2); + @$pb.TagNumber(11) + $core.String get goPackage => $_getSZ(3); + @$pb.TagNumber(37) + $core.String get csharpNamespace => $_getSZ(4); +} + +class MessageOptions extends $pb.GeneratedMessage { + MessageOptions._() : super(); + + factory MessageOptions.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('MessageOptions', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..aOB(7, 'mapEntry') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + MessageOptions createEmptyInstance() => create(); + static MessageOptions create() => MessageOptions._(); + @$core.override + MessageOptions clone() => create()..mergeFromMessage(this); + + @$pb.TagNumber(7) + $core.bool get mapEntry => $_getBF(0); +} + +class FieldOptions extends $pb.GeneratedMessage { + FieldOptions._() : super(); + + factory FieldOptions.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('FieldOptions', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..aOB(2, 'packed') + ..aOB(3, 'deprecated') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + FieldOptions createEmptyInstance() => create(); + static FieldOptions create() => FieldOptions._(); + @$core.override + FieldOptions clone() => create()..mergeFromMessage(this); + + @$pb.TagNumber(2) + $core.bool get packed => $_getBF(0); + @$pb.TagNumber(3) + $core.bool get deprecated => $_getBF(1); +} + +class EnumOptions extends $pb.GeneratedMessage { + EnumOptions._() : super(); + static final $pb.BuilderInfo _i = $pb.BuilderInfo('EnumOptions', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + EnumOptions createEmptyInstance() => create(); + static EnumOptions create() => EnumOptions._(); + @$core.override + EnumOptions clone() => create()..mergeFromMessage(this); +} + +class ServiceOptions extends $pb.GeneratedMessage { + ServiceOptions._() : super(); + static final $pb.BuilderInfo _i = $pb.BuilderInfo('ServiceOptions', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + ServiceOptions createEmptyInstance() => create(); + static ServiceOptions create() => ServiceOptions._(); + @$core.override + ServiceOptions clone() => create()..mergeFromMessage(this); +} + +class MethodOptions extends $pb.GeneratedMessage { + MethodOptions._() : super(); + static final $pb.BuilderInfo _i = $pb.BuilderInfo('MethodOptions', + package: const $pb.PackageName('google.protobuf'), + createEmptyInstance: create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + @$core.override + MethodOptions createEmptyInstance() => create(); + static MethodOptions create() => MethodOptions._(); + @$core.override + MethodOptions clone() => create()..mergeFromMessage(this); +} diff --git a/lib/generated/reflection.pb.dart b/lib/generated/reflection.pb.dart new file mode 100644 index 0000000..f1dd7c7 --- /dev/null +++ b/lib/generated/reflection.pb.dart @@ -0,0 +1,571 @@ +// Generated code - do not modify +// ignore_for_file: annotate_overrides, camel_case_types, constant_identifier_names +// ignore_for_file: non_constant_identifier_names, avoid_print +// Based on grpc.reflection.v1alpha.reflection.proto + +import 'dart:core' as $core; +import 'package:protobuf/protobuf.dart' as $pb; + +enum ServerReflectionRequest_MessageRequest { + fileByFilename, + fileContainingSymbol, + fileContainingExtension, + allExtensionNumbersOfType, + listServices, + notSet +} + +class ServerReflectionRequest extends $pb.GeneratedMessage { + factory ServerReflectionRequest({ + $core.String? host, + $core.String? fileByFilename, + $core.String? fileContainingSymbol, + ExtensionRequest? fileContainingExtension, + $core.String? allExtensionNumbersOfType, + $core.String? listServices, + }) { + final result = create(); + if (host != null) result.host = host; + if (fileByFilename != null) result.fileByFilename = fileByFilename; + if (fileContainingSymbol != null) result.fileContainingSymbol = fileContainingSymbol; + if (fileContainingExtension != null) result.fileContainingExtension = fileContainingExtension; + if (allExtensionNumbersOfType != null) result.allExtensionNumbersOfType = allExtensionNumbersOfType; + if (listServices != null) result.listServices = listServices; + return result; + } + + ServerReflectionRequest._() : super(); + + factory ServerReflectionRequest.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + factory ServerReflectionRequest.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static const $core.Map<$core.int, ServerReflectionRequest_MessageRequest> + _ServerReflectionRequest_MessageRequestByTag = { + 3: ServerReflectionRequest_MessageRequest.fileByFilename, + 4: ServerReflectionRequest_MessageRequest.fileContainingSymbol, + 5: ServerReflectionRequest_MessageRequest.fileContainingExtension, + 6: ServerReflectionRequest_MessageRequest.allExtensionNumbersOfType, + 7: ServerReflectionRequest_MessageRequest.listServices, + 0: ServerReflectionRequest_MessageRequest.notSet + }; + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + 'ServerReflectionRequest', + package: const $pb.PackageName('grpc.reflection.v1alpha'), + createEmptyInstance: create) + ..oo(0, [3, 4, 5, 6, 7]) + ..aOS(1, 'host') + ..aOS(3, 'fileByFilename') + ..aOS(4, 'fileContainingSymbol') + ..aOM(5, 'fileContainingExtension', + subBuilder: ExtensionRequest.create) + ..aOS(6, 'allExtensionNumbersOfType') + ..aOS(7, 'listServices') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + ServerReflectionRequest createEmptyInstance() => create(); + + static ServerReflectionRequest create() => ServerReflectionRequest._(); + + @$core.override + ServerReflectionRequest clone() => ServerReflectionRequest()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + ServerReflectionRequest_MessageRequest whichMessageRequest() => + _ServerReflectionRequest_MessageRequestByTag[$_whichOneof(0)] ?? + ServerReflectionRequest_MessageRequest.notSet; + + void clearMessageRequest() => clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + $core.String get host => $_getSZ(0); + @$pb.TagNumber(1) + set host($core.String v) => $_setString(0, v); + @$pb.TagNumber(1) + $core.bool hasHost() => $_has(0); + @$pb.TagNumber(1) + void clearHost() => clearField(1); + + @$pb.TagNumber(3) + $core.String get fileByFilename => $_getSZ(1); + @$pb.TagNumber(3) + set fileByFilename($core.String v) => $_setString(1, v); + @$pb.TagNumber(3) + $core.bool hasFileByFilename() => $_has(1); + @$pb.TagNumber(3) + void clearFileByFilename() => clearField(3); + + @$pb.TagNumber(4) + $core.String get fileContainingSymbol => $_getSZ(2); + @$pb.TagNumber(4) + set fileContainingSymbol($core.String v) => $_setString(2, v); + @$pb.TagNumber(4) + $core.bool hasFileContainingSymbol() => $_has(2); + @$pb.TagNumber(4) + void clearFileContainingSymbol() => clearField(4); + + @$pb.TagNumber(5) + ExtensionRequest get fileContainingExtension => $_getN(3); + @$pb.TagNumber(5) + set fileContainingExtension(ExtensionRequest v) => setField(5, v); + @$pb.TagNumber(5) + $core.bool hasFileContainingExtension() => $_has(3); + @$pb.TagNumber(5) + void clearFileContainingExtension() => clearField(5); + @$pb.TagNumber(5) + ExtensionRequest ensureFileContainingExtension() => $_ensure(3); + + @$pb.TagNumber(6) + $core.String get allExtensionNumbersOfType => $_getSZ(4); + @$pb.TagNumber(6) + set allExtensionNumbersOfType($core.String v) => $_setString(4, v); + @$pb.TagNumber(6) + $core.bool hasAllExtensionNumbersOfType() => $_has(4); + @$pb.TagNumber(6) + void clearAllExtensionNumbersOfType() => clearField(6); + + @$pb.TagNumber(7) + $core.String get listServices => $_getSZ(5); + @$pb.TagNumber(7) + set listServices($core.String v) => $_setString(5, v); + @$pb.TagNumber(7) + $core.bool hasListServices() => $_has(5); + @$pb.TagNumber(7) + void clearListServices() => clearField(7); +} + +class ExtensionRequest extends $pb.GeneratedMessage { + factory ExtensionRequest({ + $core.String? containingType, + $core.int? extensionNumber, + }) { + final result = create(); + if (containingType != null) result.containingType = containingType; + if (extensionNumber != null) result.extensionNumber = extensionNumber; + return result; + } + + ExtensionRequest._() : super(); + + factory ExtensionRequest.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('ExtensionRequest', + package: const $pb.PackageName('grpc.reflection.v1alpha'), + createEmptyInstance: create) + ..aOS(1, 'containingType') + ..a<$core.int>(2, 'extensionNumber', $pb.PbFieldType.O3) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + ExtensionRequest createEmptyInstance() => create(); + + static ExtensionRequest create() => ExtensionRequest._(); + + @$core.override + ExtensionRequest clone() => ExtensionRequest()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.String get containingType => $_getSZ(0); + @$pb.TagNumber(1) + set containingType($core.String v) => $_setString(0, v); + @$pb.TagNumber(1) + $core.bool hasContainingType() => $_has(0); + @$pb.TagNumber(1) + void clearContainingType() => clearField(1); + + @$pb.TagNumber(2) + $core.int get extensionNumber => $_getIZ(1); + @$pb.TagNumber(2) + set extensionNumber($core.int v) => $_setSignedInt32(1, v); + @$pb.TagNumber(2) + $core.bool hasExtensionNumber() => $_has(1); + @$pb.TagNumber(2) + void clearExtensionNumber() => clearField(2); +} + +enum ServerReflectionResponse_MessageResponse { + fileDescriptorResponse, + allExtensionNumbersResponse, + listServicesResponse, + errorResponse, + notSet +} + +class ServerReflectionResponse extends $pb.GeneratedMessage { + factory ServerReflectionResponse({ + $core.String? validHost, + ServerReflectionRequest? originalRequest, + FileDescriptorResponse? fileDescriptorResponse, + ExtensionNumberResponse? allExtensionNumbersResponse, + ListServiceResponse? listServicesResponse, + ErrorResponse? errorResponse, + }) { + final result = create(); + if (validHost != null) result.validHost = validHost; + if (originalRequest != null) result.originalRequest = originalRequest; + if (fileDescriptorResponse != null) result.fileDescriptorResponse = fileDescriptorResponse; + if (allExtensionNumbersResponse != null) result.allExtensionNumbersResponse = allExtensionNumbersResponse; + if (listServicesResponse != null) result.listServicesResponse = listServicesResponse; + if (errorResponse != null) result.errorResponse = errorResponse; + return result; + } + + ServerReflectionResponse._() : super(); + + factory ServerReflectionResponse.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static const $core.Map<$core.int, ServerReflectionResponse_MessageResponse> + _ServerReflectionResponse_MessageResponseByTag = { + 4: ServerReflectionResponse_MessageResponse.fileDescriptorResponse, + 5: ServerReflectionResponse_MessageResponse.allExtensionNumbersResponse, + 6: ServerReflectionResponse_MessageResponse.listServicesResponse, + 7: ServerReflectionResponse_MessageResponse.errorResponse, + 0: ServerReflectionResponse_MessageResponse.notSet + }; + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + 'ServerReflectionResponse', + package: const $pb.PackageName('grpc.reflection.v1alpha'), + createEmptyInstance: create) + ..oo(0, [4, 5, 6, 7]) + ..aOS(1, 'validHost') + ..aOM(2, 'originalRequest', + subBuilder: ServerReflectionRequest.create) + ..aOM(4, 'fileDescriptorResponse', + subBuilder: FileDescriptorResponse.create) + ..aOM(5, 'allExtensionNumbersResponse', + subBuilder: ExtensionNumberResponse.create) + ..aOM(6, 'listServicesResponse', + subBuilder: ListServiceResponse.create) + ..aOM(7, 'errorResponse', subBuilder: ErrorResponse.create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + ServerReflectionResponse createEmptyInstance() => create(); + + static ServerReflectionResponse create() => ServerReflectionResponse._(); + + @$core.override + ServerReflectionResponse clone() => ServerReflectionResponse()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + ServerReflectionResponse_MessageResponse whichMessageResponse() => + _ServerReflectionResponse_MessageResponseByTag[$_whichOneof(0)] ?? + ServerReflectionResponse_MessageResponse.notSet; + + void clearMessageResponse() => clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + $core.String get validHost => $_getSZ(0); + @$pb.TagNumber(1) + set validHost($core.String v) => $_setString(0, v); + @$pb.TagNumber(1) + $core.bool hasValidHost() => $_has(0); + @$pb.TagNumber(1) + void clearValidHost() => clearField(1); + + @$pb.TagNumber(2) + ServerReflectionRequest get originalRequest => $_getN(1); + @$pb.TagNumber(2) + set originalRequest(ServerReflectionRequest v) => setField(2, v); + @$pb.TagNumber(2) + $core.bool hasOriginalRequest() => $_has(1); + @$pb.TagNumber(2) + void clearOriginalRequest() => clearField(2); + @$pb.TagNumber(2) + ServerReflectionRequest ensureOriginalRequest() => $_ensure(1); + + @$pb.TagNumber(4) + FileDescriptorResponse get fileDescriptorResponse => $_getN(2); + @$pb.TagNumber(4) + set fileDescriptorResponse(FileDescriptorResponse v) => setField(4, v); + @$pb.TagNumber(4) + $core.bool hasFileDescriptorResponse() => $_has(2); + @$pb.TagNumber(4) + void clearFileDescriptorResponse() => clearField(4); + @$pb.TagNumber(4) + FileDescriptorResponse ensureFileDescriptorResponse() => $_ensure(2); + + @$pb.TagNumber(5) + ExtensionNumberResponse get allExtensionNumbersResponse => $_getN(3); + @$pb.TagNumber(5) + set allExtensionNumbersResponse(ExtensionNumberResponse v) => setField(5, v); + @$pb.TagNumber(5) + $core.bool hasAllExtensionNumbersResponse() => $_has(3); + @$pb.TagNumber(5) + void clearAllExtensionNumbersResponse() => clearField(5); + @$pb.TagNumber(5) + ExtensionNumberResponse ensureAllExtensionNumbersResponse() => $_ensure(3); + + @$pb.TagNumber(6) + ListServiceResponse get listServicesResponse => $_getN(4); + @$pb.TagNumber(6) + set listServicesResponse(ListServiceResponse v) => setField(6, v); + @$pb.TagNumber(6) + $core.bool hasListServicesResponse() => $_has(4); + @$pb.TagNumber(6) + void clearListServicesResponse() => clearField(6); + @$pb.TagNumber(6) + ListServiceResponse ensureListServicesResponse() => $_ensure(4); + + @$pb.TagNumber(7) + ErrorResponse get errorResponse => $_getN(5); + @$pb.TagNumber(7) + set errorResponse(ErrorResponse v) => setField(7, v); + @$pb.TagNumber(7) + $core.bool hasErrorResponse() => $_has(5); + @$pb.TagNumber(7) + void clearErrorResponse() => clearField(7); + @$pb.TagNumber(7) + ErrorResponse ensureErrorResponse() => $_ensure(5); +} + +class FileDescriptorResponse extends $pb.GeneratedMessage { + factory FileDescriptorResponse({ + $core.Iterable<$core.List<$core.int>>? fileDescriptorProto, + }) { + final result = create(); + if (fileDescriptorProto != null) result.fileDescriptorProto.addAll(fileDescriptorProto); + return result; + } + + FileDescriptorResponse._() : super(); + + factory FileDescriptorResponse.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('FileDescriptorResponse', + package: const $pb.PackageName('grpc.reflection.v1alpha'), + createEmptyInstance: create) + ..p<$core.List<$core.int>>(1, 'fileDescriptorProto', $pb.PbFieldType.PY) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + FileDescriptorResponse createEmptyInstance() => create(); + + static FileDescriptorResponse create() => FileDescriptorResponse._(); + + @$core.override + FileDescriptorResponse clone() => FileDescriptorResponse()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.List<$core.List<$core.int>> get fileDescriptorProto => $_getList(0); +} + +class ExtensionNumberResponse extends $pb.GeneratedMessage { + factory ExtensionNumberResponse({ + $core.String? baseTypeName, + $core.Iterable<$core.int>? extensionNumber, + }) { + final result = create(); + if (baseTypeName != null) result.baseTypeName = baseTypeName; + if (extensionNumber != null) result.extensionNumber.addAll(extensionNumber); + return result; + } + + ExtensionNumberResponse._() : super(); + + factory ExtensionNumberResponse.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('ExtensionNumberResponse', + package: const $pb.PackageName('grpc.reflection.v1alpha'), + createEmptyInstance: create) + ..aOS(1, 'baseTypeName') + ..p<$core.int>(2, 'extensionNumber', $pb.PbFieldType.P3) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + ExtensionNumberResponse createEmptyInstance() => create(); + + static ExtensionNumberResponse create() => ExtensionNumberResponse._(); + + @$core.override + ExtensionNumberResponse clone() => ExtensionNumberResponse()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.String get baseTypeName => $_getSZ(0); + @$pb.TagNumber(1) + set baseTypeName($core.String v) => $_setString(0, v); + @$pb.TagNumber(1) + $core.bool hasBaseTypeName() => $_has(0); + @$pb.TagNumber(1) + void clearBaseTypeName() => clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get extensionNumber => $_getList(1); +} + +class ListServiceResponse extends $pb.GeneratedMessage { + factory ListServiceResponse({ + $core.Iterable? service, + }) { + final result = create(); + if (service != null) result.service.addAll(service); + return result; + } + + ListServiceResponse._() : super(); + + factory ListServiceResponse.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('ListServiceResponse', + package: const $pb.PackageName('grpc.reflection.v1alpha'), + createEmptyInstance: create) + ..pc(1, 'service', $pb.PbFieldType.PM, + subBuilder: ServiceResponse.create) + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + ListServiceResponse createEmptyInstance() => create(); + + static ListServiceResponse create() => ListServiceResponse._(); + + @$core.override + ListServiceResponse clone() => ListServiceResponse()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.List get service => $_getList(0); +} + +class ServiceResponse extends $pb.GeneratedMessage { + factory ServiceResponse({ + $core.String? name, + }) { + final result = create(); + if (name != null) result.name = name; + return result; + } + + ServiceResponse._() : super(); + + factory ServiceResponse.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('ServiceResponse', + package: const $pb.PackageName('grpc.reflection.v1alpha'), + createEmptyInstance: create) + ..aOS(1, 'name') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + ServiceResponse createEmptyInstance() => create(); + + static ServiceResponse create() => ServiceResponse._(); + + @$core.override + ServiceResponse clone() => ServiceResponse()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) => $_setString(0, v); + @$pb.TagNumber(1) + $core.bool hasName() => $_has(0); + @$pb.TagNumber(1) + void clearName() => clearField(1); +} + +class ErrorResponse extends $pb.GeneratedMessage { + factory ErrorResponse({ + $core.int? errorCode, + $core.String? errorMessage, + }) { + final result = create(); + if (errorCode != null) result.errorCode = errorCode; + if (errorMessage != null) result.errorMessage = errorMessage; + return result; + } + + ErrorResponse._() : super(); + + factory ErrorResponse.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo('ErrorResponse', + package: const $pb.PackageName('grpc.reflection.v1alpha'), + createEmptyInstance: create) + ..a<$core.int>(1, 'errorCode', $pb.PbFieldType.O3) + ..aOS(2, 'errorMessage') + ..hasRequiredFields = false; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.override + ErrorResponse createEmptyInstance() => create(); + + static ErrorResponse create() => ErrorResponse._(); + + @$core.override + ErrorResponse clone() => ErrorResponse()..mergeFromMessage(this); + + static $core.List createRepeated() => []; + + @$pb.TagNumber(1) + $core.int get errorCode => $_getIZ(0); + @$pb.TagNumber(1) + set errorCode($core.int v) => $_setSignedInt32(0, v); + @$pb.TagNumber(1) + $core.bool hasErrorCode() => $_has(0); + @$pb.TagNumber(1) + void clearErrorCode() => clearField(1); + + @$pb.TagNumber(2) + $core.String get errorMessage => $_getSZ(1); + @$pb.TagNumber(2) + set errorMessage($core.String v) => $_setString(1, v); + @$pb.TagNumber(2) + $core.bool hasErrorMessage() => $_has(1); + @$pb.TagNumber(2) + void clearErrorMessage() => clearField(2); +} diff --git a/lib/generated/reflection.pbgrpc.dart b/lib/generated/reflection.pbgrpc.dart new file mode 100644 index 0000000..fb1e902 --- /dev/null +++ b/lib/generated/reflection.pbgrpc.dart @@ -0,0 +1,64 @@ +// Generated code - do not modify +// ignore_for_file: annotate_overrides, camel_case_types, library_prefixes +// ignore_for_file: use_super_parameters, unused_import +// Based on grpc.reflection.v1alpha.reflection.proto + +import 'dart:async' as $async; +import 'dart:core' as $core; + +import 'package:grpc/grpc.dart' as $grpc; + +import 'reflection.pb.dart' as $0; + +export 'reflection.pb.dart'; + +class ServerReflectionClient extends $grpc.Client { + static final _$serverReflectionInfo = + $grpc.ClientMethod<$0.ServerReflectionRequest, $0.ServerReflectionResponse>( + '/grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo', + ($0.ServerReflectionRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.ServerReflectionResponse.fromBuffer(value), + ); + + ServerReflectionClient( + $grpc.ClientChannel channel, { + $grpc.CallOptions? options, + $core.Iterable<$grpc.ClientInterceptor>? interceptors, + }) : super( + channel, + options: options, + interceptors: interceptors, + ); + + $grpc.ResponseStream<$0.ServerReflectionResponse> serverReflectionInfo( + $async.Stream<$0.ServerReflectionRequest> request, { + $grpc.CallOptions? options, + }) { + return $createStreamingCall( + _$serverReflectionInfo, + request, + options: options, + ); + } +} + +abstract class ServerReflectionServiceBase extends $grpc.Service { + @$core.override + $core.String get $name => 'grpc.reflection.v1alpha.ServerReflection'; + + ServerReflectionServiceBase() { + $addMethod($grpc.ServiceMethod<$0.ServerReflectionRequest, $0.ServerReflectionResponse>( + 'ServerReflectionInfo', + serverReflectionInfo, + true, + true, + ($core.List<$core.int> value) => $0.ServerReflectionRequest.fromBuffer(value), + ($0.ServerReflectionResponse value) => value.writeToBuffer(), + )); + } + + $async.Stream<$0.ServerReflectionResponse> serverReflectionInfo( + $grpc.ServiceCall call, + $async.Stream<$0.ServerReflectionRequest> request, + ); +} diff --git a/lib/providers/providers.dart b/lib/providers/providers.dart index 603a162..e3fdf38 100644 --- a/lib/providers/providers.dart +++ b/lib/providers/providers.dart @@ -3,12 +3,108 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../api/types.dart'; import '../api/client.dart'; +import '../api/grpc_client.dart'; +import '../api/grpc_config.dart'; import '../api/openapi_config.dart'; import '../services/auth_service.dart'; import '../models/user_profile.dart'; import '../models/delivery_route.dart'; import '../models/delivery.dart'; +// ============================================================================ +// API Mode Configuration - Feature Flag for HTTP/gRPC Transport +// ============================================================================ + +/// Enum representing the available API transport modes. +enum ApiMode { + /// Use HTTP/REST-based CQRS API client + http, + + /// Use gRPC-based CQRS API client + grpc, +} + +/// Configuration for API transport mode selection. +/// +/// This class allows switching between HTTP and gRPC transports +/// for API calls. Following the pattern from [ApiClientConfig]. +/// +/// Example usage: +/// ```dart +/// // To switch to gRPC, override the apiModeConfigProvider: +/// ProviderScope( +/// overrides: [ +/// apiModeConfigProvider.overrideWithValue(ApiModeConfig.developmentGrpc), +/// ], +/// child: MyApp(), +/// ) +/// ``` +class ApiModeConfig { + /// The transport mode to use for API calls. + final ApiMode mode; + + /// Whether to fall back to HTTP on gRPC failures. + /// Only applicable when [mode] is [ApiMode.grpc]. + final bool fallbackToHttpOnError; + + const ApiModeConfig({ + required this.mode, + this.fallbackToHttpOnError = true, + }); + + /// Development configuration - defaults to HTTP for stability. + /// Use this for safe development when gRPC backend may be unavailable. + static const ApiModeConfig development = ApiModeConfig( + mode: ApiMode.http, + fallbackToHttpOnError: true, + ); + + /// gRPC-first development configuration for testing gRPC integration. + /// Use this when actively developing/testing gRPC functionality. + static const ApiModeConfig developmentGrpc = ApiModeConfig( + mode: ApiMode.grpc, + fallbackToHttpOnError: true, + ); + + /// Production configuration - uses HTTP until gRPC is verified stable. + static const ApiModeConfig production = ApiModeConfig( + mode: ApiMode.http, + fallbackToHttpOnError: false, + ); + + /// Production gRPC configuration for when gRPC is production-ready. + static const ApiModeConfig productionGrpc = ApiModeConfig( + mode: ApiMode.grpc, + fallbackToHttpOnError: false, + ); + + /// Whether the current mode is gRPC. + bool get isGrpc => mode == ApiMode.grpc; + + /// Whether the current mode is HTTP. + bool get isHttp => mode == ApiMode.http; +} + +/// Provider for API mode configuration. +/// +/// Override this provider to switch between HTTP and gRPC: +/// ```dart +/// ProviderScope( +/// overrides: [ +/// apiModeConfigProvider.overrideWithValue(ApiModeConfig.developmentGrpc), +/// ], +/// child: MyApp(), +/// ) +/// ``` +final apiModeConfigProvider = Provider((ref) { + // Default to HTTP for safety during transition + return ApiModeConfig.development; +}); + +// ============================================================================ +// Core Service Providers +// ============================================================================ + final authServiceProvider = Provider((ref) { return AuthService(config: AuthConfig.development); }); @@ -21,6 +117,31 @@ final apiClientProvider = Provider((ref) { ); }); +/// Provider for the gRPC-based CQRS API client. +/// +/// Uses auto-dispose to properly shut down the gRPC channel when the provider +/// is no longer being watched. This ensures network resources are cleaned up. +/// +/// Example usage: +/// ```dart +/// final grpcClient = ref.watch(grpcClientProvider); +/// final result = await grpcClient.getDeliveryRoutes(); +/// ``` +final grpcClientProvider = Provider.autoDispose((ref) { + final authService = ref.watch(authServiceProvider); + final client = GrpcCqrsApiClient( + config: GrpcConfig.development, + authService: authService, + ); + + // Register disposal callback to clean up gRPC channel resources + ref.onDispose(() { + client.shutdown(); + }); + + return client; +}); + final isAuthenticatedProvider = FutureProvider((ref) async { final authService = ref.watch(authServiceProvider); return await authService.isAuthenticated(); @@ -38,7 +159,9 @@ final authTokenProvider = FutureProvider((ref) async { return await authService.getToken(); }); -final deliveryRoutesProvider = FutureProvider>((ref) async { +/// Internal HTTP-based delivery routes provider. +/// Use [deliveryRoutesProvider] instead, which respects the API mode configuration. +final _httpDeliveryRoutesProvider = FutureProvider>((ref) async { final authService = ref.watch(authServiceProvider); final isAuthenticated = await authService.isAuthenticated(); @@ -68,7 +191,120 @@ final deliveryRoutesProvider = FutureProvider>((ref) async { return result.whenSuccess((routes) => routes) ?? []; }); -final deliveriesProvider = FutureProvider.family, int>((ref, routeFragmentId) async { +/// Unified delivery routes provider that respects the API mode configuration. +/// +/// Automatically switches between HTTP and gRPC based on [apiModeConfigProvider]. +/// When gRPC mode is enabled and [ApiModeConfig.fallbackToHttpOnError] is true, +/// falls back to HTTP on gRPC failures. +/// +/// Example usage: +/// ```dart +/// final routes = ref.watch(deliveryRoutesProvider); +/// routes.when( +/// data: (data) => displayRoutes(data), +/// loading: () => showLoading(), +/// error: (error, stack) => showError(error), +/// ); +/// ``` +final deliveryRoutesProvider = FutureProvider>((ref) async { + final apiModeConfig = ref.watch(apiModeConfigProvider); + + if (apiModeConfig.isGrpc) { + try { + return await ref.watch(grpcDeliveryRoutesProvider.future); + } catch (e) { + if (apiModeConfig.fallbackToHttpOnError) { + debugPrint('gRPC failed, falling back to HTTP: $e'); + return await ref.watch(_httpDeliveryRoutesProvider.future); + } + rethrow; + } + } + + return await ref.watch(_httpDeliveryRoutesProvider.future); +}); + +/// Provider for delivery routes using gRPC. +/// +/// This is the gRPC-based alternative to [deliveryRoutesProvider]. +/// Uses [GrpcCqrsApiClient] for improved performance and type safety. +/// +/// Example usage: +/// ```dart +/// final routes = ref.watch(grpcDeliveryRoutesProvider); +/// routes.when( +/// data: (data) => displayRoutes(data), +/// loading: () => showLoading(), +/// error: (error, stack) => showError(error), +/// ); +/// ``` +final grpcDeliveryRoutesProvider = FutureProvider>((ref) async { + final authService = ref.watch(authServiceProvider); + final isAuthenticated = await authService.isAuthenticated(); + + if (!isAuthenticated) { + throw Exception('User not authenticated'); + } + + final grpcClient = ref.watch(grpcClientProvider); + final result = await grpcClient.getDeliveryRoutes(); + + return result.when( + success: (routes) => routes, + onError: (error) { + debugPrint('ERROR fetching delivery routes via gRPC: ${error.message}'); + if (error.originalException != null) { + debugPrint('Original exception: ${error.originalException}'); + } + throw Exception(error.message); + }, + ); +}); + +/// Provider for deliveries using gRPC. +/// +/// This is the gRPC-based alternative to [deliveriesProvider]. +/// Uses [GrpcCqrsApiClient] for improved performance and type safety. +/// Takes a [routeFragmentId] parameter to fetch deliveries for a specific route. +/// +/// Example usage: +/// ```dart +/// final deliveries = ref.watch(grpcDeliveriesProvider(routeFragmentId)); +/// deliveries.when( +/// data: (data) => displayDeliveries(data), +/// loading: () => showLoading(), +/// error: (error, stack) => showError(error), +/// ); +/// ``` +final grpcDeliveriesProvider = FutureProvider.family, int>((ref, routeFragmentId) async { + final authService = ref.watch(authServiceProvider); + final isAuthenticated = await authService.isAuthenticated(); + + if (!isAuthenticated) { + throw Exception('User not authenticated'); + } + + final grpcClient = ref.watch(grpcClientProvider); + final result = await grpcClient.getDeliveries(routeFragmentId: routeFragmentId); + + final deliveries = result.when( + success: (deliveries) => deliveries, + onError: (error) { + debugPrint('ERROR fetching deliveries for route $routeFragmentId via gRPC: ${error.message}'); + if (error.originalException != null) { + debugPrint('Original exception: ${error.originalException}'); + } + throw Exception(error.message); + }, + ); + + // Always append the warehouse delivery at the end + return [...deliveries, Delivery.createWarehouseDelivery()]; +}); + +/// Internal HTTP-based deliveries provider. +/// Use [deliveriesProvider] instead, which respects the API mode configuration. +final _httpDeliveriesProvider = FutureProvider.family, int>((ref, routeFragmentId) async { final authService = ref.watch(authServiceProvider); final isAuthenticated = await authService.isAuthenticated(); @@ -108,6 +344,41 @@ final deliveriesProvider = FutureProvider.family, int>((ref, rout return [...deliveries, Delivery.createWarehouseDelivery()]; }); +/// Unified deliveries provider that respects the API mode configuration. +/// +/// Automatically switches between HTTP and gRPC based on [apiModeConfigProvider]. +/// When gRPC mode is enabled and [ApiModeConfig.fallbackToHttpOnError] is true, +/// falls back to HTTP on gRPC failures. +/// +/// Takes a [routeFragmentId] parameter to fetch deliveries for a specific route. +/// +/// Example usage: +/// ```dart +/// final deliveries = ref.watch(deliveriesProvider(routeFragmentId)); +/// deliveries.when( +/// data: (data) => displayDeliveries(data), +/// loading: () => showLoading(), +/// error: (error, stack) => showError(error), +/// ); +/// ``` +final deliveriesProvider = FutureProvider.family, int>((ref, routeFragmentId) async { + final apiModeConfig = ref.watch(apiModeConfigProvider); + + if (apiModeConfig.isGrpc) { + try { + return await ref.watch(grpcDeliveriesProvider(routeFragmentId).future); + } catch (e) { + if (apiModeConfig.fallbackToHttpOnError) { + debugPrint('gRPC failed for route $routeFragmentId, falling back to HTTP: $e'); + return await ref.watch(_httpDeliveriesProvider(routeFragmentId).future); + } + rethrow; + } + } + + return await ref.watch(_httpDeliveriesProvider(routeFragmentId).future); +}); + /// Provider to get all deliveries from all routes final allDeliveriesProvider = FutureProvider>((ref) async { final routes = await ref.read(deliveryRoutesProvider.future); diff --git a/pubspec.lock b/pubspec.lock index 5dcac20..34ef282 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -266,7 +266,7 @@ packages: source: hosted version: "0.9.3+4" fixnum: - dependency: transitive + dependency: "direct main" description: name: fixnum sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be @@ -387,6 +387,13 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.3" + flutter_svrnty_cqrs_datasource: + dependency: "direct main" + description: + path: "/Users/mathias/Documents/workspaces/svrnty/flutter_cqrs_datasource" + relative: false + source: path + version: "0.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -437,6 +444,14 @@ packages: url: "https://pub.dev" source: hosted version: "17.0.1" + google_identity_services_web: + dependency: transitive + description: + name: google_identity_services_web + sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454" + url: "https://pub.dev" + source: hosted + version: "0.3.3+1" google_navigation_flutter: dependency: "direct main" description: @@ -445,6 +460,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.2" + googleapis_auth: + dependency: transitive + description: + name: googleapis_auth + sha256: b81fe352cc4a330b3710d2b7ad258d9bcef6f909bb759b306bf42973a7d046db + url: "https://pub.dev" + source: hosted + version: "2.0.0" graphs: dependency: transitive description: @@ -453,6 +476,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + grpc: + dependency: "direct main" + description: + name: grpc + sha256: "86be3a7d39ad865b214a7370021ac80e68939238b507730de6d97fc662cb2723" + url: "https://pub.dev" + source: hosted + version: "5.1.0" http: dependency: "direct main" description: @@ -461,6 +492,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.6.0" + http2: + dependency: transitive + description: + name: http2 + sha256: "382d3aefc5bd6dc68c6b892d7664f29b5beb3251611ae946a98d35158a82bbfa" + url: "https://pub.dev" + source: hosted + version: "2.3.1" http_interceptor: dependency: "direct main" description: @@ -845,6 +884,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.3" + protobuf: + dependency: "direct main" + description: + name: protobuf + sha256: "75ec242d22e950bdcc79ee38dd520ce4ee0bc491d7fadc4ea47694604d22bf06" + url: "https://pub.dev" + source: hosted + version: "6.0.0" pub_semver: dependency: transitive description: @@ -1251,5 +1298,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.9.2 <4.0.0" + dart: ">=3.10.1 <4.0.0" flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index 5abf42a..7fc5b22 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,13 @@ dependencies: http_interceptor: ^2.0.0 google_navigation_flutter: ^0.8.2 + # gRPC dependencies for API migration + flutter_svrnty_cqrs_datasource: + path: /Users/mathias/Documents/workspaces/svrnty/flutter_cqrs_datasource + grpc: ^5.1.0 + protobuf: ^6.0.0 + fixnum: ^1.1.0 + dev_dependencies: flutter_test: sdk: flutter diff --git a/test/api/api_mode_config_test.dart b/test/api/api_mode_config_test.dart new file mode 100644 index 0000000..7f3496e --- /dev/null +++ b/test/api/api_mode_config_test.dart @@ -0,0 +1,67 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:planb_logistic/providers/providers.dart'; + +void main() { + group('ApiMode', () { + test('has correct values', () { + expect(ApiMode.values.length, equals(2)); + expect(ApiMode.values, contains(ApiMode.http)); + expect(ApiMode.values, contains(ApiMode.grpc)); + }); + }); + + group('ApiModeConfig', () { + test('development config defaults to HTTP', () { + const config = ApiModeConfig.development; + + expect(config.mode, equals(ApiMode.http)); + expect(config.isHttp, isTrue); + expect(config.isGrpc, isFalse); + expect(config.fallbackToHttpOnError, isTrue); + }); + + test('developmentGrpc config uses gRPC', () { + const config = ApiModeConfig.developmentGrpc; + + expect(config.mode, equals(ApiMode.grpc)); + expect(config.isGrpc, isTrue); + expect(config.isHttp, isFalse); + expect(config.fallbackToHttpOnError, isTrue); + }); + + test('production config defaults to HTTP', () { + const config = ApiModeConfig.production; + + expect(config.mode, equals(ApiMode.http)); + expect(config.isHttp, isTrue); + expect(config.isGrpc, isFalse); + expect(config.fallbackToHttpOnError, isFalse); + }); + + test('productionGrpc config uses gRPC without fallback', () { + const config = ApiModeConfig.productionGrpc; + + expect(config.mode, equals(ApiMode.grpc)); + expect(config.isGrpc, isTrue); + expect(config.isHttp, isFalse); + expect(config.fallbackToHttpOnError, isFalse); + }); + + test('custom config can be created', () { + const config = ApiModeConfig( + mode: ApiMode.grpc, + fallbackToHttpOnError: false, + ); + + expect(config.mode, equals(ApiMode.grpc)); + expect(config.isGrpc, isTrue); + expect(config.fallbackToHttpOnError, isFalse); + }); + + test('default fallbackToHttpOnError is true', () { + const config = ApiModeConfig(mode: ApiMode.grpc); + + expect(config.fallbackToHttpOnError, isTrue); + }); + }); +} diff --git a/test/api/grpc_client_test.dart b/test/api/grpc_client_test.dart new file mode 100644 index 0000000..6794f4f --- /dev/null +++ b/test/api/grpc_client_test.dart @@ -0,0 +1,85 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:planb_logistic/api/grpc_client.dart'; +import 'package:planb_logistic/api/grpc_config.dart'; + +void main() { + group('GrpcCqrsApiClient', () { + test('can be created with development config', () { + final client = GrpcCqrsApiClient( + config: GrpcConfig.development, + authService: null, + ); + + expect(client.config, equals(GrpcConfig.development)); + expect(client.isConnected, isFalse); + }); + + test('can be created with production config', () { + final client = GrpcCqrsApiClient( + config: GrpcConfig.production, + authService: null, + ); + + expect(client.config, equals(GrpcConfig.production)); + expect(client.isConnected, isFalse); + }); + + test('channel is created lazily', () { + final client = GrpcCqrsApiClient( + config: GrpcConfig.development, + authService: null, + ); + + expect(client.isConnected, isFalse); + + // Access the channel to trigger lazy initialization + final channel = client.channel; + + expect(channel, isNotNull); + expect(client.isConnected, isTrue); + }); + + test('delivery client is created lazily', () { + final client = GrpcCqrsApiClient( + config: GrpcConfig.development, + authService: null, + ); + + // Access delivery client (implicitly creates channel first) + final deliveryClient = client.deliveryClient; + + expect(deliveryClient, isNotNull); + expect(client.isConnected, isTrue); + }); + + test('shutdown clears channel and client', () async { + final client = GrpcCqrsApiClient( + config: GrpcConfig.development, + authService: null, + ); + + // Initialize the channel + final _ = client.channel; + expect(client.isConnected, isTrue); + + // Shutdown + await client.shutdown(); + expect(client.isConnected, isFalse); + }); + + test('terminate clears channel and client', () async { + final client = GrpcCqrsApiClient( + config: GrpcConfig.development, + authService: null, + ); + + // Initialize the channel + final _ = client.channel; + expect(client.isConnected, isTrue); + + // Terminate + await client.terminate(); + expect(client.isConnected, isFalse); + }); + }); +} diff --git a/test/api/grpc_config_test.dart b/test/api/grpc_config_test.dart new file mode 100644 index 0000000..4094b0c --- /dev/null +++ b/test/api/grpc_config_test.dart @@ -0,0 +1,56 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:planb_logistic/api/grpc_config.dart'; + +void main() { + group('GrpcConfig', () { + test('development config has correct values', () { + const config = GrpcConfig.development; + + expect(config.host, equals('192.168.88.228')); + expect(config.port, equals(5011)); + expect(config.useTls, isFalse); + expect(config.allowSelfSignedCertificate, isTrue); + expect(config.timeout, equals(const Duration(seconds: 30))); + expect(config.address, equals('192.168.88.228:5011')); + }); + + test('production config has correct values', () { + const config = GrpcConfig.production; + + expect(config.host, equals('grpc-route.goutezplanb.com')); + expect(config.port, equals(443)); + expect(config.useTls, isTrue); + expect(config.allowSelfSignedCertificate, isFalse); + expect(config.timeout, equals(const Duration(seconds: 30))); + expect(config.address, equals('grpc-route.goutezplanb.com:443')); + }); + + test('custom config can be created', () { + const config = GrpcConfig( + host: 'custom.example.com', + port: 9000, + timeout: Duration(seconds: 60), + useTls: true, + allowSelfSignedCertificate: true, + ); + + expect(config.host, equals('custom.example.com')); + expect(config.port, equals(9000)); + expect(config.useTls, isTrue); + expect(config.allowSelfSignedCertificate, isTrue); + expect(config.timeout, equals(const Duration(seconds: 60))); + expect(config.address, equals('custom.example.com:9000')); + }); + + test('default values are correctly applied', () { + const config = GrpcConfig( + host: 'test.example.com', + port: 443, + ); + + expect(config.useTls, isTrue); + expect(config.allowSelfSignedCertificate, isFalse); + expect(config.timeout, equals(const Duration(seconds: 30))); + }); + }); +} diff --git a/test/e2e/GRPC_E2E_VERIFICATION.md b/test/e2e/GRPC_E2E_VERIFICATION.md new file mode 100644 index 0000000..64b4a5e --- /dev/null +++ b/test/e2e/GRPC_E2E_VERIFICATION.md @@ -0,0 +1,192 @@ +# gRPC Integration E2E Verification Guide + +This document provides steps for verifying the gRPC integration end-to-end. + +## Prerequisites + +1. gRPC backend server running at `192.168.88.228:5011` +2. Flutter environment configured +3. Valid test credentials for authentication + +## Step 1: Enable gRPC Mode + +To enable gRPC mode, override the `apiModeConfigProvider` in the app's `ProviderScope`: + +### Option A: Code Change (for testing) + +Edit `lib/main.dart` to add the provider override: + +```dart +import 'package:planb_logistic/providers/providers.dart'; + +void main() { + runApp( + ProviderScope( + overrides: [ + // Enable gRPC mode with fallback to HTTP + apiModeConfigProvider.overrideWithValue(ApiModeConfig.developmentGrpc), + ], + child: const MyApp(), + ), + ); +} +``` + +### Option B: Environment-Based Toggle + +The app can also be configured to check an environment variable or build flag: + +```dart +final useGrpc = const bool.fromEnvironment('USE_GRPC', defaultValue: false); +final apiMode = useGrpc ? ApiModeConfig.developmentGrpc : ApiModeConfig.development; +``` + +Then run with: +```bash +flutter run -d chrome --dart-define=USE_GRPC=true +``` + +## Step 2: Login to App + +1. Launch the app in Chrome browser: + ```bash + flutter run -d chrome + ``` + +2. Click "Login with Keycloak" button +3. Enter valid credentials +4. Wait for redirect back to the app + +**Expected:** User is authenticated and redirected to the main app. + +## Step 3: Navigate to Routes Page + +1. After login, navigate to the Routes page +2. The app should fetch delivery routes via gRPC + +**Expected Behavior:** +- Routes list should populate with delivery route data +- No console errors related to gRPC +- In browser DevTools console, you should see: + - No "gRPC failed" messages (unless backend is unavailable) + - If gRPC fails and fallback is enabled, you may see: "gRPC failed, falling back to HTTP: ..." + +**Verification Points:** +- Routes display with correct data (id, name, deliveries count) +- Loading indicator shows during fetch +- Error state handled gracefully if backend unavailable + +## Step 4: Select a Route - Verify Deliveries Load + +1. Click on a route from the list +2. Navigate to the deliveries page for that route + +**Expected Behavior:** +- Deliveries list should populate with delivery data for the selected route +- Each delivery should show: + - Delivery index/number + - Customer name + - Address information + - Status (delivered/pending/skipped) +- Warehouse delivery should appear at the end of the list + +**Verification Points:** +- Correct number of deliveries loaded +- All delivery fields properly mapped from gRPC response +- Warehouse delivery appended correctly + +## Step 5: Mark a Delivery Complete + +1. Select an uncompleted delivery +2. Mark it as complete (tap completion button) + +**Expected Behavior:** +- Command sent via gRPC to `completeDelivery` endpoint +- Delivery status updates to "completed" +- UI refreshes to show new status + +**Verification Points:** +- No error messages +- Delivery marked as completed in the list +- Timestamp recorded for completion + +## Step 6: Check Browser Console for gRPC Logs + +Open browser DevTools (F12) and check the Console tab for: + +### Success Indicators: +- No error messages related to gRPC +- Successful data loading without fallback messages + +### Warning Indicators (acceptable): +- "gRPC failed, falling back to HTTP: ..." - indicates gRPC failed but HTTP fallback worked + +### Error Indicators (need investigation): +- "UNAUTHENTICATED" errors - token issue +- "UNAVAILABLE" errors - backend not reachable +- "DEADLINE_EXCEEDED" errors - timeout +- Unhandled exceptions + +## Verification Checklist + +| Step | Check | Status | +|------|-------|--------| +| 1 | gRPC mode enabled via provider override | [ ] | +| 2 | Login successful | [ ] | +| 3 | Routes load (via gRPC or HTTP fallback) | [ ] | +| 4 | Deliveries load for selected route | [ ] | +| 5 | Complete delivery command works | [ ] | +| 6 | No critical console errors | [ ] | + +## Troubleshooting + +### gRPC Backend Unavailable + +If the gRPC backend at `192.168.88.228:5011` is not reachable: + +1. **With fallback enabled (default):** App falls back to HTTP automatically +2. **Without fallback:** App shows error state + +To test with HTTP only: +```dart +apiModeConfigProvider.overrideWithValue(ApiModeConfig.development) +``` + +### Authentication Issues + +If you see UNAUTHENTICATED errors: + +1. Check that the auth token is valid +2. Try logging out and back in +3. Verify the token is being sent in gRPC metadata + +### Connection Timeout + +If requests are timing out: + +1. Check network connectivity to `192.168.88.228:5011` +2. Verify the backend server is running +3. Check firewall/VPN settings + +## Production Configuration + +For production deployment, use: + +```dart +apiModeConfigProvider.overrideWithValue(ApiModeConfig.productionGrpc) +``` + +This connects to `grpc-route.goutezplanb.com:443` with TLS enabled. + +## Test Commands + +```bash +# Run unit tests (no backend required) +flutter test + +# Run with gRPC enabled +flutter run -d chrome --dart-define=USE_GRPC=true + +# Check gRPC backend is reachable (requires grpcurl) +grpcurl -plaintext 192.168.88.228:5011 list +```