use super::{
AutoParams, ChangelogConfig, CompiledSchema, FieldDefinition, FieldType, MutationDefinition,
QueryDefinition, TypeDefinition, compiled::ArgumentDefinition,
};
const ENTITY_CHANGE_LOG: &str = "EntityChangeLog";
const TRANSPORT_CHECKPOINT: &str = "TransportCheckpoint";
pub fn inject_changelog(schema: &mut CompiledSchema) {
let Some(cfg) = schema.changelog.clone() else {
return;
};
if !cfg.expose {
return;
}
schema.types.push(entity_change_log_type(&cfg));
schema.types.push(transport_checkpoint_type(&cfg));
schema.queries.push(entity_change_logs_query(&cfg));
schema.queries.push(transport_checkpoint_query(&cfg));
schema.mutations.push(upsert_checkpoint_mutation(&cfg));
schema.build_indexes();
}
fn entity_change_log_type(cfg: &ChangelogConfig) -> TypeDefinition {
let mut t =
TypeDefinition::new(ENTITY_CHANGE_LOG, format!("{}.v_entity_change_log", cfg.schema))
.with_description(
"An observer entity-change-log entry. Read-only; populated by the observer \
system. Paginate via the `pk_entity_change_log` cursor.",
)
.with_field(FieldDefinition::new("id", FieldType::Id))
.with_field(FieldDefinition::new("pk_entity_change_log", FieldType::Int))
.with_field(FieldDefinition::nullable("fk_customer_org", FieldType::String))
.with_field(FieldDefinition::nullable("fk_contact", FieldType::String))
.with_field(FieldDefinition::new("object_type", FieldType::String))
.with_field(FieldDefinition::new("object_id", FieldType::String))
.with_field(FieldDefinition::new("modification_type", FieldType::String))
.with_field(FieldDefinition::nullable("change_status", FieldType::String))
.with_field(FieldDefinition::new("object_data", FieldType::Json))
.with_field(FieldDefinition::nullable("extra_metadata", FieldType::Json))
.with_field(FieldDefinition::new("created_at", FieldType::DateTime));
t.requires_role.clone_from(&cfg.read_role);
t
}
fn transport_checkpoint_type(cfg: &ChangelogConfig) -> TypeDefinition {
let mut t =
TypeDefinition::new(TRANSPORT_CHECKPOINT, format!("{}.v_transport_checkpoint", cfg.schema))
.with_description(
"A consumer's change-log cursor checkpoint, keyed by `transport_name`.",
)
.with_field(FieldDefinition::new("transport_name", FieldType::String))
.with_field(FieldDefinition::new("last_pk", FieldType::Int))
.with_field(FieldDefinition::new("updated_at", FieldType::DateTime));
t.requires_role.clone_from(&cfg.read_role);
t
}
fn entity_change_logs_query(cfg: &ChangelogConfig) -> QueryDefinition {
let mut q = QueryDefinition::new("entity_change_logs", ENTITY_CHANGE_LOG)
.returning_list()
.with_sql_source(format!("{}.v_entity_change_log", cfg.schema));
q.description = Some(format!(
"Cursor-paginate the observer change-log. Poll with \
`where: {{ pk_entity_change_log: {{ gt: $cursor }} }}`, \
`orderBy: [{{ field: \"pk_entity_change_log\", direction: ASC }}]`, and a \
`limit` (server max {}). Optional equality filters: object_type, modification_type.",
cfg.max_limit
));
q.auto_params = AutoParams {
has_where: true,
has_order_by: true,
has_limit: true,
has_offset: false,
};
q.cache_ttl_seconds = Some(0);
q.requires_role.clone_from(&cfg.read_role);
q
}
fn transport_checkpoint_query(cfg: &ChangelogConfig) -> QueryDefinition {
let mut q = QueryDefinition::new("transport_checkpoint", TRANSPORT_CHECKPOINT)
.with_sql_source(format!("{}.v_transport_checkpoint", cfg.schema));
q.nullable = true;
q.description = Some("Fetch one consumer's checkpoint by transport_name.".to_string());
q.arguments = vec![ArgumentDefinition::new("transport_name", FieldType::String)];
q.cache_ttl_seconds = Some(0);
q.requires_role.clone_from(&cfg.read_role);
q
}
fn upsert_checkpoint_mutation(cfg: &ChangelogConfig) -> MutationDefinition {
let mut m = MutationDefinition::new("upsert_transport_checkpoint", TRANSPORT_CHECKPOINT);
m.sql_source = Some(format!("{}.fn_upsert_transport_checkpoint", cfg.schema));
m.description =
Some("Create or advance a consumer's change-log checkpoint (idempotent).".to_string());
m.arguments = vec![
ArgumentDefinition::new("transport_name", FieldType::String),
ArgumentDefinition::new("last_pk", FieldType::Int),
];
m.requires_role.clone_from(&cfg.write_role);
m
}
#[cfg(test)]
mod tests;