admit method
CockpitOperationDescriptor
admit(
- CockpitOperationInvocation invocation, {
- 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;
}