encodeDobroDeskOperation function

String encodeDobroDeskOperation(
  1. Map<String, Object?> request
)

Implementation

String encodeDobroDeskOperation(Map<String, Object?> request) {
  if (!_json(request)) _invalid();
  switch (request['operation']) {
    case 'request-client-tool':
      _only(request, ['operation', 'input']);
      final input = _record(request['input']);
      _only(input, ['toolName', 'basisMessageId', 'input']);
      if (input['toolName'] is! String ||
          !RegExp(r'^[a-z][a-z0-9_]{0,63}$')
              .hasMatch(input['toolName'] as String) ||
          !_reference(input['basisMessageId'])) _invalid();
      _toolPayload(input['input']);
      break;
    case 'complete-client-tool':
      _only(request, ['operation', 'input']);
      final input = _record(request['input']);
      _only(input, ['callId', 'output']);
      if (!_toolCall(input['callId'])) _invalid();
      _toolPayload(input['output']);
      break;
    case 'client-tool-status':
      _only(request, ['operation', 'callId']);
      if (!_toolCall(request['callId'])) _invalid();
      break;
    case 'submit-form':
      _only(request, ['operation', 'requestId', 'values']);
      if (!_reference(request['requestId'])) _invalid();
      _record(request['values']);
      break;
    case 'procedure-action':
      _only(request, ['operation', 'input']);
      final input = _record(request['input']);
      _only(input, [
        'action',
        'value',
        'idempotencyKey',
        'procedureId',
        'locale',
        'runId',
        'expectedUpdatedAt',
        'stepId',
        'parameterId',
        'timeZone',
        'nativeCommerceUi'
      ]);
      if (![
            'start',
            'answer',
            'availability',
            'select',
            'calendar_date',
            'back',
            'confirm',
            'decline'
          ].contains(input['action']) ||
          !_text(input['value'], 512) ||
          !_key(input['idempotencyKey'])) _invalid();
      if (input['action'] == 'start') {
        if (!_text(input['procedureId']) ||
            !RegExp(r'^[a-z][a-zA-Z0-9_.-]{0,127}$')
                .hasMatch(input['procedureId'] as String) ||
            input.containsKey('runId') ||
            input.containsKey('expectedUpdatedAt')) _invalid();
      } else if (!_reference(input['runId']) ||
          !(input['runId'] as String).startsWith('wr_') ||
          !_date(input['expectedUpdatedAt'])) {
        _invalid();
      }
      if (input.containsKey('locale') &&
          !['en', 'uk', 'de', 'es'].contains(input['locale'])) _invalid();
      for (final key in ['stepId', 'parameterId']) {
        if (input.containsKey(key) &&
            (input[key] is! String ||
                !RegExp(r'^[a-z][a-z0-9_]{0,63}$')
                    .hasMatch(input[key] as String))) _invalid();
      }
      if (input.containsKey('timeZone') && !_text(input['timeZone']))
        _invalid();
      if (input.containsKey('nativeCommerceUi') &&
          input['nativeCommerceUi'] != true) _invalid();
      break;
    case 'attachment-intents':
      _only(request, ['operation', 'messageId', 'attachments']);
      if (!_reference(request['messageId']) || request['attachments'] is! List)
        _invalid();
      final attachments = request['attachments'] as List;
      if (attachments.isEmpty || attachments.length > 5) _invalid();
      final clientIds = <String>{};
      for (final raw in attachments) {
        final file = _record(raw);
        _only(file, ['clientId', 'fileName', 'mimeType', 'sizeBytes', 'kind']);
        if (!_text(file['clientId'], 100) ||
            !_text(file['fileName'], 180) ||
            RegExp(r'[/\\]').hasMatch(file['fileName'] as String) ||
            !_text(file['mimeType']) ||
            file['sizeBytes'] is! int ||
            (file['sizeBytes'] as int) <= 0 ||
            (file['sizeBytes'] as int) > 10 * 1024 * 1024 ||
            !['image', 'video', 'pdf', 'text'].contains(file['kind']) ||
            !clientIds.add(file['clientId'] as String)) _invalid();
      }
      break;
    case 'upload-attachment':
      _only(request, ['operation', 'intent', 'fileUri', 'mimeType']);
      final intent = _record(request['intent']);
      _only(intent, [
        'clientId',
        'attachmentId',
        'uploadUrl',
        'uploadToken',
        'expiresAt'
      ]);
      if (!_reference(intent['attachmentId']) ||
          !_text(intent['clientId'], 100) ||
          !_text(intent['uploadToken'], 8192) ||
          !_date(intent['expiresAt']) ||
          !_text(request['mimeType']) ||
          intent['uploadUrl'] is! String ||
          request['fileUri'] is! String) _invalid();
      final upload = Uri.tryParse(intent['uploadUrl'] as String);
      final file = Uri.tryParse(request['fileUri'] as String);
      if (upload == null ||
          file == null ||
          !(upload.scheme == 'https' ||
              upload.scheme == 'http' && upload.host == 'localhost') ||
          upload.host.isEmpty ||
          upload.userInfo.isNotEmpty ||
          upload.hasFragment ||
          !upload.path.startsWith('/api/widget/v1/uploads/') ||
          !['file', 'content'].contains(file.scheme) ||
          file.userInfo.isNotEmpty ||
          file.hasQuery ||
          file.hasFragment ||
          file.scheme == 'file' &&
              file.host.isNotEmpty &&
              file.host != 'localhost') _invalid();
      break;
    case 'list-orders':
      _only(request, ['operation', 'query']);
      if (request.containsKey('query')) {
        final query = _record(request['query']);
        _only(query, ['cursor', 'store']);
        if (query.values.any((value) => !_reference(value))) _invalid();
      }
      break;
    case 'order-detail':
      _only(request, ['operation', 'orderRef']);
      if (!_reference(request['orderRef'])) _invalid();
      break;
    case 'create-commerce-proposal':
      _only(request, ['operation', 'input', 'idempotencyKey']);
      final input = _record(request['input']);
      _only(input, ['actionId', 'selection']);
      final selection = _record(input['selection']);
      _only(selection, ['storeReference', 'orderReference', 'lineItems']);
      if (!_key(request['idempotencyKey']) ||
          input['actionId'] != 'shopify_order_cancel' ||
          !_reference(selection['storeReference']) ||
          !_reference(selection['orderReference'])) _invalid();
      if (selection.containsKey('lineItems')) {
        final items = selection['lineItems'];
        if (items is! List || items.length > 100) _invalid();
        for (final raw in items) {
          final item = _record(raw);
          _only(item, ['lineItemReference', 'quantity']);
          if (!_reference(item['lineItemReference']) ||
              item['quantity'] is! int ||
              (item['quantity'] as int) < 1) _invalid();
        }
      }
      break;
    case 'confirm-commerce-proposal':
      _only(request, ['operation', 'proposalId', 'input', 'idempotencyKey']);
      final input = _record(request['input']);
      _only(input, ['confirmed']);
      if (!_reference(request['proposalId']) ||
          !_key(request['idempotencyKey']) ||
          input['confirmed'] != true) _invalid();
      break;
    case 'commerce-proposal':
      _only(request, ['operation', 'proposalId']);
      if (!_reference(request['proposalId'])) _invalid();
      break;
    case 'complete-host-action':
      _only(request, ['operation', 'requestId', 'actionId', 'result']);
      if (request['requestId'] is! String ||
          !RegExp(r'^[A-Za-z0-9][A-Za-z0-9:_-]{0,127}$')
              .hasMatch(request['requestId'] as String) ||
          request['actionId'] is! String ||
          !RegExp(r'^[a-z0-9][a-z0-9:_-]{0,63}$')
              .hasMatch(request['actionId'] as String)) _invalid();
      final result = _record(request['result']);
      _only(result, ['ok', 'status', 'data', 'code', 'retryable']);
      final status = result['status'];
      final ok = result['ok'];
      if (ok is! bool ||
          status is! String ||
          !['acknowledged', 'completed', 'failed'].contains(status) ||
          (status == 'failed') == ok) _invalid();
      if (result.containsKey('code') &&
          (result['code'] is! String ||
              !RegExp(r'^[a-z][a-z0-9_]{0,63}$')
                  .hasMatch(result['code'] as String))) _invalid();
      if (result.containsKey('retryable') && result['retryable'] is! bool)
        _invalid();
      if (utf8.encode(jsonEncode(request)).length > 16 * 1024) _invalid();
      break;
    default:
      _invalid();
  }
  final encoded = jsonEncode(request);
  if (utf8.encode(encoded).length > 65536) _invalid();
  return encoded;
}