v0.1.0-pre.011
This commit is contained in:
@@ -3,11 +3,11 @@
|
||||
|
||||
//! Instruction-level staking projections for native Stake and Vote observations.
|
||||
|
||||
const ACCEPTED_FAMILIES: &[crate::EventFamily] = &[
|
||||
crate::EventFamily::Lifecycle,
|
||||
crate::EventFamily::Admin,
|
||||
crate::EventFamily::Staking,
|
||||
crate::EventFamily::Reward,
|
||||
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
|
||||
crate::MdEventFamily::Lifecycle,
|
||||
crate::MdEventFamily::Admin,
|
||||
crate::MdEventFamily::Staking,
|
||||
crate::MdEventFamily::Reward,
|
||||
];
|
||||
const STAKE_SURFACE: &str = "solana_native_stake";
|
||||
const VOTE_SURFACE: &str = "solana_native_vote";
|
||||
@@ -82,10 +82,10 @@ impl ProjectionKind {
|
||||
};
|
||||
}
|
||||
|
||||
fn family(self) -> crate::MaterializedEventFamily {
|
||||
fn family(self) -> crate::MdMaterializedEventFamily {
|
||||
return match self {
|
||||
Self::VoteReward => crate::MaterializedEventFamily::Reward,
|
||||
_ => crate::MaterializedEventFamily::Staking,
|
||||
Self::VoteReward => crate::MdMaterializedEventFamily::Reward,
|
||||
_ => crate::MdMaterializedEventFamily::Staking,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -96,9 +96,9 @@ impl ProjectionKind {
|
||||
|
||||
/// Stable staking materializer for committed native Stake and Vote observations.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct StakingMaterializer;
|
||||
pub struct MtStakingMaterializer;
|
||||
|
||||
impl crate::Materializer for crate::StakingMaterializer {
|
||||
impl crate::MtMaterializer for crate::MtStakingMaterializer {
|
||||
fn materializer_name(&self) -> &'static str {
|
||||
return "kb_materializer_staking";
|
||||
}
|
||||
@@ -107,35 +107,35 @@ impl crate::Materializer for crate::StakingMaterializer {
|
||||
return env!("CARGO_PKG_VERSION");
|
||||
}
|
||||
|
||||
fn accepts_event(&self, event: &crate::DecodedProtocolEvent) -> bool {
|
||||
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
|
||||
return family_is_accepted(event.event_family)
|
||||
&& projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some();
|
||||
}
|
||||
|
||||
fn materialize_event(
|
||||
&self,
|
||||
event: &crate::DecodedProtocolEvent,
|
||||
) -> kb_core::Result<std::vec::Vec<crate::MaterializedEvent>> {
|
||||
if !crate::Materializer::accepts_event(self, event) {
|
||||
event: &crate::MdDecodedProtocolEvent,
|
||||
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
|
||||
if !crate::MtMaterializer::accepts_event(self, event) {
|
||||
return std::result::Result::Ok(std::vec::Vec::new());
|
||||
}
|
||||
return std::result::Result::Ok(std::vec::Vec::new());
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::EventMaterializer for crate::StakingMaterializer {
|
||||
fn identity(&self) -> crate::MaterializerIdentity {
|
||||
return crate::MaterializerIdentity {
|
||||
impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
|
||||
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||
return crate::MtApiMaterializerIdentity {
|
||||
name: "solana_native_staking".to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
fn accepted_families(&self) -> &'static [crate::EventFamily] {
|
||||
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||
return ACCEPTED_FAMILIES;
|
||||
}
|
||||
|
||||
fn accepts_observation(&self, observation: &crate::DecodedObservation) -> bool {
|
||||
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
||||
return family_is_accepted(observation.event.event_family)
|
||||
&& projection(
|
||||
observation.event.surface_code.0.as_str(),
|
||||
@@ -146,15 +146,15 @@ impl crate::EventMaterializer for crate::StakingMaterializer {
|
||||
|
||||
fn transaction_policy(
|
||||
&self,
|
||||
_family: crate::EventFamily,
|
||||
) -> crate::MaterializationTransactionPolicy {
|
||||
return crate::MaterializationTransactionPolicy::SuccessfulCommittedOnly;
|
||||
_family: crate::MdEventFamily,
|
||||
) -> crate::MtApiMaterializationTransactionPolicy {
|
||||
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
|
||||
}
|
||||
|
||||
fn materialize(
|
||||
&self,
|
||||
observation: &crate::DecodedObservation,
|
||||
) -> crate::MaterializerExecutionResult {
|
||||
observation: &crate::DcApiDecodedObservation,
|
||||
) -> crate::MtApiMaterializerExecutionResult {
|
||||
let projection = match projection(
|
||||
observation.event.surface_code.0.as_str(),
|
||||
observation.event.event_name.0.as_str(),
|
||||
@@ -162,22 +162,22 @@ impl crate::EventMaterializer for crate::StakingMaterializer {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
tracing::debug!(
|
||||
target: crate::MATERIALIZER_STAKING_TRACING_TARGET,
|
||||
target: crate::MT_STAKING_TRACING_TARGET,
|
||||
action = "materialize_staking",
|
||||
surface_code = %observation.event.surface_code.0.as_str(),
|
||||
entry_code = %observation.event.event_name.0.as_str(),
|
||||
accepted = false,
|
||||
"ignore observation outside supported native staking projections"
|
||||
);
|
||||
return crate::MaterializerExecutionResult::ignored();
|
||||
return crate::MtApiMaterializerExecutionResult::ignored();
|
||||
},
|
||||
};
|
||||
if !family_is_accepted(observation.event.event_family) {
|
||||
return crate::MaterializerExecutionResult::ignored();
|
||||
return crate::MtApiMaterializerExecutionResult::ignored();
|
||||
}
|
||||
if observation.transaction_failed || !observation.observation_committed {
|
||||
tracing::debug!(
|
||||
target: crate::MATERIALIZER_STAKING_TRACING_TARGET,
|
||||
target: crate::MT_STAKING_TRACING_TARGET,
|
||||
action = "materialize_staking",
|
||||
surface_code = %observation.event.surface_code.0.as_str(),
|
||||
entry_code = %observation.event.event_name.0.as_str(),
|
||||
@@ -185,7 +185,7 @@ impl crate::EventMaterializer for crate::StakingMaterializer {
|
||||
transaction_failed = observation.transaction_failed,
|
||||
"refuse uncommitted native staking observation"
|
||||
);
|
||||
return crate::MaterializerExecutionResult::refused(
|
||||
return crate::MtApiMaterializerExecutionResult::refused(
|
||||
"failed_transaction_staking_refused",
|
||||
"failed or uncommitted native Stake/Vote observations cannot create staking outputs",
|
||||
);
|
||||
@@ -201,7 +201,7 @@ impl crate::EventMaterializer for crate::StakingMaterializer {
|
||||
.cloned()
|
||||
.unwrap_or(serde_json::Value::Null);
|
||||
let operation = observation.event.event_name.0.clone();
|
||||
let output = crate::MaterializedOutput {
|
||||
let output = crate::MtApiMaterializedOutput {
|
||||
output_key: projection.output_key(operation.as_str()),
|
||||
family: projection.family(),
|
||||
payload_json: serde_json::json!({
|
||||
@@ -225,7 +225,7 @@ impl crate::EventMaterializer for crate::StakingMaterializer {
|
||||
}),
|
||||
};
|
||||
tracing::debug!(
|
||||
target: crate::MATERIALIZER_STAKING_TRACING_TARGET,
|
||||
target: crate::MT_STAKING_TRACING_TARGET,
|
||||
action = "materialize_staking",
|
||||
surface_code = %observation.event.surface_code.0.as_str(),
|
||||
entry_code = %observation.event.event_name.0.as_str(),
|
||||
@@ -233,15 +233,15 @@ impl crate::EventMaterializer for crate::StakingMaterializer {
|
||||
output_count = 1_usize,
|
||||
"materialize committed native staking observation"
|
||||
);
|
||||
return crate::MaterializerExecutionResult {
|
||||
status: crate::MaterializerOutcomeStatus::Inserted,
|
||||
return crate::MtApiMaterializerExecutionResult {
|
||||
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
|
||||
outputs: std::vec![output],
|
||||
diagnostics: std::vec::Vec::new(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn family_is_accepted(family: crate::EventFamily) -> bool {
|
||||
fn family_is_accepted(family: crate::MdEventFamily) -> bool {
|
||||
return ACCEPTED_FAMILIES.iter().any(|accepted| return *accepted == family);
|
||||
}
|
||||
|
||||
@@ -593,25 +593,25 @@ mod tests {
|
||||
fn observation(
|
||||
surface_code: &str,
|
||||
entry_code: &str,
|
||||
family: crate::EventFamily,
|
||||
family: crate::MdEventFamily,
|
||||
transaction_failed: bool,
|
||||
accounts: serde_json::Value,
|
||||
parameters: serde_json::Value,
|
||||
) -> crate::DecodedObservation {
|
||||
return crate::DecodedObservation {
|
||||
) -> crate::DcApiDecodedObservation {
|
||||
return crate::DcApiDecodedObservation {
|
||||
event_key: format!("{entry_code}:0"),
|
||||
event: crate::DecodedProtocolEvent {
|
||||
signature: crate::Signature("signature".to_string()),
|
||||
slot: crate::Slot(42),
|
||||
instruction_path: crate::InstructionPath("0".to_string()),
|
||||
program_id: crate::ProgramId("program-id".to_string()),
|
||||
protocol_code: crate::ProtocolCode("solana_native".to_string()),
|
||||
surface_code: crate::SurfaceCode(surface_code.to_string()),
|
||||
event_code: crate::EventCode(format!("{surface_code}.{entry_code}")),
|
||||
event_name: crate::EventName(entry_code.to_string()),
|
||||
event: crate::MdDecodedProtocolEvent {
|
||||
signature: crate::MdSignature("signature".to_string()),
|
||||
slot: crate::MdSlot(42),
|
||||
instruction_path: crate::MdInstructionPath("0".to_string()),
|
||||
program_id: crate::MdProgramId("program-id".to_string()),
|
||||
protocol_code: crate::MdProtocolCode("solana_native".to_string()),
|
||||
surface_code: crate::MdSurfaceCode(surface_code.to_string()),
|
||||
event_code: crate::MdEventCode(format!("{surface_code}.{entry_code}")),
|
||||
event_name: crate::MdEventName(entry_code.to_string()),
|
||||
event_family: family,
|
||||
source_kind: crate::EventSourceKind::Instruction,
|
||||
confidence: crate::DecoderConfidence::ManualExact,
|
||||
source_kind: crate::MdEventSourceKind::Instruction,
|
||||
confidence: crate::MdDecoderConfidence::ManualExact,
|
||||
},
|
||||
payload_json: serde_json::json!({"accounts": accounts, "parameters": parameters}),
|
||||
transaction_failed,
|
||||
@@ -621,9 +621,9 @@ mod tests {
|
||||
std::option::Option::None
|
||||
},
|
||||
observation_committed: !transaction_failed,
|
||||
proof: crate::DecoderProof {
|
||||
kind: crate::DecoderProofKind::Manual,
|
||||
confidence: crate::DecoderConfidence::ManualExact,
|
||||
proof: crate::DcApiDecoderProof {
|
||||
kind: crate::DcApiDecoderProofKind::Manual,
|
||||
confidence: crate::MdDecoderConfidence::ManualExact,
|
||||
evidence: std::vec!["fixture".to_string()],
|
||||
},
|
||||
};
|
||||
@@ -635,18 +635,18 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn stake_initialize_creates_instructional_staking_projection() {
|
||||
let materializer = crate::StakingMaterializer;
|
||||
let materializer = crate::MtStakingMaterializer;
|
||||
let observation = observation(
|
||||
super::STAKE_SURFACE,
|
||||
"initialize",
|
||||
crate::EventFamily::Lifecycle,
|
||||
crate::MdEventFamily::Lifecycle,
|
||||
false,
|
||||
serde_json::json!([account("stake_account", "stake111")]),
|
||||
serde_json::json!({"authorized": {"staker": "staker111"}}),
|
||||
);
|
||||
let result = crate::EventMaterializer::materialize(&materializer, &observation);
|
||||
assert_eq!(result.status, crate::MaterializerOutcomeStatus::Inserted);
|
||||
assert_eq!(result.outputs[0].family, crate::MaterializedEventFamily::Staking);
|
||||
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
|
||||
assert_eq!(result.outputs[0].family, crate::MdMaterializedEventFamily::Staking);
|
||||
assert_eq!(result.outputs[0].output_key, "stake_account:initialize:0");
|
||||
assert_eq!(result.outputs[0].payload_json["projection"]["stakeAccount"], "stake111");
|
||||
assert_eq!(
|
||||
@@ -657,11 +657,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn stake_authority_and_lockup_events_are_projected() {
|
||||
let materializer = crate::StakingMaterializer;
|
||||
let materializer = crate::MtStakingMaterializer;
|
||||
let authority = observation(
|
||||
super::STAKE_SURFACE,
|
||||
"authorize_with_seed",
|
||||
crate::EventFamily::Admin,
|
||||
crate::MdEventFamily::Admin,
|
||||
false,
|
||||
serde_json::json!([account("stake_account", "stake111")]),
|
||||
serde_json::json!({
|
||||
@@ -674,7 +674,7 @@ mod tests {
|
||||
let lockup = observation(
|
||||
super::STAKE_SURFACE,
|
||||
"set_lockup_checked",
|
||||
crate::EventFamily::Admin,
|
||||
crate::MdEventFamily::Admin,
|
||||
false,
|
||||
serde_json::json!([
|
||||
account("stake_account", "stake111"),
|
||||
@@ -682,8 +682,9 @@ mod tests {
|
||||
]),
|
||||
serde_json::json!({"epoch": 42}),
|
||||
);
|
||||
let authority_result = crate::EventMaterializer::materialize(&materializer, &authority);
|
||||
let lockup_result = crate::EventMaterializer::materialize(&materializer, &lockup);
|
||||
let authority_result =
|
||||
crate::MtApiEventMaterializer::materialize(&materializer, &authority);
|
||||
let lockup_result = crate::MtApiEventMaterializer::materialize(&materializer, &lockup);
|
||||
assert_eq!(authority_result.outputs[0].output_key, "stake_authority:authorize_with_seed:0");
|
||||
assert_eq!(
|
||||
authority_result.outputs[0].payload_json["projection"]["newAuthority"],
|
||||
@@ -698,11 +699,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn stake_value_projection_does_not_duplicate_lamport_changes() {
|
||||
let materializer = crate::StakingMaterializer;
|
||||
let materializer = crate::MtStakingMaterializer;
|
||||
let observation = observation(
|
||||
super::STAKE_SURFACE,
|
||||
"withdraw",
|
||||
crate::EventFamily::Staking,
|
||||
crate::MdEventFamily::Staking,
|
||||
false,
|
||||
serde_json::json!([
|
||||
account("stake_account", "stake111"),
|
||||
@@ -710,7 +711,7 @@ mod tests {
|
||||
]),
|
||||
serde_json::json!({"lamports": 99}),
|
||||
);
|
||||
let result = crate::EventMaterializer::materialize(&materializer, &observation);
|
||||
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
assert_eq!(result.outputs[0].output_key, "stake_value:withdraw:0");
|
||||
assert_eq!(result.outputs[0].payload_json["projection"]["lamports"], 99);
|
||||
assert_eq!(
|
||||
@@ -721,11 +722,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn vote_account_and_authority_events_are_projected() {
|
||||
let materializer = crate::StakingMaterializer;
|
||||
let materializer = crate::MtStakingMaterializer;
|
||||
let initialize = observation(
|
||||
super::VOTE_SURFACE,
|
||||
"initialize_account_v2",
|
||||
crate::EventFamily::Lifecycle,
|
||||
crate::MdEventFamily::Lifecycle,
|
||||
false,
|
||||
serde_json::json!([
|
||||
account("vote_account", "vote111"),
|
||||
@@ -740,7 +741,7 @@ mod tests {
|
||||
let authorize = observation(
|
||||
super::VOTE_SURFACE,
|
||||
"authorize_checked",
|
||||
crate::EventFamily::Admin,
|
||||
crate::MdEventFamily::Admin,
|
||||
false,
|
||||
serde_json::json!([
|
||||
account("vote_account", "vote111"),
|
||||
@@ -748,8 +749,10 @@ mod tests {
|
||||
]),
|
||||
serde_json::json!({"authorizationType": {"kind": "voter"}, "newAuthorityFromAccount": true}),
|
||||
);
|
||||
let initialize_result = crate::EventMaterializer::materialize(&materializer, &initialize);
|
||||
let authorize_result = crate::EventMaterializer::materialize(&materializer, &authorize);
|
||||
let initialize_result =
|
||||
crate::MtApiEventMaterializer::materialize(&materializer, &initialize);
|
||||
let authorize_result =
|
||||
crate::MtApiEventMaterializer::materialize(&materializer, &authorize);
|
||||
assert_eq!(initialize_result.outputs[0].output_key, "vote_account:initialize_account_v2:0");
|
||||
assert_eq!(
|
||||
initialize_result.outputs[0].payload_json["projection"]["validatorIdentity"],
|
||||
@@ -764,11 +767,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn vote_state_and_reward_outputs_are_distinguished() {
|
||||
let materializer = crate::StakingMaterializer;
|
||||
let materializer = crate::MtStakingMaterializer;
|
||||
let vote_state = observation(
|
||||
super::VOTE_SURFACE,
|
||||
"tower_sync_switch",
|
||||
crate::EventFamily::Staking,
|
||||
crate::MdEventFamily::Staking,
|
||||
false,
|
||||
serde_json::json!([
|
||||
account("vote_account", "vote111"),
|
||||
@@ -779,7 +782,7 @@ mod tests {
|
||||
let reward = observation(
|
||||
super::VOTE_SURFACE,
|
||||
"deposit_delegator_rewards",
|
||||
crate::EventFamily::Reward,
|
||||
crate::MdEventFamily::Reward,
|
||||
false,
|
||||
serde_json::json!([
|
||||
account("vote_account", "vote111"),
|
||||
@@ -787,40 +790,40 @@ mod tests {
|
||||
]),
|
||||
serde_json::json!({"depositLamports": 123}),
|
||||
);
|
||||
let vote_result = crate::EventMaterializer::materialize(&materializer, &vote_state);
|
||||
let reward_result = crate::EventMaterializer::materialize(&materializer, &reward);
|
||||
let vote_result = crate::MtApiEventMaterializer::materialize(&materializer, &vote_state);
|
||||
let reward_result = crate::MtApiEventMaterializer::materialize(&materializer, &reward);
|
||||
assert_eq!(vote_result.outputs[0].output_key, "vote_state:tower_sync_switch:0");
|
||||
assert_eq!(
|
||||
vote_result.outputs[0].payload_json["projection"]["switchProofHash"],
|
||||
"switch111"
|
||||
);
|
||||
assert_eq!(reward_result.outputs[0].family, crate::MaterializedEventFamily::Reward);
|
||||
assert_eq!(reward_result.outputs[0].family, crate::MdMaterializedEventFamily::Reward);
|
||||
assert_eq!(reward_result.outputs[0].output_key, "vote_reward:deposit_delegator_rewards:0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_staking_mutation_is_refused() {
|
||||
let materializer = crate::StakingMaterializer;
|
||||
let materializer = crate::MtStakingMaterializer;
|
||||
let observation = observation(
|
||||
super::STAKE_SURFACE,
|
||||
"delegate_stake",
|
||||
crate::EventFamily::Staking,
|
||||
crate::MdEventFamily::Staking,
|
||||
true,
|
||||
serde_json::json!([account("stake_account", "stake111")]),
|
||||
serde_json::json!({}),
|
||||
);
|
||||
let result = crate::EventMaterializer::materialize(&materializer, &observation);
|
||||
assert_eq!(result.status, crate::MaterializerOutcomeStatus::Refused);
|
||||
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
|
||||
assert!(result.outputs.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unrelated_or_disabled_observations_are_ignored() {
|
||||
let materializer = crate::StakingMaterializer;
|
||||
let materializer = crate::MtStakingMaterializer;
|
||||
let unrelated = observation(
|
||||
"solana_native_system",
|
||||
"create_account",
|
||||
crate::EventFamily::Lifecycle,
|
||||
crate::MdEventFamily::Lifecycle,
|
||||
false,
|
||||
serde_json::json!([]),
|
||||
serde_json::json!({}),
|
||||
@@ -828,30 +831,32 @@ mod tests {
|
||||
let redelegate = observation(
|
||||
super::STAKE_SURFACE,
|
||||
"redelegate",
|
||||
crate::EventFamily::Staking,
|
||||
crate::MdEventFamily::Staking,
|
||||
false,
|
||||
serde_json::json!([]),
|
||||
serde_json::json!({"officiallyDisabled": true}),
|
||||
);
|
||||
let unrelated_result = crate::EventMaterializer::materialize(&materializer, &unrelated);
|
||||
let redelegate_result = crate::EventMaterializer::materialize(&materializer, &redelegate);
|
||||
assert_eq!(unrelated_result.status, crate::MaterializerOutcomeStatus::Ignored);
|
||||
assert_eq!(redelegate_result.status, crate::MaterializerOutcomeStatus::Ignored);
|
||||
let unrelated_result =
|
||||
crate::MtApiEventMaterializer::materialize(&materializer, &unrelated);
|
||||
let redelegate_result =
|
||||
crate::MtApiEventMaterializer::materialize(&materializer, &redelegate);
|
||||
assert_eq!(unrelated_result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
|
||||
assert_eq!(redelegate_result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_serialization_is_deterministic() {
|
||||
let materializer = crate::StakingMaterializer;
|
||||
let materializer = crate::MtStakingMaterializer;
|
||||
let observation = observation(
|
||||
super::VOTE_SURFACE,
|
||||
"update_commission_bps",
|
||||
crate::EventFamily::Admin,
|
||||
crate::MdEventFamily::Admin,
|
||||
false,
|
||||
serde_json::json!([account("vote_account", "vote111")]),
|
||||
serde_json::json!({"commissionBasisPoints": 250, "commissionKind": "inflation_rewards"}),
|
||||
);
|
||||
let first = crate::EventMaterializer::materialize(&materializer, &observation);
|
||||
let second = crate::EventMaterializer::materialize(&materializer, &observation);
|
||||
let first = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
let second = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||
let first_json_result = serde_json::to_string(&first.outputs[0].payload_json);
|
||||
let first_json = match first_json_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
|
||||
Reference in New Issue
Block a user