Admin API to get and set policy data
This commit is contained in:
@@ -45,6 +45,11 @@ fn finish(t: TransformOpenApi) -> TransformOpenApi {
|
||||
description: Some("Manage compatibility sessions from legacy clients".to_owned()),
|
||||
..Tag::default()
|
||||
})
|
||||
.tag(Tag {
|
||||
name: "policy-data".to_owned(),
|
||||
description: Some("Manage the dynamic policy data".to_owned()),
|
||||
..Tag::default()
|
||||
})
|
||||
.tag(Tag {
|
||||
name: "oauth2-session".to_owned(),
|
||||
description: Some("Manage OAuth2 sessions".to_owned()),
|
||||
|
||||
@@ -534,3 +534,50 @@ impl UpstreamOAuthLink {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/// The policy data
|
||||
#[derive(Serialize, JsonSchema)]
|
||||
pub struct PolicyData {
|
||||
#[serde(skip)]
|
||||
id: Ulid,
|
||||
|
||||
/// The creation date of the policy data
|
||||
created_at: DateTime<Utc>,
|
||||
|
||||
/// The policy data content
|
||||
data: serde_json::Value,
|
||||
}
|
||||
|
||||
impl From<mas_data_model::PolicyData> for PolicyData {
|
||||
fn from(policy_data: mas_data_model::PolicyData) -> Self {
|
||||
Self {
|
||||
id: policy_data.id,
|
||||
created_at: policy_data.created_at,
|
||||
data: policy_data.data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for PolicyData {
|
||||
const KIND: &'static str = "policy-data";
|
||||
const PATH: &'static str = "/api/admin/v1/policy-data";
|
||||
|
||||
fn id(&self) -> Ulid {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl PolicyData {
|
||||
/// Samples of policy data
|
||||
pub fn samples() -> [Self; 1] {
|
||||
[Self {
|
||||
id: Ulid::from_bytes([0x01; 16]),
|
||||
created_at: DateTime::default(),
|
||||
data: serde_json::json!({
|
||||
"hello": "world",
|
||||
"foo": 42,
|
||||
"bar": true
|
||||
}),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::passwords::PasswordManager;
|
||||
|
||||
mod compat_sessions;
|
||||
mod oauth2_sessions;
|
||||
mod policy_data;
|
||||
mod upstream_oauth_links;
|
||||
mod user_emails;
|
||||
mod user_sessions;
|
||||
@@ -47,6 +48,21 @@ where
|
||||
"/oauth2-sessions/{id}",
|
||||
get_with(self::oauth2_sessions::get, self::oauth2_sessions::get_doc),
|
||||
)
|
||||
.api_route(
|
||||
"/policy-data",
|
||||
post_with(self::policy_data::set, self::policy_data::set_doc),
|
||||
)
|
||||
.api_route(
|
||||
"/policy-data/latest",
|
||||
get_with(
|
||||
self::policy_data::get_latest,
|
||||
self::policy_data::get_latest_doc,
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/policy-data/{id}",
|
||||
get_with(self::policy_data::get, self::policy_data::get_doc),
|
||||
)
|
||||
.api_route(
|
||||
"/users",
|
||||
get_with(self::users::list, self::users::list_doc)
|
||||
|
||||
153
crates/handlers/src/admin/v1/policy_data/get.rs
Normal file
153
crates/handlers/src/admin/v1/policy_data/get.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
use aide::{OperationIo, transform::TransformOperation};
|
||||
use axum::{Json, response::IntoResponse};
|
||||
use hyper::StatusCode;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::{
|
||||
admin::{
|
||||
call_context::CallContext,
|
||||
model::PolicyData,
|
||||
params::UlidPathParam,
|
||||
response::{ErrorResponse, SingleResponse},
|
||||
},
|
||||
impl_from_error_for_route,
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error, OperationIo)]
|
||||
#[aide(output_with = "Json<ErrorResponse>")]
|
||||
pub enum RouteError {
|
||||
#[error(transparent)]
|
||||
Internal(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
|
||||
#[error("Policy data with ID {0} not found")]
|
||||
NotFound(Ulid),
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(mas_storage::RepositoryError);
|
||||
|
||||
impl IntoResponse for RouteError {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
let error = ErrorResponse::from_error(&self);
|
||||
let status = match self {
|
||||
Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Self::NotFound(_) => StatusCode::NOT_FOUND,
|
||||
};
|
||||
(status, Json(error)).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn doc(operation: TransformOperation) -> TransformOperation {
|
||||
operation
|
||||
.id("getPolicyData")
|
||||
.summary("Get policy data by ID")
|
||||
.tag("policy-data")
|
||||
.response_with::<200, Json<SingleResponse<PolicyData>>, _>(|t| {
|
||||
let [sample, ..] = PolicyData::samples();
|
||||
let response = SingleResponse::new_canonical(sample);
|
||||
t.description("Policy data was found").example(response)
|
||||
})
|
||||
.response_with::<404, RouteError, _>(|t| {
|
||||
let response = ErrorResponse::from_error(&RouteError::NotFound(Ulid::nil()));
|
||||
t.description("Policy data was not found").example(response)
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "handler.admin.v1.policy_data.get", skip_all, err)]
|
||||
pub async fn handler(
|
||||
CallContext { mut repo, .. }: CallContext,
|
||||
id: UlidPathParam,
|
||||
) -> Result<Json<SingleResponse<PolicyData>>, RouteError> {
|
||||
let policy_data = repo
|
||||
.policy_data()
|
||||
.get()
|
||||
.await?
|
||||
.ok_or(RouteError::NotFound(*id))?;
|
||||
|
||||
Ok(Json(SingleResponse::new_canonical(policy_data.into())))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hyper::{Request, StatusCode};
|
||||
use insta::assert_json_snapshot;
|
||||
use sqlx::PgPool;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::test_utils::{RequestBuilderExt, ResponseExt, TestState, setup};
|
||||
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_get(pool: PgPool) {
|
||||
setup();
|
||||
let mut state = TestState::from_pool(pool).await.unwrap();
|
||||
let token = state.token_with_scope("urn:mas:admin").await;
|
||||
|
||||
let mut rng = state.rng();
|
||||
let mut repo = state.repository().await.unwrap();
|
||||
|
||||
let policy_data = repo
|
||||
.policy_data()
|
||||
.set(
|
||||
&mut rng,
|
||||
&state.clock,
|
||||
serde_json::json!({"hello": "world"}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
repo.save().await.unwrap();
|
||||
|
||||
let request = Request::get(format!("/api/admin/v1/policy-data/{}", policy_data.id))
|
||||
.bearer(&token)
|
||||
.empty();
|
||||
let response = state.request(request).await;
|
||||
response.assert_status(StatusCode::OK);
|
||||
let body: serde_json::Value = response.json();
|
||||
assert_json_snapshot!(body, @r###"
|
||||
{
|
||||
"data": {
|
||||
"type": "policy-data",
|
||||
"id": "01FSHN9AG0MZAA6S4AF7CTV32E",
|
||||
"attributes": {
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"data": {
|
||||
"hello": "world"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
}
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_get_not_found(pool: PgPool) {
|
||||
setup();
|
||||
let mut state = TestState::from_pool(pool).await.unwrap();
|
||||
let token = state.token_with_scope("urn:mas:admin").await;
|
||||
|
||||
let request = Request::get(format!("/api/admin/v1/policy-data/{}", Ulid::nil()))
|
||||
.bearer(&token)
|
||||
.empty();
|
||||
let response = state.request(request).await;
|
||||
response.assert_status(StatusCode::NOT_FOUND);
|
||||
let body: serde_json::Value = response.json();
|
||||
assert_json_snapshot!(body, @r###"
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"title": "Policy data with ID 00000000000000000000000000 not found"
|
||||
}
|
||||
]
|
||||
}
|
||||
"###);
|
||||
}
|
||||
}
|
||||
149
crates/handlers/src/admin/v1/policy_data/get_latest.rs
Normal file
149
crates/handlers/src/admin/v1/policy_data/get_latest.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
use aide::{OperationIo, transform::TransformOperation};
|
||||
use axum::{Json, response::IntoResponse};
|
||||
use hyper::StatusCode;
|
||||
|
||||
use crate::{
|
||||
admin::{
|
||||
call_context::CallContext,
|
||||
model::PolicyData,
|
||||
response::{ErrorResponse, SingleResponse},
|
||||
},
|
||||
impl_from_error_for_route,
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error, OperationIo)]
|
||||
#[aide(output_with = "Json<ErrorResponse>")]
|
||||
pub enum RouteError {
|
||||
#[error(transparent)]
|
||||
Internal(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
|
||||
#[error("No policy data found")]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(mas_storage::RepositoryError);
|
||||
|
||||
impl IntoResponse for RouteError {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
let error = ErrorResponse::from_error(&self);
|
||||
let status = match self {
|
||||
Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Self::NotFound => StatusCode::NOT_FOUND,
|
||||
};
|
||||
(status, Json(error)).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn doc(operation: TransformOperation) -> TransformOperation {
|
||||
operation
|
||||
.id("getLatestPolicyData")
|
||||
.summary("Get the latest policy data")
|
||||
.tag("policy-data")
|
||||
.response_with::<200, Json<SingleResponse<PolicyData>>, _>(|t| {
|
||||
let [sample, ..] = PolicyData::samples();
|
||||
let response = SingleResponse::new_canonical(sample);
|
||||
t.description("Latest policy data was found")
|
||||
.example(response)
|
||||
})
|
||||
.response_with::<404, RouteError, _>(|t| {
|
||||
let response = ErrorResponse::from_error(&RouteError::NotFound);
|
||||
t.description("No policy data was found").example(response)
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "handler.admin.v1.policy_data.get_latest", skip_all, err)]
|
||||
pub async fn handler(
|
||||
CallContext { mut repo, .. }: CallContext,
|
||||
) -> Result<Json<SingleResponse<PolicyData>>, RouteError> {
|
||||
let policy_data = repo
|
||||
.policy_data()
|
||||
.get()
|
||||
.await?
|
||||
.ok_or(RouteError::NotFound)?;
|
||||
|
||||
Ok(Json(SingleResponse::new_canonical(policy_data.into())))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hyper::{Request, StatusCode};
|
||||
use insta::assert_json_snapshot;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::test_utils::{RequestBuilderExt, ResponseExt, TestState, setup};
|
||||
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_get_latest(pool: PgPool) {
|
||||
setup();
|
||||
let mut state = TestState::from_pool(pool).await.unwrap();
|
||||
let token = state.token_with_scope("urn:mas:admin").await;
|
||||
|
||||
let mut rng = state.rng();
|
||||
let mut repo = state.repository().await.unwrap();
|
||||
|
||||
repo.policy_data()
|
||||
.set(
|
||||
&mut rng,
|
||||
&state.clock,
|
||||
serde_json::json!({"hello": "world"}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
repo.save().await.unwrap();
|
||||
|
||||
let request = Request::get("/api/admin/v1/policy-data/latest")
|
||||
.bearer(&token)
|
||||
.empty();
|
||||
let response = state.request(request).await;
|
||||
response.assert_status(StatusCode::OK);
|
||||
let body: serde_json::Value = response.json();
|
||||
assert_json_snapshot!(body, @r###"
|
||||
{
|
||||
"data": {
|
||||
"type": "policy-data",
|
||||
"id": "01FSHN9AG0MZAA6S4AF7CTV32E",
|
||||
"attributes": {
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"data": {
|
||||
"hello": "world"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
}
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_get_no_latest(pool: PgPool) {
|
||||
setup();
|
||||
let mut state = TestState::from_pool(pool).await.unwrap();
|
||||
let token = state.token_with_scope("urn:mas:admin").await;
|
||||
|
||||
let request = Request::get("/api/admin/v1/policy-data/latest")
|
||||
.bearer(&token)
|
||||
.empty();
|
||||
let response = state.request(request).await;
|
||||
response.assert_status(StatusCode::NOT_FOUND);
|
||||
let body: serde_json::Value = response.json();
|
||||
assert_json_snapshot!(body, @r###"
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"title": "No policy data found"
|
||||
}
|
||||
]
|
||||
}
|
||||
"###);
|
||||
}
|
||||
}
|
||||
14
crates/handlers/src/admin/v1/policy_data/mod.rs
Normal file
14
crates/handlers/src/admin/v1/policy_data/mod.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
|
||||
mod get;
|
||||
mod get_latest;
|
||||
mod set;
|
||||
|
||||
pub use self::{
|
||||
get::{doc as get_doc, handler as get},
|
||||
get_latest::{doc as get_latest_doc, handler as get_latest},
|
||||
set::{doc as set_doc, handler as set},
|
||||
};
|
||||
133
crates/handlers/src/admin/v1/policy_data/set.rs
Normal file
133
crates/handlers/src/admin/v1/policy_data/set.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
use aide::{NoApi, OperationIo, transform::TransformOperation};
|
||||
use axum::{Json, response::IntoResponse};
|
||||
use hyper::StatusCode;
|
||||
use mas_storage::BoxRng;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
admin::{
|
||||
call_context::CallContext,
|
||||
model::PolicyData,
|
||||
response::{ErrorResponse, SingleResponse},
|
||||
},
|
||||
impl_from_error_for_route,
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error, OperationIo)]
|
||||
#[aide(output_with = "Json<ErrorResponse>")]
|
||||
pub enum RouteError {
|
||||
#[error(transparent)]
|
||||
Internal(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(mas_storage::RepositoryError);
|
||||
|
||||
impl IntoResponse for RouteError {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
let error = ErrorResponse::from_error(&self);
|
||||
let status = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
(status, Json(error)).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
fn data_example() -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"hello": "world",
|
||||
"foo": 42,
|
||||
"bar": true
|
||||
})
|
||||
}
|
||||
|
||||
/// # JSON payload for the `POST /api/admin/v1/policy-data`
|
||||
#[derive(Deserialize, JsonSchema)]
|
||||
#[serde(rename = "SetPolicyDataRequest")]
|
||||
pub struct SetPolicyDataRequest {
|
||||
#[schemars(example = "data_example")]
|
||||
pub data: serde_json::Value,
|
||||
}
|
||||
|
||||
pub fn doc(operation: TransformOperation) -> TransformOperation {
|
||||
operation
|
||||
.id("setPolicyData")
|
||||
.summary("Set the current policy data")
|
||||
.tag("policy-data")
|
||||
.response_with::<201, Json<SingleResponse<PolicyData>>, _>(|t| {
|
||||
let [sample, ..] = PolicyData::samples();
|
||||
let response = SingleResponse::new_canonical(sample);
|
||||
t.description("Policy data was successfully set")
|
||||
.example(response)
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "handler.admin.v1.policy_data.set", skip_all, err)]
|
||||
pub async fn handler(
|
||||
CallContext {
|
||||
mut repo, clock, ..
|
||||
}: CallContext,
|
||||
NoApi(mut rng): NoApi<BoxRng>,
|
||||
Json(request): Json<SetPolicyDataRequest>,
|
||||
) -> Result<(StatusCode, Json<SingleResponse<PolicyData>>), RouteError> {
|
||||
let policy_data = repo
|
||||
.policy_data()
|
||||
.set(&mut rng, &clock, request.data)
|
||||
.await?;
|
||||
|
||||
repo.save().await?;
|
||||
|
||||
Ok((
|
||||
StatusCode::CREATED,
|
||||
Json(SingleResponse::new_canonical(policy_data.into())),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hyper::{Request, StatusCode};
|
||||
use insta::assert_json_snapshot;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::test_utils::{RequestBuilderExt, ResponseExt, TestState, setup};
|
||||
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_create(pool: PgPool) {
|
||||
setup();
|
||||
let mut state = TestState::from_pool(pool).await.unwrap();
|
||||
let token = state.token_with_scope("urn:mas:admin").await;
|
||||
|
||||
let request = Request::post("/api/admin/v1/policy-data")
|
||||
.bearer(&token)
|
||||
.json(serde_json::json!({
|
||||
"data": {
|
||||
"hello": "world"
|
||||
}
|
||||
}));
|
||||
let response = state.request(request).await;
|
||||
response.assert_status(StatusCode::CREATED);
|
||||
let body: serde_json::Value = response.json();
|
||||
assert_json_snapshot!(body, @r###"
|
||||
{
|
||||
"data": {
|
||||
"type": "policy-data",
|
||||
"id": "01FSHN9AG0MZAA6S4AF7CTV32E",
|
||||
"attributes": {
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"data": {
|
||||
"hello": "world"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
}
|
||||
}
|
||||
"###);
|
||||
}
|
||||
}
|
||||
@@ -593,6 +593,185 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/admin/v1/policy-data": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"policy-data"
|
||||
],
|
||||
"summary": "Set the current policy data",
|
||||
"operationId": "setPolicyData",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SetPolicyDataRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Policy data was successfully set",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SingleResponse_for_PolicyData"
|
||||
},
|
||||
"example": {
|
||||
"data": {
|
||||
"type": "policy-data",
|
||||
"id": "01040G2081040G2081040G2081",
|
||||
"attributes": {
|
||||
"created_at": "1970-01-01T00:00:00Z",
|
||||
"data": {
|
||||
"hello": "world",
|
||||
"foo": 42,
|
||||
"bar": true
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01040G2081040G2081040G2081"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01040G2081040G2081040G2081"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/admin/v1/policy-data/latest": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"policy-data"
|
||||
],
|
||||
"summary": "Get the latest policy data",
|
||||
"operationId": "getLatestPolicyData",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Latest policy data was found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SingleResponse_for_PolicyData"
|
||||
},
|
||||
"example": {
|
||||
"data": {
|
||||
"type": "policy-data",
|
||||
"id": "01040G2081040G2081040G2081",
|
||||
"attributes": {
|
||||
"created_at": "1970-01-01T00:00:00Z",
|
||||
"data": {
|
||||
"hello": "world",
|
||||
"foo": 42,
|
||||
"bar": true
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01040G2081040G2081040G2081"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01040G2081040G2081040G2081"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "No policy data was found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"errors": [
|
||||
{
|
||||
"title": "No policy data found"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/admin/v1/policy-data/{id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"policy-data"
|
||||
],
|
||||
"summary": "Get policy data by ID",
|
||||
"operationId": "getPolicyData",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "The ID of the resource",
|
||||
"$ref": "#/components/schemas/ULID"
|
||||
},
|
||||
"style": "simple"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Policy data was found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SingleResponse_for_PolicyData"
|
||||
},
|
||||
"example": {
|
||||
"data": {
|
||||
"type": "policy-data",
|
||||
"id": "01040G2081040G2081040G2081",
|
||||
"attributes": {
|
||||
"created_at": "1970-01-01T00:00:00Z",
|
||||
"data": {
|
||||
"hello": "world",
|
||||
"foo": 42,
|
||||
"bar": true
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01040G2081040G2081040G2081"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/policy-data/01040G2081040G2081040G2081"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Policy data was not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
},
|
||||
"example": {
|
||||
"errors": [
|
||||
{
|
||||
"title": "Policy data with ID 00000000000000000000000000 not found"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/admin/v1/users": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -2666,6 +2845,86 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SetPolicyDataRequest": {
|
||||
"title": "JSON payload for the `POST /api/admin/v1/policy-data`",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"examples": [
|
||||
{
|
||||
"hello": "world",
|
||||
"foo": 42,
|
||||
"bar": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"SingleResponse_for_PolicyData": {
|
||||
"description": "A top-level response with a single resource",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"links"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/SingleResource_for_PolicyData"
|
||||
},
|
||||
"links": {
|
||||
"$ref": "#/components/schemas/SelfLinks"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SingleResource_for_PolicyData": {
|
||||
"description": "A single resource, with its type, ID, attributes and related links",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"attributes",
|
||||
"id",
|
||||
"links",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"description": "The type of the resource",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "The ID of the resource",
|
||||
"$ref": "#/components/schemas/ULID"
|
||||
},
|
||||
"attributes": {
|
||||
"description": "The attributes of the resource",
|
||||
"$ref": "#/components/schemas/PolicyData"
|
||||
},
|
||||
"links": {
|
||||
"description": "Related links",
|
||||
"$ref": "#/components/schemas/SelfLinks"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PolicyData": {
|
||||
"description": "The policy data",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"created_at",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"description": "The creation date of the policy data",
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"data": {
|
||||
"description": "The policy data content"
|
||||
}
|
||||
}
|
||||
},
|
||||
"UserFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3252,6 +3511,10 @@
|
||||
"name": "compat-session",
|
||||
"description": "Manage compatibility sessions from legacy clients"
|
||||
},
|
||||
{
|
||||
"name": "policy-data",
|
||||
"description": "Manage the dynamic policy data"
|
||||
},
|
||||
{
|
||||
"name": "oauth2-session",
|
||||
"description": "Manage OAuth2 sessions"
|
||||
|
||||
Reference in New Issue
Block a user