admit method

CockpitOperationDescriptor admit(
  1. CockpitOperationInvocation invocation, {
  2. required Iterable<String> negotiatedFeatureIds,
})

Implementation

CockpitOperationDescriptor admit(
  CockpitOperationInvocation invocation, {
  required Iterable<String> negotiatedFeatureIds,
}) {
  final contract = _contracts[invocation.kind];
  if (contract == null) {
    throw _admissionError(
      code: CockpitErrorCode.unsupportedOperation,
      category: CockpitErrorCategory.unsupported,
      message: 'Unknown operation kind ${invocation.kind}.',
    );
  }
  final descriptor = contract.descriptor;
  late final CockpitOperationAdmissionProjection projection;
  try {
    projection = contract.decodeAdmission(invocation.input);
  } on FormatException {
    throw _inputAdmissionError(descriptor);
  }
  final scopeValid = switch (descriptor.scope) {
    CockpitOperationScope.supervisor =>
      invocation.rootId == null && invocation.workspaceId == null,
    CockpitOperationScope.root =>
      invocation.rootId != null && invocation.workspaceId == null,
    CockpitOperationScope.workspace =>
      invocation.rootId == null && invocation.workspaceId != null,
  };
  if (!scopeValid) {
    throw _admissionError(
      code: CockpitErrorCode.invalidRequest,
      category: CockpitErrorCategory.invalidInput,
      message: 'Operation scope identifiers are inconsistent.',
    );
  }
  _requireProjectionMatch(
    descriptor: descriptor,
    field: 'rootId',
    projected: projection.rootId,
    envelope: invocation.rootId,
  );
  _requireProjectionMatch(
    descriptor: descriptor,
    field: 'workspaceId',
    projected: projection.workspaceId,
    envelope: invocation.workspaceId,
  );
  _requireProjectionMatch(
    descriptor: descriptor,
    field: 'idempotencyKey',
    projected: projection.idempotencyKey,
    envelope: invocation.idempotencyKey,
  );
  final hasKey = invocation.idempotencyKey != null;
  if ((descriptor.idempotency == CockpitIdempotencyBehavior.required &&
          !hasKey) ||
      (descriptor.idempotency == CockpitIdempotencyBehavior.prohibited &&
          hasKey)) {
    throw _admissionError(
      code: CockpitErrorCode.invalidRequest,
      category: CockpitErrorCategory.invalidInput,
      message: 'Operation idempotency key does not match its descriptor.',
    );
  }
  final features = negotiatedFeatureIds.toSet();
  final missing = <String>{
    ...descriptor.requiredFeatures,
    ...invocation.requiredFeatures,
    ...projection.requiredFeatures,
  }.where((feature) => !features.contains(feature)).toList(growable: false);
  if (missing.isNotEmpty ||
      descriptor.safetyEffects.any((effect) => !effect.isKnown)) {
    throw _admissionError(
      code: CockpitErrorCode.upgradeRequired,
      category: CockpitErrorCategory.unsupported,
      message: missing.isNotEmpty
          ? 'Operation requires unavailable negotiated features.'
          : 'Operation declares an unsupported safety effect.',
      details: <String, Object?>{
        if (missing.isNotEmpty) 'missingFeatures': missing,
        if (descriptor.safetyEffects.any((effect) => !effect.isKnown))
          'unsupportedSafetyEffects': descriptor.safetyEffects
              .where((effect) => !effect.isKnown)
              .map((effect) => effect.wireValue)
              .toList(),
      },
    );
  }
  return descriptor;
}