Current section
Files
Jump to
Current section
Files
src/lightspeed/integration/adapter_sdk.gleam
//// Ecosystem adapter SDK contracts and reference adapters for M41.
import gleam/int
import gleam/list
/// Stable SDK semantic version.
pub type Semver {
Semver(major: Int, minor: Int, patch: Int)
}
/// Adapter boundary class.
pub type Boundary {
DatabaseSource
ObjectStoreSource
QueueSource
BillingService
IdentityService
}
/// Adapter capability contract.
pub type Capability {
PullBatch
PushBatch
AckCheckpoint
ReplayCheckpoint
IdempotentWrites
WebhookVerification
TenantScopedAuth
HealthProbe
}
/// Verification step for developer/operator integration checks.
pub type VerificationStep {
VerificationStep(id: String, instruction: String, expected_evidence: String)
}
/// One adapter contract.
pub type AdapterContract {
AdapterContract(
name: String,
boundary: Boundary,
provider_module: String,
sdk_version: Semver,
adapter_version: Semver,
capabilities: List(Capability),
verification_steps: List(VerificationStep),
support_tier: String,
migration_guide: String,
)
}
/// Compatibility guarantees for adapter boundaries.
pub type CompatibilityPolicy {
CompatibilityPolicy(
sdk_major: Int,
adapter_major: Int,
min_adapter_minor: Int,
max_adapter_minor: Int,
deprecation_window_days: Int,
)
}
/// Current stable SDK version.
pub fn sdk_version() -> Semver {
Semver(major: 1, minor: 0, patch: 0)
}
/// Current adapter compatibility policy.
pub fn compatibility_policy() -> CompatibilityPolicy {
CompatibilityPolicy(
sdk_major: 1,
adapter_major: 1,
min_adapter_minor: 0,
max_adapter_minor: 4,
deprecation_window_days: 180,
)
}
/// Reference adapter: Postgres CDC source.
pub fn postgres_cdc_source_adapter() -> AdapterContract {
AdapterContract(
name: "postgres_cdc_source",
boundary: DatabaseSource,
provider_module: "Lightspeed.Adapter.PostgresCdc",
sdk_version: sdk_version(),
adapter_version: Semver(major: 1, minor: 2, patch: 0),
capabilities: [
PullBatch,
AckCheckpoint,
ReplayCheckpoint,
TenantScopedAuth,
HealthProbe,
],
verification_steps: default_verification_steps("postgres_cdc_source"),
support_tier: "ga",
migration_guide: "docs/ecosystem_adapter_sdk.md#postgres-cdc-source",
)
}
/// Reference adapter: object-store batch source.
pub fn object_store_source_adapter() -> AdapterContract {
AdapterContract(
name: "object_store_source",
boundary: ObjectStoreSource,
provider_module: "Lightspeed.Adapter.ObjectStoreSource",
sdk_version: sdk_version(),
adapter_version: Semver(major: 1, minor: 1, patch: 3),
capabilities: [
PullBatch,
AckCheckpoint,
ReplayCheckpoint,
TenantScopedAuth,
HealthProbe,
],
verification_steps: default_verification_steps("object_store_source"),
support_tier: "ga",
migration_guide: "docs/ecosystem_adapter_sdk.md#object-store-source",
)
}
/// Reference adapter: queue source.
pub fn queue_source_adapter() -> AdapterContract {
AdapterContract(
name: "queue_source",
boundary: QueueSource,
provider_module: "Lightspeed.Adapter.QueueSource",
sdk_version: sdk_version(),
adapter_version: Semver(major: 1, minor: 3, patch: 1),
capabilities: [
PullBatch,
PushBatch,
AckCheckpoint,
ReplayCheckpoint,
TenantScopedAuth,
HealthProbe,
],
verification_steps: default_verification_steps("queue_source"),
support_tier: "ga",
migration_guide: "docs/ecosystem_adapter_sdk.md#queue-source",
)
}
/// Reference adapter: billing service.
pub fn billing_service_adapter() -> AdapterContract {
AdapterContract(
name: "billing_service",
boundary: BillingService,
provider_module: "Lightspeed.Adapter.BillingService",
sdk_version: sdk_version(),
adapter_version: Semver(major: 1, minor: 0, patch: 5),
capabilities: [
PushBatch,
IdempotentWrites,
WebhookVerification,
TenantScopedAuth,
HealthProbe,
],
verification_steps: default_verification_steps("billing_service"),
support_tier: "ga",
migration_guide: "docs/ecosystem_adapter_sdk.md#billing-service",
)
}
/// Reference adapter: identity provider.
pub fn identity_service_adapter() -> AdapterContract {
AdapterContract(
name: "identity_service",
boundary: IdentityService,
provider_module: "Lightspeed.Adapter.IdentityService",
sdk_version: sdk_version(),
adapter_version: Semver(major: 1, minor: 4, patch: 0),
capabilities: [
PullBatch,
PushBatch,
IdempotentWrites,
TenantScopedAuth,
HealthProbe,
],
verification_steps: default_verification_steps("identity_service"),
support_tier: "ga",
migration_guide: "docs/ecosystem_adapter_sdk.md#identity-service",
)
}
/// Reference M41 adapters.
pub fn reference_adapters() -> List(AdapterContract) {
[
postgres_cdc_source_adapter(),
object_store_source_adapter(),
queue_source_adapter(),
billing_service_adapter(),
identity_service_adapter(),
]
}
/// Validate one adapter contract.
pub fn valid(adapter: AdapterContract) -> Bool {
adapter.name != ""
&& adapter.provider_module != ""
&& adapter.support_tier == "ga"
&& adapter.migration_guide != ""
&& semver_valid(adapter.sdk_version)
&& semver_valid(adapter.adapter_version)
&& list.length(adapter.capabilities) >= 4
&& capabilities_non_empty(adapter.capabilities)
&& requires_health_probe(adapter.capabilities)
&& requires_tenant_scoped_auth(adapter.capabilities)
&& verification_steps_valid(adapter.verification_steps)
}
/// Validate adapter compatibility against one policy.
pub fn compatible(
adapter: AdapterContract,
policy: CompatibilityPolicy,
) -> Bool {
adapter.sdk_version.major == policy.sdk_major
&& adapter.adapter_version.major == policy.adapter_major
&& adapter.adapter_version.minor >= policy.min_adapter_minor
&& adapter.adapter_version.minor <= policy.max_adapter_minor
&& policy.deprecation_window_days >= 90
}
/// Validate all reference adapters against one policy.
pub fn reference_adapters_compatible(policy: CompatibilityPolicy) -> Bool {
reference_adapters()
|> all_adapters_valid_and_compatible(policy)
}
/// Stable semver label.
pub fn semver_label(version: Semver) -> String {
int.to_string(version.major)
<> "."
<> int.to_string(version.minor)
<> "."
<> int.to_string(version.patch)
}
/// Stable boundary label.
pub fn boundary_label(boundary: Boundary) -> String {
case boundary {
DatabaseSource -> "database_source"
ObjectStoreSource -> "object_store_source"
QueueSource -> "queue_source"
BillingService -> "billing_service"
IdentityService -> "identity_service"
}
}
/// Stable capability label.
pub fn capability_label(capability: Capability) -> String {
case capability {
PullBatch -> "pull_batch"
PushBatch -> "push_batch"
AckCheckpoint -> "ack_checkpoint"
ReplayCheckpoint -> "replay_checkpoint"
IdempotentWrites -> "idempotent_writes"
WebhookVerification -> "webhook_verification"
TenantScopedAuth -> "tenant_scoped_auth"
HealthProbe -> "health_probe"
}
}
/// Stable adapter signature.
pub fn adapter_signature(adapter: AdapterContract) -> String {
"adapter:"
<> adapter.name
<> "|boundary="
<> boundary_label(adapter.boundary)
<> "|provider="
<> adapter.provider_module
<> "|sdk="
<> semver_label(adapter.sdk_version)
<> "|adapter_version="
<> semver_label(adapter.adapter_version)
<> "|capabilities="
<> join_with(",", list.map(adapter.capabilities, capability_label))
<> "|support_tier="
<> adapter.support_tier
<> "|verification="
<> verification_signature(adapter.verification_steps)
}
/// Stable compatibility policy label.
pub fn compatibility_policy_label(policy: CompatibilityPolicy) -> String {
"sdk_major="
<> int.to_string(policy.sdk_major)
<> "|adapter_major="
<> int.to_string(policy.adapter_major)
<> "|min_minor="
<> int.to_string(policy.min_adapter_minor)
<> "|max_minor="
<> int.to_string(policy.max_adapter_minor)
<> "|deprecation_window_days="
<> int.to_string(policy.deprecation_window_days)
}
/// Stable compatibility signature across all reference adapters.
pub fn compatibility_signature(policy: CompatibilityPolicy) -> String {
let entries =
reference_adapters()
|> list.map(fn(adapter) {
adapter.name <> ":compatible=" <> bool_label(compatible(adapter, policy))
})
compatibility_policy_label(policy) <> "|matrix=" <> join_with(",", entries)
}
/// Stable verification signature.
pub fn verification_signature(steps: List(VerificationStep)) -> String {
join_with(";", list.map(steps, verification_step_signature))
}
/// Stable verification step signature.
pub fn verification_step_signature(step: VerificationStep) -> String {
step.id <> ":evidence=" <> step.expected_evidence
}
/// Adapter name accessor.
pub fn name(adapter: AdapterContract) -> String {
adapter.name
}
/// Adapter boundary accessor.
pub fn boundary(adapter: AdapterContract) -> Boundary {
adapter.boundary
}
/// Adapter capability accessor.
pub fn capabilities(adapter: AdapterContract) -> List(Capability) {
adapter.capabilities
}
/// Adapter verification steps accessor.
pub fn verification_steps(adapter: AdapterContract) -> List(VerificationStep) {
adapter.verification_steps
}
fn default_verification_steps(prefix: String) -> List(VerificationStep) {
[
VerificationStep(
id: prefix <> "_contract_handshake",
instruction: "Verify adapter handshake against SDK contract schema.",
expected_evidence: "evidence." <> prefix <> ".contract_handshake",
),
VerificationStep(
id: prefix <> "_version_compatibility",
instruction: "Verify adapter version within supported policy window.",
expected_evidence: "evidence." <> prefix <> ".version_compatibility",
),
VerificationStep(
id: prefix <> "_round_trip_replay",
instruction: "Run deterministic round-trip and replay verification.",
expected_evidence: "evidence." <> prefix <> ".round_trip_replay",
),
VerificationStep(
id: prefix <> "_operator_handoff",
instruction: "Verify operator handoff runbook and escalation hooks.",
expected_evidence: "evidence." <> prefix <> ".operator_handoff",
),
]
}
fn semver_valid(version: Semver) -> Bool {
version.major >= 0 && version.minor >= 0 && version.patch >= 0
}
fn capabilities_non_empty(capabilities: List(Capability)) -> Bool {
case capabilities {
[] -> False
[_, ..] -> True
}
}
fn requires_health_probe(capabilities: List(Capability)) -> Bool {
contains_capability(capabilities, HealthProbe)
}
fn requires_tenant_scoped_auth(capabilities: List(Capability)) -> Bool {
contains_capability(capabilities, TenantScopedAuth)
}
fn contains_capability(
capabilities: List(Capability),
expected: Capability,
) -> Bool {
case capabilities {
[] -> False
[capability, ..rest] ->
capability == expected || contains_capability(rest, expected)
}
}
fn verification_steps_valid(steps: List(VerificationStep)) -> Bool {
list.length(steps) >= 4 && verification_steps_entries_valid(steps)
}
fn verification_steps_entries_valid(steps: List(VerificationStep)) -> Bool {
case steps {
[] -> True
[step, ..rest] ->
step.id != ""
&& step.instruction != ""
&& step.expected_evidence != ""
&& verification_steps_entries_valid(rest)
}
}
fn all_adapters_valid_and_compatible(
adapters: List(AdapterContract),
policy: CompatibilityPolicy,
) -> Bool {
case adapters {
[] -> True
[adapter, ..rest] ->
valid(adapter)
&& compatible(adapter, policy)
&& all_adapters_valid_and_compatible(rest, policy)
}
}
fn join_with(separator: String, values: List(String)) -> String {
case values {
[] -> ""
[value] -> value
[value, ..rest] -> value <> separator <> join_with(separator, rest)
}
}
fn bool_label(value: Bool) -> String {
case value {
True -> "true"
False -> "false"
}
}