#![allow(clippy::all)]
#![allow(unused_imports)]
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unreachable_code)]
#![allow(unused_mut)]
#![expect(unused_crate_dependencies)]
use crate::prelude::*;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)]
pub struct Pipeline {
pub name: String,
pub source: Link<Source>,
}
impl crate::HasName for Pipeline {
fn name(&self) -> &str { &self.name }
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)]
pub struct Source {
pub name: String,
pub url: String,
}
impl crate::HasName for Source {
fn name(&self) -> &str { &self.name }
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)]
pub struct Sink {
pub name: String,
pub destination: String,
}
impl crate::HasName for Sink {
fn name(&self) -> &str { &self.name }
}
type SinkCollection = Collection<Sink>;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)]
#[serde(transparent)]
pub struct ArchivedPipeline(pub Pipeline);
impl ArchivedPipeline {
pub fn new(inner: Pipeline) -> Self { Self(inner) }
pub fn into_inner(self) -> Pipeline { self.0 }
pub fn inner(&self) -> &Pipeline { &self.0 }
pub fn inner_mut(&mut self) -> &mut Pipeline { &mut self.0 }
}
impl ::core::ops::Deref for ArchivedPipeline {
type Target = Pipeline;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl ::core::ops::DerefMut for ArchivedPipeline {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl ::core::convert::AsRef<Pipeline> for ArchivedPipeline {
fn as_ref(&self) -> &Pipeline { &self.0 }
}
impl ::core::convert::AsMut<Pipeline> for ArchivedPipeline {
fn as_mut(&mut self) -> &mut Pipeline { &mut self.0 }
}
impl ::core::convert::From<Pipeline> for ArchivedPipeline {
fn from(inner: Pipeline) -> Self { Self(inner) }
}
impl ::core::convert::From<ArchivedPipeline> for Pipeline {
fn from(wrapper: ArchivedPipeline) -> Self { wrapper.0 }
}
impl crate::HasName for ArchivedPipeline {
fn name(&self) -> &str { self.0.name() }
}
impl crate::StateEntity for ArchivedPipeline {
type Entry = StateEntry;
const STATE_ENTRY: StateEntry = StateEntry::ArchivedPipeline;
fn description(&self) -> Option<&str> { self.0.description() }
fn summary(&self, id: crate::EntityId) -> crate::Summary { self.0.summary(id) }
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)]
#[serde(transparent)]
pub struct JsonConfig(pub serde_json::Value);
impl JsonConfig {
pub fn new(inner: serde_json::Value) -> Self { Self(inner) }
pub fn into_inner(self) -> serde_json::Value { self.0 }
pub fn inner(&self) -> &serde_json::Value { &self.0 }
pub fn inner_mut(&mut self) -> &mut serde_json::Value { &mut self.0 }
}
impl ::core::ops::Deref for JsonConfig {
type Target = serde_json::Value;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl ::core::ops::DerefMut for JsonConfig {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl ::core::convert::AsRef<serde_json::Value> for JsonConfig {
fn as_ref(&self) -> &serde_json::Value { &self.0 }
}
impl ::core::convert::AsMut<serde_json::Value> for JsonConfig {
fn as_mut(&mut self) -> &mut serde_json::Value { &mut self.0 }
}
impl ::core::convert::From<serde_json::Value> for JsonConfig {
fn from(inner: serde_json::Value) -> Self { Self(inner) }
}
impl ::core::convert::From<JsonConfig> for serde_json::Value {
fn from(wrapper: JsonConfig) -> Self { wrapper.0 }
}
impl crate::HasName for JsonConfig {
fn name(&self) -> &str { ForeignEntity::name(&self.0) }
}
impl crate::StateEntity for JsonConfig {
type Entry = StateEntry;
const STATE_ENTRY: StateEntry = StateEntry::JsonConfig;
fn description(&self) -> Option<&str> { ForeignEntity::description(&self.0) }
fn summary(&self, id: crate::EntityId) -> crate::Summary { ForeignEntity::summary(&self.0, id) }
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, utoipa::ToSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum StateEntry {
Pipeline,
Source,
ArchivedPipeline,
Sink,
JsonConfig,
}
impl StateEntry {
pub fn as_ref(&self) -> &str {
match self {
Self::Pipeline => "pipeline",
Self::Source => "source",
Self::ArchivedPipeline => "archived_pipeline",
Self::Sink => "sink",
Self::JsonConfig => "json_config",
}
}
}
impl ::core::convert::AsRef<str> for StateEntry {
fn as_ref(&self) -> &str { self.as_ref() }
}
impl ::std::str::FromStr for StateEntry {
type Err = String;
fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
match s {
"pipeline" => Ok(Self::Pipeline),
"source" => Ok(Self::Source),
"archived_pipeline" => Ok(Self::ArchivedPipeline),
"sink" => Ok(Self::Sink),
"json_config" => Ok(Self::JsonConfig),
_ => Err(format!("Unknown entity type: {0}", s)),
}
}
}
pub trait ForeignEntity: Clone + ::serde::Serialize + for<'de> ::serde::Deserialize<'de> {
fn name(&self) -> &str;
fn description(&self) -> Option<&str> { None }
fn summary(&self, id: crate::EntityId) -> crate::Summary {
crate::Summary {
id,
name: self.name().to_string(),
description: self.description().map(ToString::to_string),
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)]
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
pub enum Entity {
Pipeline(Pipeline),
Source(Source),
ArchivedPipeline(ArchivedPipeline),
Sink(Sink),
JsonConfig(JsonConfig),
}
impl From<&Entity> for StateEntry {
fn from(entity: &Entity) -> Self {
match entity {
Entity::Pipeline(_) => StateEntry::Pipeline,
Entity::Source(_) => StateEntry::Source,
Entity::ArchivedPipeline(_) => StateEntry::ArchivedPipeline,
Entity::Sink(_) => StateEntry::Sink,
Entity::JsonConfig(_) => StateEntry::JsonConfig,
}
}
}
impl crate::StateEntity for Pipeline {
type Entry = StateEntry;
const STATE_ENTRY: StateEntry = StateEntry::Pipeline;
}
impl crate::StateEntity for Source {
type Entry = StateEntry;
const STATE_ENTRY: StateEntry = StateEntry::Source;
}
impl crate::StateEntity for Sink {
type Entry = StateEntry;
const STATE_ENTRY: StateEntry = StateEntry::Sink;
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct State {
pub pipelines: crate::Collection<Pipeline>,
pub sources: crate::Collection<Source>,
pub archived: crate::Collection<ArchivedPipeline>,
pub sinks: SinkCollection,
pub configs: crate::Collection<JsonConfig>,
}
impl Default for State {
fn default() -> Self { Self::new() }
}
impl State {
pub fn new() -> Self {
Self {
pipelines: crate::Collection::new(),
sources: crate::Collection::new(),
archived: crate::Collection::new(),
configs: crate::Collection::new(),
sinks: Default::default(),
}
}
pub fn create_entity(&mut self, entity: Entity) -> crate::EntityId {
use crate::StateCollection;
match entity {
Entity::Pipeline(inner) => self.pipelines.create(inner),
Entity::Source(inner) => self.sources.create(inner),
Entity::ArchivedPipeline(inner) => self.archived.create(inner),
Entity::JsonConfig(inner) => self.configs.create(inner),
Entity::Sink(inner) => self.sinks.create(inner),
}
}
pub fn update_entity(&mut self, id: &str, entity: Entity) -> crate::Result<()> {
use crate::StateCollection;
match entity {
Entity::Pipeline(inner) => {
self.pipelines.update(id, inner)?;
}
Entity::Source(inner) => {
self.sources.update(id, inner)?;
}
Entity::ArchivedPipeline(inner) => {
self.archived.update(id, inner)?;
}
Entity::JsonConfig(inner) => {
self.configs.update(id, inner)?;
}
Entity::Sink(inner) => {
self.sinks.update(id, inner)?;
}
}
Ok(())
}
pub fn remove_entity(&mut self, id: &str, entry: StateEntry) -> crate::Result<()> {
use crate::StateCollection;
match entry {
StateEntry::Pipeline => self.pipelines.remove(id).map(|_| ()),
StateEntry::Source => self.sources.remove(id).map(|_| ()),
StateEntry::ArchivedPipeline => self.archived.remove(id).map(|_| ()),
StateEntry::JsonConfig => self.configs.remove(id).map(|_| ()),
StateEntry::Sink => self.sinks.remove(id).map(|_| ()),
}
}
pub fn get_entity(&self, id: &str, entry: StateEntry) -> Option<(crate::EntityId, Entity)> {
use crate::StateCollection;
match entry {
StateEntry::Pipeline => self
.pipelines
.get_entity(id)
.map(|(id, e)| (id.clone(), Entity::Pipeline(e.clone()))),
StateEntry::Source => {
self.sources.get_entity(id).map(|(id, e)| (id.clone(), Entity::Source(e.clone())))
}
StateEntry::ArchivedPipeline => self
.archived
.get_entity(id)
.map(|(id, e)| (id.clone(), Entity::ArchivedPipeline(e.clone()))),
StateEntry::JsonConfig => self
.configs
.get_entity(id)
.map(|(id, e)| (id.clone(), Entity::JsonConfig(e.clone()))),
StateEntry::Sink => {
self.sinks.get_entity(id).map(|(id, e)| (id.clone(), Entity::Sink(e.clone())))
}
}
}
pub fn list_entities(
&self,
entry: Option<StateEntry>,
) -> crate::hashbrown::HashMap<StateEntry, Vec<crate::Summary>> {
use crate::StateCollection;
let mut result = crate::hashbrown::HashMap::default();
if entry.is_none() || entry == Some(StateEntry::Pipeline) {
result.insert(StateEntry::Pipeline, self.pipelines.list());
}
if entry.is_none() || entry == Some(StateEntry::Source) {
result.insert(StateEntry::Source, self.sources.list());
}
if entry.is_none() || entry == Some(StateEntry::ArchivedPipeline) {
result.insert(StateEntry::ArchivedPipeline, self.archived.list());
}
if entry.is_none() || entry == Some(StateEntry::JsonConfig) {
result.insert(StateEntry::JsonConfig, self.configs.list());
}
if entry.is_none() || entry == Some(StateEntry::Sink) {
result.insert(StateEntry::Sink, self.sinks.list());
}
result
}
pub fn search_entities(
&self,
needle: &str,
) -> crate::hashbrown::HashMap<StateEntry, crate::hashbrown::HashMap<crate::EntityId, Entity>>
{
use crate::StateCollection;
let mut result = crate::hashbrown::HashMap::default();
{
let matches = self.pipelines.search_entities(needle);
if !matches.is_empty() {
let mut entities = crate::hashbrown::HashMap::default();
for (id, entity) in matches {
entities.insert(id.clone(), Entity::Pipeline(entity.clone()));
}
result.insert(StateEntry::Pipeline, entities);
}
}
{
let matches = self.sources.search_entities(needle);
if !matches.is_empty() {
let mut entities = crate::hashbrown::HashMap::default();
for (id, entity) in matches {
entities.insert(id.clone(), Entity::Source(entity.clone()));
}
result.insert(StateEntry::Source, entities);
}
}
{
let matches = self.archived.search_entities(needle);
if !matches.is_empty() {
let mut entities = crate::hashbrown::HashMap::default();
for (id, entity) in matches {
entities.insert(id.clone(), Entity::ArchivedPipeline(entity.clone()));
}
result.insert(StateEntry::ArchivedPipeline, entities);
}
}
{
let matches = self.configs.search_entities(needle);
if !matches.is_empty() {
let mut entities = crate::hashbrown::HashMap::default();
for (id, entity) in matches {
entities.insert(id.clone(), Entity::JsonConfig(entity.clone()));
}
result.insert(StateEntry::JsonConfig, entities);
}
}
{
let matches = self.sinks.search_entities(needle);
if !matches.is_empty() {
let mut entities = crate::hashbrown::HashMap::default();
for (id, entity) in matches {
entities.insert(id.clone(), Entity::Sink(entity.clone()));
}
result.insert(StateEntry::Sink, entities);
}
}
result
}
pub fn is_empty(&self) -> bool {
self.pipelines.is_empty()
&& self.sources.is_empty()
&& self.archived.is_empty()
&& self.configs.is_empty()
&& self.sinks.is_empty()
&& true
}
}
pub mod link_aliases {
use super::*;
pub type PipelineLink = crate::Link<Pipeline>;
pub type SourceLink = crate::Link<Source>;
pub type ArchivedPipelineLink = crate::Link<ArchivedPipeline>;
pub type JsonConfigLink = crate::Link<JsonConfig>;
pub type SinkLink = crate::Link<Sink>;
}
impl ForeignEntity for serde_json::Value {
fn name(&self) -> &str { self.get("name").and_then(|v| v.as_str()).unwrap_or("unnamed") }
fn description(&self) -> Option<&str> { self.get("description").and_then(|v| v.as_str()) }
}
#[derive(utoipa::OpenApi)]
#[openapi(
paths(
create_entity,
list_entities,
get_entity_by_id,
update_entity,
patch_entity_by_id,
remove_entity,
),
components(
responses(
OperationResponse,
EntitiesResponse,
ListResponse,
GetEntityResponse,
crate::ApiError,
),
schemas(
Entity,
StateEntry,
OperationResponse,
EntitiesResponse,
EntitiesMap,
ListResponse,
GetEntityResponse,
crate::Summary,
crate::EntityId,
link_aliases::PipelineLink,
link_aliases::SourceLink,
link_aliases::ArchivedPipelineLink,
link_aliases::SinkLink,
link_aliases::JsonConfigLink
)
),
tags((name = "entity", description = "Entity management endpoints"))
)]
#[derive(Debug, Clone)]
pub struct ApiState {
pub state: ::std::sync::Arc<::tokio::sync::RwLock<State>>,
}
impl ApiState {
pub fn new(state: State) -> Self {
Self { state: ::std::sync::Arc::new(::tokio::sync::RwLock::new(state)) }
}
pub fn new_from_state(state: ::std::sync::Arc<::tokio::sync::RwLock<State>>) -> Self {
Self { state }
}
}
impl ApiState {
pub fn router<S>(state: S) -> ::axum::Router<S>
where
S: Send + Sync + Clone + 'static,
ApiState: ::axum::extract::FromRef<S>,
{
::axum::Router::new()
.route(
"/",
::axum::routing::get(get_entities)
.put(create_entity)
.layer(::tower_http::compression::CompressionLayer::new()),
)
.route(
"/list",
::axum::routing::get(list_all_entities)
.layer(::tower_http::compression::CompressionLayer::new()),
)
.route(
"/list/{type}",
::axum::routing::get(list_entities)
.layer(::tower_http::compression::CompressionLayer::new()),
)
.route(
"/{id}",
::axum::routing::get(get_entity_by_id)
.post(update_entity)
.patch(patch_entity_by_id),
)
.route("/{entry}/{id}", ::axum::routing::delete(remove_entity))
.with_state(state)
}
pub fn event_middleware<T>(
event_tx: ::tokio::sync::mpsc::Sender<T>,
) -> impl Fn(
::axum::http::Request<::axum::body::Body>,
::axum::middleware::Next,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = ::axum::response::Response> + Send>,
> + Clone
where
T: From<ResponseEvent> + Send + 'static,
{
move |req: ::axum::http::Request<::axum::body::Body>, next: ::axum::middleware::Next| {
let tx = event_tx.clone();
Box::pin(async move {
let response = next.run(req).await;
if let Some(event) = response.extensions().get::<ResponseEvent>() {
let converted: T = event.clone().into();
let _ = tx.send(converted).await;
}
response
})
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)]
pub struct GetEntityQuery {
#[serde(rename = "type")]
entity_type: StateEntry,
}
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::ToResponse,
)]
pub struct OperationResponse {
#[schema(value_type = String, format = "uuid")]
pub id: crate::EntityId,
pub message: String,
}
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::ToResponse,
)]
pub struct GetEntityResponse {
id: crate::EntityId,
entity: Entity,
}
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::ToResponse,
)]
pub struct EntitiesResponse {
pub entities: EntitiesMap,
}
#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
pub struct EntitiesMap {
pub entities:
crate::hashbrown::HashMap<StateEntry, crate::hashbrown::HashMap<crate::EntityId, Entity>>,
}
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::ToResponse,
)]
pub struct ListResponse {
#[schema(
value_type = HashMap<StateEntry,
Vec<crate::Summary>>,
example = json!(
{"pipeline":[{"id":"my-pipeline",
"name":"My Pipeline",
"description":"Example pipeline"}],
"source":[{"id":"my-source",
"name":"My Source",
"description":"Example source"}]}
)
)]
pub entities: crate::hashbrown::HashMap<StateEntry, Vec<crate::Summary>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)]
pub enum ResponseEvent {
Created { id: crate::EntityId, entity: Entity },
Updated { id: crate::EntityId, entity: Entity },
Deleted { id: crate::EntityId, entry: StateEntry },
}
impl ::serde::Serialize for EntitiesMap {
fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
{
use ::serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(self.entities.len()))?;
for (state_entry, entities) in &self.entities {
let mut entity_map: crate::hashbrown::HashMap<
crate::EntityId,
crate::serde_json::Value,
> = crate::hashbrown::HashMap::default();
for (id, entity) in entities {
let inner_value =
crate::serde_json::to_value(entity).map_err(::serde::ser::Error::custom)?;
drop(entity_map.insert(id.clone(), inner_value));
}
map.serialize_entry(&state_entry.as_ref(), &entity_map)?;
}
map.end()
}
}
#[utoipa::path(
put,
path = "/",
tag = "entity",
request_body = Entity,
responses(
(status = 200, description = "Entity created successfully", body = OperationResponse),
(status = 500, description = "Internal server error", body = crate::ApiError)
)
)]
pub async fn create_entity(
::axum::extract::State(stately): ::axum::extract::State<ApiState>,
::axum::Json(entity): ::axum::Json<Entity>,
) -> ::axum::response::Response {
use ::axum::response::IntoResponse;
let mut state = stately.state.write().await;
let id = state.create_entity(entity.clone());
let mut response =
::axum::Json(OperationResponse { id: id.clone(), message: format!("Entity created") })
.into_response();
response.extensions_mut().insert(ResponseEvent::Created { id, entity });
response
}
#[utoipa::path(
post,
path = "/{id}",
tag = "entity",
params(
("id" = String, Path, description = "Entity ID")
),
request_body = Entity,
responses(
(status = 200, description = "Entity updated successfully", body = OperationResponse),
(status = 500, description = "Internal server error", body = crate::ApiError)
)
)]
pub async fn update_entity(
::axum::extract::State(stately): ::axum::extract::State<ApiState>,
::axum::extract::Path(id): ::axum::extract::Path<String>,
::axum::Json(entity): ::axum::Json<Entity>,
) -> ::axum::response::Response {
use ::axum::response::IntoResponse;
let mut state = stately.state.write().await;
match state.update_entity(&id, entity.clone()) {
Ok(_) => {
let entity_id: crate::EntityId = id.into();
let mut response = ::axum::Json(OperationResponse {
id: entity_id.clone(),
message: format!("Entity updated"),
})
.into_response();
response.extensions_mut().insert(ResponseEvent::Updated { id: entity_id, entity });
response
}
Err(e) => e.into_response(),
}
}
#[utoipa::path(
patch,
path = "/{id}",
tag = "entity",
params(
("id" = String, Path, description = "Entity ID")
),
request_body = Entity,
responses(
(status = 200, description = "Entity patched successfully", body = OperationResponse),
(status = 500, description = "Internal server error", body = crate::ApiError)
)
)]
pub async fn patch_entity_by_id(
::axum::extract::State(stately): ::axum::extract::State<ApiState>,
::axum::extract::Path(id): ::axum::extract::Path<String>,
::axum::Json(entity): ::axum::Json<Entity>,
) -> ::axum::response::Response {
use ::axum::response::IntoResponse;
let mut state = stately.state.write().await;
match state.update_entity(&id, entity.clone()) {
Ok(_) => {
let entity_id: crate::EntityId = id.into();
let mut response = ::axum::Json(OperationResponse {
id: entity_id.clone(),
message: format!("Entity patched"),
})
.into_response();
response.extensions_mut().insert(ResponseEvent::Updated { id: entity_id, entity });
response
}
Err(e) => e.into_response(),
}
}
#[utoipa::path(
delete,
path = "/{entry}/{id}",
tag = "entity",
params(
("entry" = StateEntry, Path, description = "Entity type"),
("id" = String, Path, description = "Entity ID")
),
responses(
(status = 200, description = "Entity removed successfully", body = OperationResponse),
(status = 404, description = "Entity not found", body = crate::ApiError),
(status = 500, description = "Internal server error", body = crate::ApiError)
)
)]
pub async fn remove_entity(
::axum::extract::State(stately): ::axum::extract::State<ApiState>,
::axum::extract::Path((entry, id)): ::axum::extract::Path<(StateEntry, String)>,
) -> ::axum::response::Response {
use ::axum::response::IntoResponse;
let mut state = stately.state.write().await;
if let Err(e) = state.remove_entity(&id, entry) {
return e.into_response();
}
let entity_id: crate::EntityId = id.into();
let mut response = ::axum::Json(OperationResponse {
id: entity_id.clone(),
message: format!("Entity removed"),
})
.into_response();
response.extensions_mut().insert(ResponseEvent::Deleted { id: entity_id, entry });
response
}
#[utoipa::path(
get,
path = "/list",
tag = "entity",
responses(
(status = 200, description = "List all entities", body = ListResponse)
)
)]
pub async fn list_all_entities(
::axum::extract::State(stately): ::axum::extract::State<ApiState>,
) -> crate::Result<::axum::Json<ListResponse>> {
let state = stately.state.read().await;
let entities = state.list_entities(None);
Ok(::axum::Json(ListResponse { entities }))
}
#[utoipa::path(
get,
path = "/list/{type}",
tag = "entity",
params(
("type" = StateEntry, Path, description = "Entity type to list")
),
responses(
(status = 200, description = "List entities by type", body = ListResponse)
)
)]
pub async fn list_entities(
::axum::extract::State(stately): ::axum::extract::State<ApiState>,
::axum::extract::Path(entity_type): ::axum::extract::Path<StateEntry>,
) -> crate::Result<::axum::Json<ListResponse>> {
let state = stately.state.read().await;
let entities = state.list_entities(Some(entity_type));
Ok(::axum::Json(ListResponse { entities }))
}
#[utoipa::path(
get,
path = "/",
tag = "entity",
params(
("name" = Option<String>, Query, description = "Identifier of entity, ie id or name"),
("type" = Option<StateEntry>, Query, description = "Type of entity")
),
responses(
(status = 200, description = "Get entities with filters", body = EntitiesResponse)
)
)]
pub async fn get_entities(
::axum::extract::State(stately): ::axum::extract::State<ApiState>,
) -> crate::Result<::axum::Json<EntitiesResponse>> {
let state = stately.state.read().await;
let entities = state.search_entities("");
Ok(::axum::Json(EntitiesResponse { entities: EntitiesMap { entities } }))
}
#[utoipa::path(
get,
path = "/{id}",
tag = "entity",
params(
("id" = String, Path, description = "Entity ID"),
GetEntityQuery
),
responses(
(status = 200, description = "Successfully retrieved entity", body = GetEntityResponse),
(status = 404, description = "Entity not found", body = crate::ApiError)
)
)]
pub async fn get_entity_by_id(
::axum::extract::State(stately): ::axum::extract::State<ApiState>,
::axum::extract::Path(id): ::axum::extract::Path<String>,
::axum::extract::Query(query): ::axum::extract::Query<GetEntityQuery>,
) -> crate::Result<::axum::Json<GetEntityResponse>> {
let state = stately.state.read().await;
let Some((id, entity)) = state.get_entity(&id, query.entity_type) else {
return Err(crate::Error::NotFound(format!("Entity with ID {0} not found", id)));
};
Ok(::axum::Json(GetEntityResponse { id, entity }))
}