Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/put doc endpoint merge fix #1523

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions catalyst-gateway/bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ rust_decimal = { version = "1.36.0", features = [
"serde-with-float",
"db-tokio-postgres",
] }
poem = { version = "3.1.3", features = ["embed", "prometheus", "compression"] }
poem-openapi = { version = "5.1.2", features = [
poem = { version = "3.1.6", features = ["embed", "prometheus", "compression"] }
poem-openapi = { version = "5.1.5", features = [
"openapi-explorer",
"rapidoc",
"redoc",
Expand Down
1 change: 1 addition & 0 deletions catalyst-gateway/bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod cli;
mod db;
mod jinja;
mod logger;
mod metrics;
mod service;
mod settings;
mod utils;
Expand Down
1 change: 1 addition & 0 deletions catalyst-gateway/bin/src/metrics/chain_follower.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//! Metrics related to Chain Follower analytics.
1 change: 1 addition & 0 deletions catalyst-gateway/bin/src/metrics/chain_indexer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//! Metrics related to Chain Indexer analytics.
60 changes: 60 additions & 0 deletions catalyst-gateway/bin/src/metrics/endpoint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//! Metrics related to endpoint analytics.

use std::sync::LazyLock;

use prometheus::{register_histogram_vec, register_int_counter_vec, HistogramVec, IntCounterVec};

/// Labels for the metrics
const METRIC_LABELS: [&str; 3] = ["endpoint", "method", "status_code"];
/// Labels for the client metrics
const CLIENT_METRIC_LABELS: [&str; 2] = ["client", "status_code"];

// Prometheus Metrics maintained by the service

/// HTTP Request duration histogram.
pub(crate) static HTTP_REQ_DURATION_MS: LazyLock<HistogramVec> = LazyLock::new(|| {
register_histogram_vec!(
"http_request_duration_ms",
"Duration of HTTP requests in milliseconds",
&METRIC_LABELS
)
.unwrap()
});

/// HTTP Request CPU Time histogram.
pub(crate) static HTTP_REQ_CPU_TIME_MS: LazyLock<HistogramVec> = LazyLock::new(|| {
register_histogram_vec!(
"http_request_cpu_time_ms",
"CPU Time of HTTP requests in milliseconds",
&METRIC_LABELS
)
.unwrap()
});

// No Tacho implemented to enable this.
// static ref HTTP_REQUEST_RATE: GaugeVec = register_gauge_vec!(
// "http_request_rate",
// "Rate of HTTP requests per second",
// &METRIC_LABELS
// )
// .unwrap();

/// HTTP Request count histogram.
pub(crate) static HTTP_REQUEST_COUNT: LazyLock<IntCounterVec> = LazyLock::new(|| {
register_int_counter_vec!(
"http_request_count",
"Number of HTTP requests",
&METRIC_LABELS
)
.unwrap()
});

/// Client Request Count histogram.
pub(crate) static CLIENT_REQUEST_COUNT: LazyLock<IntCounterVec> = LazyLock::new(|| {
register_int_counter_vec!(
"client_request_count",
"Number of HTTP requests per client",
&CLIENT_METRIC_LABELS
)
.unwrap()
});
1 change: 1 addition & 0 deletions catalyst-gateway/bin/src/metrics/memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//! Metrics related to memory analytics.
18 changes: 18 additions & 0 deletions catalyst-gateway/bin/src/metrics/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! This module contains submodules related to metrics report and analytics.

use prometheus::{default_registry, Registry};

pub(crate) mod chain_follower;
pub(crate) mod chain_indexer;
pub(crate) mod endpoint;
pub(crate) mod memory;

/// Initialize Prometheus metrics.
///
/// ## Returns
///
/// Returns the default prometheus registry.
#[must_use]
pub(crate) fn init_prometheus() -> Registry {
default_registry().clone()
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ use crate::{
#[derive(ApiResponse)]
#[allow(dead_code)]
pub(crate) enum Responses {
/// ## OK
///
/// The Document that was requested.
#[oai(status = 200)]
Ok(Cbor),
/// ## Not Found
///
/// The document could not be found.
#[oai(status = 404)]
NotFound,
Expand Down
32 changes: 29 additions & 3 deletions catalyst-gateway/bin/src/service/api/documents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@ use poem_openapi::{
payload::Json,
OpenApi,
};
use put_document::MAXIMUM_DOCUMENT_SIZE;
use post_document_index_query::query_filter::DocumentIndexQueryFilterBody;
use put_document::{bad_put_request::PutDocumentBadRequest, MAXIMUM_DOCUMENT_SIZE};

use crate::service::{
common::{
auth::none_or_rbac::NoneOrRBAC,
objects::document::bad_put_request::PutDocumentBadRequest,
self,
auth::{none_or_rbac::NoneOrRBAC, rbac::scheme::CatalystRBACSecurityScheme},
tags::ApiTags,
types::{generic::uuidv7::UUIDv7, payload::cbor::Cbor},
},
utilities::middleware::schema_validation::schema_version_validation,
};

mod get_document;
mod post_document_index_query;
mod put_document;

/// Cardano Follower API Endpoints
Expand Down Expand Up @@ -84,4 +86,28 @@ impl DocumentApi {
},
}
}

/// Post A Signed Document Index Query.
///
/// This endpoint produces a summary of signed documents that meet the criteria
/// defined in the request body.
///
/// It does not return the actual documents, just an index of the document identifiers
/// which allows the documents to be retrieved by the `GET document` endpoint.
#[oai(
path = "/draft/document/index",
method = "post",
operation_id = "postDocument",
transform = "schema_version_validation"
)]
async fn post_document(
&self, /// The Query Filter Specification
query: Json<DocumentIndexQueryFilterBody>,
page: Query<Option<common::types::generic::query::pagination::Page>>,
limit: Query<Option<common::types::generic::query::pagination::Limit>>,
/// Authorization required.
_auth: CatalystRBACSecurityScheme,
) -> post_document_index_query::AllResponses {
post_document_index_query::endpoint(query.0 .0, page.0, limit.0).await
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//! Document Index Query

use poem_openapi::{payload::Json, ApiResponse, Object};
use query_filter::DocumentIndexQueryFilter;
use response::DocumentIndexListDocumented;

use super::common;
use crate::service::common::responses::WithErrorResponses;

pub(crate) mod query_filter;
pub(crate) mod response;

/// Endpoint responses.
#[derive(ApiResponse)]
#[allow(dead_code)]
pub(crate) enum Responses {
/// ## OK
///
/// The Index of documents which match the query filter.
#[oai(status = 200)]
Ok(Json<DocumentIndexListDocumented>),
/// ## Not Found
///
/// No documents were found which match the query filter.
#[oai(status = 404)]
NotFound,
}

/// All responses.
pub(crate) type AllResponses = WithErrorResponses<Responses>;

/// Update user schema
#[derive(Debug, Object, Clone, Eq, PartialEq)]
pub(crate) struct QueryDocumentIndex {
/// Name
name: Option<String>,
}

/// # POST `/document/index`
#[allow(clippy::unused_async, clippy::no_effect_underscore_binding)]
pub(crate) async fn endpoint(
filter: DocumentIndexQueryFilter,
page: Option<common::types::generic::query::pagination::Page>,
limit: Option<common::types::generic::query::pagination::Limit>,
) -> AllResponses {
let _filter = filter;
let _page = page;
let _limit = limit;

// We return this when the filter results in no documents found.
Responses::NotFound.into()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//! CIP36 object

// TODO: This is NOT common, remove it once the rationalized endpoint is implemented.
// Retained to keep the existing code from breaking only.

use poem_openapi::{types::Example, NewType, Object};

use super::common::types::document::ver::EqOrRangedVerDocumented;
use crate::service::common::types::document::{
doc_ref::IdAndVerRefDocumented, doc_type::DocumentType, id::EqOrRangedIdDocumented,
};

/// Query Filter for the generation of a signed document index.
///
/// The Query works as a filter which acts like a sieve to filter out documents
/// which do not strictly match the metadata or payload fields included in the query
/// itself.
#[allow(clippy::doc_markdown)]
#[derive(Object, Default)]
#[oai(example = true)]
pub(crate) struct DocumentIndexQueryFilter {
/// ## Signed Document Type.
///
/// The document type must match one of the
/// [Registered Document Types](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/types/)
///
/// UUIDv4 Formatted 128bit value.
#[oai(rename = "type", skip_serializing_if_is_none)]
doc_type: Option<DocumentType>,
/// ## Document ID
///
/// Either an absolute single Document ID or a range of
/// [Document IDs](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#id)
#[oai(skip_serializing_if_is_none)]
id: Option<EqOrRangedIdDocumented>,
/// ## Document Version
///
/// Either an absolute single Document Version or a range of
/// [Document Versions](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#ver)
#[oai(skip_serializing_if_is_none)]
ver: Option<EqOrRangedVerDocumented>,
/// ## Document Reference
///
/// A [reference](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/meta/#ref-document-reference)
/// to another signed document. This fields can match any reference that matches the
/// defined [Document IDs](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#id)
/// and/or
/// [Document Versions](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#ver)
///
/// The kind of document that the reference refers to is defined by the
/// [Document Type](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/types/)
#[oai(rename = "ref", skip_serializing_if_is_none)]
doc_ref: Option<IdAndVerRefDocumented>,
/// ## Document Template
///
/// Documents that are created based on a template include the
/// [template reference](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/meta/#template-template-reference)
/// to another signed document. This fields can match any template reference that
/// matches the defined [Document IDs](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#id)
/// and/or
/// [Document Versions](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#ver)
///
/// The kind of document that the reference refers to is defined by the
/// [Document Type](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/types/)
/// however, it will always be a template type document that matches the document
/// itself.
#[oai(skip_serializing_if_is_none)]
template: Option<IdAndVerRefDocumented>,
/// ## Document Reply
///
/// This is a
/// [reply reference](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/meta/#reply-reply-reference)
/// which links one document to another, when acting as a reply to it.
/// Replies typically reference the same kind of document.
/// This fields can match any reply reference that matches the defined
/// [Document IDs](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#id)
/// and/or
/// [Document Versions](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#ver)
///
/// The kind of document that the reference refers to is defined by the
/// [Document Type](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/types/).
#[oai(skip_serializing_if_is_none)]
reply: Option<IdAndVerRefDocumented>,
/// ## Brand
///
/// This is a
/// [brand reference](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/meta/#brand_id)
/// to a brand document which defines the brand the document falls under.
/// This fields can match any brand reference that matches the defined
/// [Document IDs](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#id)
/// and/or
/// [Document Versions](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#ver)
///
/// Whether a Document Type has a brand reference is defined by its
/// [Document Type](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/types/).
#[oai(skip_serializing_if_is_none)]
brand: Option<IdAndVerRefDocumented>,
/// ## Campaign
///
/// This is a
/// [campaign reference](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/meta/#campaign_id)
/// to a campaign document which defines the campaign the document falls under.
/// This fields can match any campaign reference that matches the defined
/// [Document IDs](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#id)
/// and/or
/// [Document Versions](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#ver)
///
/// Whether a Document Type has a campaign reference is defined by its
/// [Document Type](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/types/).
#[oai(skip_serializing_if_is_none)]
campaign: Option<IdAndVerRefDocumented>,
/// ## Category
///
/// This is a
/// [category reference](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/meta/#category_id)
/// to a category document which defines the category the document falls under.
/// This fields can match any category reference that matches the defined
/// [Document IDs](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#id)
/// and/or
/// [Document Versions](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/spec/#ver)
///
/// Whether a Document Type has a category reference is defined by its
/// [Document Type](https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/types/).
#[oai(skip_serializing_if_is_none)]
category: Option<IdAndVerRefDocumented>,
}

impl Example for DocumentIndexQueryFilter {
fn example() -> Self {
Self {
doc_type: Some(DocumentType::example()),
id: Some(EqOrRangedIdDocumented::example()),
ver: Some(EqOrRangedVerDocumented::example()),
doc_ref: Some(IdAndVerRefDocumented::example_id_ref()),
template: Some(IdAndVerRefDocumented::example_id_and_ver_ref()),
reply: Some(IdAndVerRefDocumented::example()),
..Default::default()
}
}
}

// Note: We need to do this, because POEM doesn't give us a way to set `"title"` for the
// openapi docs on an object.
#[derive(NewType)]
#[oai(from_multipart = false, from_parameter = false, to_header = false)]
/// Document Index Query Filter
///
/// A Query Filter which causes documents whose metadata matches the provided
/// fields to be returned in the index list response.
///
/// Fields which are not set, are not used to filter documents based on those metadata
/// fields. This is equivalent to returning documents where those metadata fields either
/// do not exist, or do exist, but have any value.
pub(crate) struct DocumentIndexQueryFilterBody(pub(crate) DocumentIndexQueryFilter);
Loading
Loading