runtime: gate premium protocol adapters by feature
This commit is contained in:
@@ -39,6 +39,7 @@ Verification:
|
|||||||
|
|
||||||
Progress:
|
Progress:
|
||||||
- done:
|
- done:
|
||||||
|
- public target repository `crank-community` now exists and can receive bootstrap templates once the remaining private repositories are created
|
||||||
- gate for creating `3` target repositories is already documented before any physical commercial split
|
- gate for creating `3` target repositories is already documented before any physical commercial split
|
||||||
- canonical Community deployment manifest and env template now live under `deploy/community/*`, and public deploy uses that manifest instead of the root compose file
|
- canonical Community deployment manifest and env template now live under `deploy/community/*`, and public deploy uses that manifest instead of the root compose file
|
||||||
- CI, README, runtime/deploy smoke docs now point to `deploy/community/*` as the Community delivery source of truth
|
- CI, README, runtime/deploy smoke docs now point to `deploy/community/*` as the Community delivery source of truth
|
||||||
@@ -46,8 +47,12 @@ Progress:
|
|||||||
- explicit repository split map now defines what goes to `crank-community`, `crank-enterprise`, and `crank-cloud`
|
- explicit repository split map now defines what goes to `crank-community`, `crank-enterprise`, and `crank-cloud`
|
||||||
- `Community` is now fixed as `REST-only` in product docs, capability model, backend validation, demo seed, and UI protocol expectations
|
- `Community` is now fixed as `REST-only` in product docs, capability model, backend validation, demo seed, and UI protocol expectations
|
||||||
- bootstrap templates now exist for `crank-community`, `crank-enterprise`, and `crank-cloud`, including initial README and workflow skeletons
|
- bootstrap templates now exist for `crank-community`, `crank-enterprise`, and `crank-cloud`, including initial README and workflow skeletons
|
||||||
|
- `crank-runtime` now has protocol feature seams, and the runtime crate compiles with `--no-default-features` as a `REST-only` base
|
||||||
- pending:
|
- pending:
|
||||||
- next management gate is to actually create the `3` target repositories and move these bootstrap templates into their roots before physical split starts
|
- next management gate is to create the remaining private target repositories:
|
||||||
|
- `crank-enterprise`
|
||||||
|
- `crank-cloud`
|
||||||
|
- after all `3` repositories exist, move the prepared bootstrap templates into their roots before physical split starts
|
||||||
|
|
||||||
## Planned
|
## Planned
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,27 @@ license.workspace = true
|
|||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = [
|
||||||
|
"protocol-graphql",
|
||||||
|
"protocol-grpc",
|
||||||
|
"protocol-soap",
|
||||||
|
"protocol-websocket",
|
||||||
|
]
|
||||||
|
protocol-graphql = ["dep:crank-adapter-graphql"]
|
||||||
|
protocol-grpc = ["dep:crank-adapter-grpc"]
|
||||||
|
protocol-soap = ["dep:crank-adapter-soap"]
|
||||||
|
protocol-websocket = ["dep:crank-adapter-websocket"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aes-gcm.workspace = true
|
aes-gcm.workspace = true
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
base64.workspace = true
|
base64.workspace = true
|
||||||
crank-adapter-graphql = { path = "../crank-adapter-graphql" }
|
crank-adapter-graphql = { path = "../crank-adapter-graphql", optional = true }
|
||||||
crank-adapter-grpc = { path = "../crank-adapter-grpc" }
|
crank-adapter-grpc = { path = "../crank-adapter-grpc", optional = true }
|
||||||
crank-adapter-rest = { path = "../crank-adapter-rest" }
|
crank-adapter-rest = { path = "../crank-adapter-rest" }
|
||||||
crank-adapter-soap = { path = "../crank-adapter-soap" }
|
crank-adapter-soap = { path = "../crank-adapter-soap", optional = true }
|
||||||
crank-adapter-websocket = { path = "../crank-adapter-websocket" }
|
crank-adapter-websocket = { path = "../crank-adapter-websocket", optional = true }
|
||||||
crank-core = { path = "../crank-core" }
|
crank-core = { path = "../crank-core" }
|
||||||
crank-mapping = { path = "../crank-mapping" }
|
crank-mapping = { path = "../crank-mapping" }
|
||||||
crank-schema = { path = "../crank-schema" }
|
crank-schema = { path = "../crank-schema" }
|
||||||
|
|||||||
@@ -1,29 +1,34 @@
|
|||||||
use crank_adapter_graphql::GraphqlAdapterError;
|
|
||||||
use crank_adapter_grpc::GrpcAdapterError;
|
|
||||||
use crank_adapter_rest::RestAdapterError;
|
use crank_adapter_rest::RestAdapterError;
|
||||||
use crank_adapter_soap::SoapAdapterError;
|
|
||||||
use crank_adapter_websocket::WebsocketAdapterError;
|
|
||||||
use crank_core::{ExecutionMode, Protocol};
|
use crank_core::{ExecutionMode, Protocol};
|
||||||
use crank_mapping::MappingError;
|
use crank_mapping::MappingError;
|
||||||
use crank_schema::SchemaError;
|
use crank_schema::SchemaError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[cfg(feature = "protocol-graphql")]
|
||||||
|
use crank_adapter_graphql::GraphqlAdapterError;
|
||||||
|
#[cfg(feature = "protocol-grpc")]
|
||||||
|
use crank_adapter_grpc::GrpcAdapterError;
|
||||||
|
#[cfg(feature = "protocol-soap")]
|
||||||
|
use crank_adapter_soap::SoapAdapterError;
|
||||||
|
#[cfg(feature = "protocol-websocket")]
|
||||||
|
use crank_adapter_websocket::WebsocketAdapterError;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum RuntimeError {
|
pub enum RuntimeError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Schema(#[from] SchemaError),
|
Schema(#[from] SchemaError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Mapping(#[from] MappingError),
|
Mapping(#[from] MappingError),
|
||||||
#[error(transparent)]
|
#[error("{0}")]
|
||||||
GraphqlAdapter(#[from] GraphqlAdapterError),
|
GraphqlAdapter(String),
|
||||||
#[error(transparent)]
|
#[error("{0}")]
|
||||||
GrpcAdapter(#[from] GrpcAdapterError),
|
GrpcAdapter(String),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
RestAdapter(#[from] RestAdapterError),
|
RestAdapter(#[from] RestAdapterError),
|
||||||
#[error(transparent)]
|
#[error("{0}")]
|
||||||
SoapAdapter(#[from] SoapAdapterError),
|
SoapAdapter(String),
|
||||||
#[error(transparent)]
|
#[error("{0}")]
|
||||||
WebsocketAdapter(#[from] WebsocketAdapterError),
|
WebsocketAdapter(String),
|
||||||
#[error("protocol {protocol:?} is not supported by runtime")]
|
#[error("protocol {protocol:?} is not supported by runtime")]
|
||||||
UnsupportedProtocol { protocol: Protocol },
|
UnsupportedProtocol { protocol: Protocol },
|
||||||
#[error("operation {operation_id} does not define streaming config")]
|
#[error("operation {operation_id} does not define streaming config")]
|
||||||
@@ -53,3 +58,31 @@ pub enum RuntimeError {
|
|||||||
details: String,
|
details: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "protocol-graphql")]
|
||||||
|
impl From<GraphqlAdapterError> for RuntimeError {
|
||||||
|
fn from(value: GraphqlAdapterError) -> Self {
|
||||||
|
Self::GraphqlAdapter(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "protocol-grpc")]
|
||||||
|
impl From<GrpcAdapterError> for RuntimeError {
|
||||||
|
fn from(value: GrpcAdapterError) -> Self {
|
||||||
|
Self::GrpcAdapter(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "protocol-soap")]
|
||||||
|
impl From<SoapAdapterError> for RuntimeError {
|
||||||
|
fn from(value: SoapAdapterError) -> Self {
|
||||||
|
Self::SoapAdapter(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "protocol-websocket")]
|
||||||
|
impl From<WebsocketAdapterError> for RuntimeError {
|
||||||
|
fn from(value: WebsocketAdapterError) -> Self {
|
||||||
|
Self::WebsocketAdapter(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,11 +3,7 @@ use std::sync::Arc;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
|
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
|
||||||
use crank_adapter_graphql::{GraphqlAdapter, GraphqlRequest};
|
|
||||||
use crank_adapter_grpc::{GrpcAdapter, GrpcRequest, GrpcWindowRequest};
|
|
||||||
use crank_adapter_rest::{RestAdapter, RestRequest, RestWindowRequest};
|
use crank_adapter_rest::{RestAdapter, RestRequest, RestWindowRequest};
|
||||||
use crank_adapter_soap::{SoapAdapter, SoapRequest};
|
|
||||||
use crank_adapter_websocket::{WebsocketAdapter, WebsocketWindowRequest};
|
|
||||||
use crank_core::{
|
use crank_core::{
|
||||||
CachedHeader, CachedResponse, ExecutionMode, HttpMethod, ResponseCacheStore, Target,
|
CachedHeader, CachedResponse, ExecutionMode, HttpMethod, ResponseCacheStore, Target,
|
||||||
TransportBehavior,
|
TransportBehavior,
|
||||||
@@ -22,12 +18,25 @@ use crate::{
|
|||||||
RuntimeRequestContext, WindowExecutionResult,
|
RuntimeRequestContext, WindowExecutionResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "protocol-graphql")]
|
||||||
|
use crank_adapter_graphql::{GraphqlAdapter, GraphqlRequest};
|
||||||
|
#[cfg(feature = "protocol-grpc")]
|
||||||
|
use crank_adapter_grpc::{GrpcAdapter, GrpcRequest, GrpcWindowRequest};
|
||||||
|
#[cfg(feature = "protocol-soap")]
|
||||||
|
use crank_adapter_soap::{SoapAdapter, SoapRequest};
|
||||||
|
#[cfg(feature = "protocol-websocket")]
|
||||||
|
use crank_adapter_websocket::{WebsocketAdapter, WebsocketWindowRequest};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RuntimeExecutor {
|
pub struct RuntimeExecutor {
|
||||||
|
#[cfg(feature = "protocol-graphql")]
|
||||||
graphql_adapter: GraphqlAdapter,
|
graphql_adapter: GraphqlAdapter,
|
||||||
|
#[cfg(feature = "protocol-grpc")]
|
||||||
grpc_adapter: GrpcAdapter,
|
grpc_adapter: GrpcAdapter,
|
||||||
rest_adapter: RestAdapter,
|
rest_adapter: RestAdapter,
|
||||||
|
#[cfg(feature = "protocol-soap")]
|
||||||
soap_adapter: SoapAdapter,
|
soap_adapter: SoapAdapter,
|
||||||
|
#[cfg(feature = "protocol-websocket")]
|
||||||
websocket_adapter: WebsocketAdapter,
|
websocket_adapter: WebsocketAdapter,
|
||||||
limits: RuntimeLimits,
|
limits: RuntimeLimits,
|
||||||
unary_limiter: Arc<Semaphore>,
|
unary_limiter: Arc<Semaphore>,
|
||||||
@@ -50,10 +59,14 @@ impl RuntimeExecutor {
|
|||||||
|
|
||||||
pub fn with_limits(limits: RuntimeLimits) -> Self {
|
pub fn with_limits(limits: RuntimeLimits) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
#[cfg(feature = "protocol-graphql")]
|
||||||
graphql_adapter: GraphqlAdapter::new(),
|
graphql_adapter: GraphqlAdapter::new(),
|
||||||
|
#[cfg(feature = "protocol-grpc")]
|
||||||
grpc_adapter: GrpcAdapter::new(),
|
grpc_adapter: GrpcAdapter::new(),
|
||||||
rest_adapter: RestAdapter::new(),
|
rest_adapter: RestAdapter::new(),
|
||||||
|
#[cfg(feature = "protocol-soap")]
|
||||||
soap_adapter: SoapAdapter::new(),
|
soap_adapter: SoapAdapter::new(),
|
||||||
|
#[cfg(feature = "protocol-websocket")]
|
||||||
websocket_adapter: WebsocketAdapter::new(),
|
websocket_adapter: WebsocketAdapter::new(),
|
||||||
unary_limiter: Arc::new(Semaphore::new(limits.max_concurrent_unary)),
|
unary_limiter: Arc::new(Semaphore::new(limits.max_concurrent_unary)),
|
||||||
window_limiter: Arc::new(Semaphore::new(limits.max_concurrent_window)),
|
window_limiter: Arc::new(Semaphore::new(limits.max_concurrent_window)),
|
||||||
@@ -312,6 +325,15 @@ impl RuntimeExecutor {
|
|||||||
let context_headers = runtime_context_headers(request_context);
|
let context_headers = runtime_context_headers(request_context);
|
||||||
match &operation.target {
|
match &operation.target {
|
||||||
Target::Grpc(target) => {
|
Target::Grpc(target) => {
|
||||||
|
#[cfg(not(feature = "protocol-grpc"))]
|
||||||
|
{
|
||||||
|
let _ = target;
|
||||||
|
return Err(RuntimeError::UnsupportedProtocol {
|
||||||
|
protocol: operation.protocol,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[cfg(feature = "protocol-grpc")]
|
||||||
|
{
|
||||||
let request = GrpcRequest {
|
let request = GrpcRequest {
|
||||||
headers: merge_headers(
|
headers: merge_headers(
|
||||||
&BTreeMap::new(),
|
&BTreeMap::new(),
|
||||||
@@ -334,7 +356,17 @@ impl RuntimeExecutor {
|
|||||||
data: response.body,
|
data: response.body,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Target::Graphql(target) => {
|
Target::Graphql(target) => {
|
||||||
|
#[cfg(not(feature = "protocol-graphql"))]
|
||||||
|
{
|
||||||
|
let _ = target;
|
||||||
|
return Err(RuntimeError::UnsupportedProtocol {
|
||||||
|
protocol: operation.protocol,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[cfg(feature = "protocol-graphql")]
|
||||||
|
{
|
||||||
let request = GraphqlRequest {
|
let request = GraphqlRequest {
|
||||||
headers: merge_headers(
|
headers: merge_headers(
|
||||||
&BTreeMap::new(),
|
&BTreeMap::new(),
|
||||||
@@ -354,6 +386,7 @@ impl RuntimeExecutor {
|
|||||||
data: response.data,
|
data: response.data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Target::Rest(target) => {
|
Target::Rest(target) => {
|
||||||
let request = RestRequest {
|
let request = RestRequest {
|
||||||
path_params: prepared_request.path_params.clone(),
|
path_params: prepared_request.path_params.clone(),
|
||||||
@@ -377,6 +410,15 @@ impl RuntimeExecutor {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Target::Soap(target) => {
|
Target::Soap(target) => {
|
||||||
|
#[cfg(not(feature = "protocol-soap"))]
|
||||||
|
{
|
||||||
|
let _ = target;
|
||||||
|
Err(RuntimeError::UnsupportedProtocol {
|
||||||
|
protocol: operation.protocol,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
#[cfg(feature = "protocol-soap")]
|
||||||
|
{
|
||||||
let request = SoapRequest {
|
let request = SoapRequest {
|
||||||
headers: merge_headers(
|
headers: merge_headers(
|
||||||
&BTreeMap::new(),
|
&BTreeMap::new(),
|
||||||
@@ -399,6 +441,7 @@ impl RuntimeExecutor {
|
|||||||
data: Value::Null,
|
data: Value::Null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Target::Websocket(_) => Err(RuntimeError::UnsupportedExecutionMode {
|
Target::Websocket(_) => Err(RuntimeError::UnsupportedExecutionMode {
|
||||||
operation_id: operation.operation_id.as_str().to_owned(),
|
operation_id: operation.operation_id.as_str().to_owned(),
|
||||||
mode: ExecutionMode::Unary,
|
mode: ExecutionMode::Unary,
|
||||||
@@ -449,6 +492,15 @@ impl RuntimeExecutor {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Target::Grpc(target) => {
|
Target::Grpc(target) => {
|
||||||
|
#[cfg(not(feature = "protocol-grpc"))]
|
||||||
|
{
|
||||||
|
let _ = target;
|
||||||
|
return Err(RuntimeError::UnsupportedProtocol {
|
||||||
|
protocol: operation.protocol,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[cfg(feature = "protocol-grpc")]
|
||||||
|
{
|
||||||
let Some(streaming) = operation.execution_config.streaming.as_ref() else {
|
let Some(streaming) = operation.execution_config.streaming.as_ref() else {
|
||||||
return Err(RuntimeError::MissingStreamingConfig {
|
return Err(RuntimeError::MissingStreamingConfig {
|
||||||
operation_id: operation.operation_id.as_str().to_owned(),
|
operation_id: operation.operation_id.as_str().to_owned(),
|
||||||
@@ -482,7 +534,17 @@ impl RuntimeExecutor {
|
|||||||
data: Value::Null,
|
data: Value::Null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Target::Websocket(target) => {
|
Target::Websocket(target) => {
|
||||||
|
#[cfg(not(feature = "protocol-websocket"))]
|
||||||
|
{
|
||||||
|
let _ = target;
|
||||||
|
Err(RuntimeError::UnsupportedProtocol {
|
||||||
|
protocol: operation.protocol,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
#[cfg(feature = "protocol-websocket")]
|
||||||
|
{
|
||||||
let Some(streaming) = operation.execution_config.streaming.as_ref() else {
|
let Some(streaming) = operation.execution_config.streaming.as_ref() else {
|
||||||
return Err(RuntimeError::MissingStreamingConfig {
|
return Err(RuntimeError::MissingStreamingConfig {
|
||||||
operation_id: operation.operation_id.as_str().to_owned(),
|
operation_id: operation.operation_id.as_str().to_owned(),
|
||||||
@@ -523,6 +585,7 @@ impl RuntimeExecutor {
|
|||||||
data: Value::Null,
|
data: Value::Null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Target::Soap(_) => Err(RuntimeError::UnsupportedExecutionMode {
|
Target::Soap(_) => Err(RuntimeError::UnsupportedExecutionMode {
|
||||||
operation_id: operation.operation_id.as_str().to_owned(),
|
operation_id: operation.operation_id.as_str().to_owned(),
|
||||||
mode: ExecutionMode::Window,
|
mode: ExecutionMode::Window,
|
||||||
|
|||||||
@@ -79,6 +79,7 @@
|
|||||||
- создание `crank-community`, `crank-enterprise` и `crank-cloud` должно быть отдельным осознанным шагом;
|
- создание `crank-community`, `crank-enterprise` и `crank-cloud` должно быть отдельным осознанным шагом;
|
||||||
- до этого момента в текущем репозитории нужно завершить capability model, extension seams и public contracts;
|
- до этого момента в текущем репозитории нужно завершить capability model, extension seams и public contracts;
|
||||||
- физическое вынесение private code нельзя начинать раньше, чем эти три целевых repositories созданы и для них определены delivery boundaries.
|
- физическое вынесение private code нельзя начинать раньше, чем эти три целевых repositories созданы и для них определены delivery boundaries.
|
||||||
|
- на текущий момент public repository `crank-community` уже создан; следующий обязательный внешний шаг — создание private repositories `crank-enterprise` и `crank-cloud`.
|
||||||
|
|
||||||
## 6. Техническая стратегия разделения
|
## 6. Техническая стратегия разделения
|
||||||
|
|
||||||
@@ -109,6 +110,12 @@
|
|||||||
- cache store abstraction;
|
- cache store abstraction;
|
||||||
- feature availability checks.
|
- feature availability checks.
|
||||||
|
|
||||||
|
Дополнительно для runtime layer:
|
||||||
|
|
||||||
|
- premium protocol adapters должны подключаться как feature-gated seams;
|
||||||
|
- `REST`-база должна собираться без `GraphQL/gRPC/SOAP/WebSocket` crates;
|
||||||
|
- `UnsupportedProtocol` должен оставаться публичным fallback contract для disabled protocol paths.
|
||||||
|
|
||||||
Private code должен подключаться как реализация этих контрактов, а не как условные ветки по всему коду Community.
|
Private code должен подключаться как реализация этих контрактов, а не как условные ветки по всему коду Community.
|
||||||
|
|
||||||
### 6.3. Server-side enforcement
|
### 6.3. Server-side enforcement
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
- старые review-файлы не требуются как отдельный источник истины.
|
- старые review-файлы не требуются как отдельный источник истины.
|
||||||
- canonical Community deployment manifest и env template вынесены в `deploy/community/*`;
|
- canonical Community deployment manifest и env template вынесены в `deploy/community/*`;
|
||||||
- public CI/CD delivery path использует именно `deploy/community/*` как source of truth для Community поставки.
|
- public CI/CD delivery path использует именно `deploy/community/*` как source of truth для Community поставки.
|
||||||
|
- `crank-runtime` уже имеет feature seams для premium protocol adapters, так что Community base можно собирать как `REST-only` runtime foundation.
|
||||||
|
|
||||||
## 4. Этап 2. Community machine access completion
|
## 4. Этап 2. Community machine access completion
|
||||||
|
|
||||||
@@ -144,6 +145,13 @@
|
|||||||
- `crank-enterprise`
|
- `crank-enterprise`
|
||||||
- `crank-cloud`
|
- `crank-cloud`
|
||||||
|
|
||||||
|
Текущий статус:
|
||||||
|
|
||||||
|
- `crank-community` уже создан;
|
||||||
|
- до начала physical split остаются private repositories:
|
||||||
|
- `crank-enterprise`
|
||||||
|
- `crank-cloud`
|
||||||
|
|
||||||
Bootstrap templates для этого шага уже должны быть подготовлены заранее в текущем репозитории:
|
Bootstrap templates для этого шага уже должны быть подготовлены заранее в текущем репозитории:
|
||||||
|
|
||||||
- `templates/repositories/crank-community/*`
|
- `templates/repositories/crank-community/*`
|
||||||
|
|||||||
@@ -119,6 +119,11 @@
|
|||||||
- запись invocation events;
|
- запись invocation events;
|
||||||
- возврат нормализованного результата.
|
- возврат нормализованного результата.
|
||||||
|
|
||||||
|
Дополнительное правило для split:
|
||||||
|
|
||||||
|
- `crank-runtime` должен собираться как `REST-only` base даже без premium adapter crates;
|
||||||
|
- premium protocol adapters подключаются через feature seams, а не как безусловная зависимость Community runtime.
|
||||||
|
|
||||||
### 4.7. Protocol adapters
|
### 4.7. Protocol adapters
|
||||||
|
|
||||||
- `crank-adapter-rest`
|
- `crank-adapter-rest`
|
||||||
@@ -129,6 +134,12 @@
|
|||||||
|
|
||||||
Каждый adapter знает только свой протокол.
|
Каждый adapter знает только свой протокол.
|
||||||
|
|
||||||
|
Для open-core split это означает:
|
||||||
|
|
||||||
|
- `crank-adapter-rest` остается обязательной частью Community runtime;
|
||||||
|
- `crank-adapter-graphql`, `crank-adapter-grpc`, `crank-adapter-websocket`, `crank-adapter-soap`
|
||||||
|
должны подключаться как отделяемые protocol modules.
|
||||||
|
|
||||||
### 4.8. `apps/admin-api`
|
### 4.8. `apps/admin-api`
|
||||||
|
|
||||||
Должен содержать сервисные группы:
|
Должен содержать сервисные группы:
|
||||||
|
|||||||
@@ -50,6 +50,11 @@ Public repository.
|
|||||||
- public CI/CD for Community;
|
- public CI/CD for Community;
|
||||||
- Community docs, smoke docs и demo flow.
|
- Community docs, smoke docs и demo flow.
|
||||||
|
|
||||||
|
Техническая оговорка:
|
||||||
|
|
||||||
|
- `crank-runtime` в Community должен уметь собираться без premium adapter crates;
|
||||||
|
- `UnsupportedProtocol` и capability model должны оставаться достаточным fallback для disabled protocol families.
|
||||||
|
|
||||||
В нем не должно остаться:
|
В нем не должно остаться:
|
||||||
|
|
||||||
- private token issuer implementations;
|
- private token issuer implementations;
|
||||||
@@ -106,6 +111,11 @@ Private repository.
|
|||||||
- все capability checks
|
- все capability checks
|
||||||
- `REST`-only product surface
|
- `REST`-only product surface
|
||||||
|
|
||||||
|
Как промежуточный этап до физического переноса:
|
||||||
|
|
||||||
|
- `crank-runtime` в общем коде должен иметь feature-gated seams для `GraphQL/gRPC/SOAP/WebSocket`;
|
||||||
|
- это позволяет потом перенести premium adapters в private repos без полного форка runtime base.
|
||||||
|
|
||||||
Уходит в `crank-enterprise`:
|
Уходит в `crank-enterprise`:
|
||||||
|
|
||||||
- `crank-adapter-graphql`
|
- `crank-adapter-graphql`
|
||||||
@@ -195,6 +205,13 @@ Private repository.
|
|||||||
3. Зафиксировать отдельные package/release names.
|
3. Зафиксировать отдельные package/release names.
|
||||||
4. Подготовить начальные README и baseline workflows для всех трех репозиториев.
|
4. Подготовить начальные README и baseline workflows для всех трех репозиториев.
|
||||||
|
|
||||||
|
Текущий статус:
|
||||||
|
|
||||||
|
- `crank-community` уже создан;
|
||||||
|
- до старта physical split остаются внешние шаги:
|
||||||
|
- создать `crank-enterprise`
|
||||||
|
- создать `crank-cloud`
|
||||||
|
|
||||||
Пока эти четыре пункта не выполнены, physical split не начинается.
|
Пока эти четыре пункта не выполнены, physical split не начинается.
|
||||||
|
|
||||||
Техническая заготовка для этого шага уже живет в:
|
Техническая заготовка для этого шага уже живет в:
|
||||||
@@ -207,7 +224,8 @@ Private repository.
|
|||||||
|
|
||||||
Следующий управленческий шаг после завершения текущего boundary-трека:
|
Следующий управленческий шаг после завершения текущего boundary-трека:
|
||||||
|
|
||||||
- создать `crank-community`
|
- создать `crank-enterprise`
|
||||||
|
- создать `crank-cloud`
|
||||||
- создать `crank-enterprise`
|
- создать `crank-enterprise`
|
||||||
- создать `crank-cloud`
|
- создать `crank-cloud`
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user