Skip to content
Open
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 Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ roaring = { version = "0.11.2", default-features = fa
rpds = { version = "1.1.2", default-features = false }
rstest = { version = "0.26.1", default-features = false }
rustc_version = { version = "0.4.1", default-features = false }
scalar_api_reference = { version = "0.2.2", default-features = false }
scalar_api_reference = { version = "0.2.12", default-features = false }
scc = { version = "3.8.4", default-features = false }
schemars = { version = "1.2.2" }
semver = { version = "1.0.27", default-features = false }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use core::{cmp::Ordering, fmt, iter, mem};
use std::{collections::HashMap, io};

use error_stack::{Report, ResultExt as _};
#[cfg(feature = "schemars")]
use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};

use self::metadata::{
ArrayMetadata, ObjectMetadata, PropertyArrayMetadata, PropertyMetadata, PropertyObjectMetadata,
Expand Down Expand Up @@ -85,6 +87,44 @@ pub enum Property {
Value(PropertyValue),
}

#[cfg(feature = "schemars")]
impl JsonSchema for Property {
fn schema_name() -> Cow<'static, str> {
Cow::Borrowed("Property")
}

fn json_schema(generator: &mut SchemaGenerator) -> Schema {
json_schema!({
"description": "A list of properties, properties keyed by the base URL of their \
property type, or a value of a data type.",
"anyOf": [
generator.subschema_for::<PropertyArray>(),
generator.subschema_for::<PropertyObject>(),
generator.subschema_for::<PropertyValue>(),
],
})
}
}

/// The schema of [`Property::Array`], which documents it as a component of its own.
#[cfg(feature = "schemars")]
struct PropertyArray;

#[cfg(feature = "schemars")]
impl JsonSchema for PropertyArray {
fn schema_name() -> Cow<'static, str> {
Cow::Borrowed("PropertyArray")
}

fn json_schema(generator: &mut SchemaGenerator) -> Schema {
json_schema!({
"type": "array",
"description": "A list of properties.",
"items": generator.subschema_for::<Property>(),
})
}
}

/// Property data combined with its corresponding metadata.
///
/// [`PropertyWithMetadata`] pairs each property with its metadata, maintaining the same
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(feature = "schemars")]
use alloc::borrow::Cow;
#[cfg(feature = "postgres")]
use core::error::Error;
use std::collections::HashMap;
Expand All @@ -7,6 +9,8 @@ use bytes::BytesMut;
use error_stack::Report;
#[cfg(feature = "postgres")]
use postgres_types::{FromSql, IsNull, ToSql, Type};
#[cfg(feature = "schemars")]
use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};

use super::{
Property, PropertyPath, PropertyPathElement, PropertyPathError, PropertyWithMetadata,
Expand All @@ -20,6 +24,26 @@ use crate::{knowledge::PropertyValue, ontology::BaseUrl};
#[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))]
pub struct PropertyObject(HashMap<BaseUrl, Property>);

#[cfg(feature = "schemars")]
impl JsonSchema for PropertyObject {
fn schema_name() -> Cow<'static, str> {
Cow::Borrowed("PropertyObject")
}

fn json_schema(generator: &mut SchemaGenerator) -> Schema {
json_schema!({
"type": "object",
"description": "Properties keyed by the base URL of their property type.",
"propertyNames": generator.subschema_for::<BaseUrl>(),
"additionalProperties": generator.subschema_for::<Property>(),
"examples": [{
"https://hash.ai/@h/types/property-type/shortname/": "alice",
"https://hash.ai/@h/types/property-type/preferred-name/": "Alice",
}],
})
}
}

impl PropertyObject {
#[must_use]
pub const fn new(properties: HashMap<BaseUrl, Property>) -> Self {
Expand Down
18 changes: 18 additions & 0 deletions libs/@blockprotocol/type-system/rust/src/knowledge/value/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod metadata;

#[cfg(feature = "schemars")]
use alloc::borrow::Cow;
#[cfg(feature = "postgres")]
use core::error::Error;
use core::fmt::{self, Debug};
Expand All @@ -10,6 +12,8 @@ use bytes::BytesMut;
use hash_codec::numeric::Real;
#[cfg(feature = "postgres")]
use postgres_types::{FromSql, IsNull, Json, ToSql, Type};
#[cfg(feature = "schemars")]
use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
use serde::Serialize as _;

pub use self::metadata::ValueMetadata;
Expand Down Expand Up @@ -62,6 +66,20 @@ pub enum PropertyValue {
Object(HashMap<String, Self>),
}

#[cfg(feature = "schemars")]
impl JsonSchema for PropertyValue {
fn schema_name() -> Cow<'static, str> {
Cow::Borrowed("PropertyValue")
}

fn json_schema(_: &mut SchemaGenerator) -> Schema {
json_schema!({
"description": "A value of a data type: `null`, a boolean, a string, a number, an \
array or an object.",
})
}
}

impl fmt::Display for PropertyValue {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.serialize(fmt)
Expand Down
19 changes: 19 additions & 0 deletions libs/@blockprotocol/type-system/rust/src/ontology/id/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,25 @@ impl ToSchema<'_> for BaseUrl {
}
}

#[cfg(feature = "schemars")]
impl JsonSchema for BaseUrl {
fn schema_name() -> Cow<'static, str> {
Cow::Borrowed("BaseUrl")
}

fn json_schema(_: &mut SchemaGenerator) -> Schema {
json_schema!({
"type": "string",
"title": "Base URL",
"description": "The base URL of a SemType ontology type (the $id of the schema without \
the versioned suffix). It must end with a trailing slash.",
"format": "uri",
"maxLength": 2048,
"pattern": "/$",
})
}
}

#[cfg(feature = "postgres")]
impl<'a> FromSql<'a> for BaseUrl {
fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ use crate::{knowledge::entity::id::EntityUuid, principal::role::WebRoleId};
)]
#[cfg_attr(feature = "codegen", derive(specta::Type))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(
feature = "schemars",
derive(schemars::JsonSchema),
schemars(
inline,
with = "Uuid",
description = "The UUID of a web.",
example = "8d86c8c3-d66d-43d1-859b-676c3bcaeadc"
)
)]
#[cfg_attr(
feature = "postgres",
derive(postgres_types::ToSql, postgres_types::FromSql),
Expand Down
3 changes: 1 addition & 2 deletions libs/@local/graph/api/src/rest/documentation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ const PUBLIC_REFERENCE_PATH: &str = "/";
/// # Panics
///
/// Panics if a document or a source list cannot be serialized, route paths overlap, the Scalar
/// bundle is missing or not UTF-8, the embedded Scalar configuration is invalid, or the Scalar
/// optional-auth correction does not match exactly once.
/// bundle is missing, or the embedded Scalar configuration is invalid.
pub(crate) fn routes(apis: &[Api]) -> Router {
let legacy_path = format!("{}/legacy", internal::PREFIX);
let legacy_document_path = format!("{legacy_path}/openapi.json");
Expand Down
31 changes: 3 additions & 28 deletions libs/@local/graph/api/src/rest/documentation/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,34 +38,9 @@ fn render(configuration: &str, sources: Value, bundle: &str) -> Result<Bytes, se
Ok(Bytes::from(render_scalar(&configuration, Some(bundle))))
}

/// The Scalar bundle with the optional-authentication correction applied.
static JAVASCRIPT: LazyLock<Bytes> = LazyLock::new(|| {
let mut javascript =
String::from_utf8(get_asset("scalar.js").expect("the Scalar bundle should exist"))
.expect("the Scalar bundle should be UTF-8");

// @scalar/api-reference 1.49.2 incorrectly makes an empty security alternative mandatory when
// another alternative combines schemes.
for (original, corrected) in [
(
"!t.some((e=>Object.keys(e).length>1))&&e.length<t.length",
"e.length<t.length",
),
(
"return e.some((e=>0===Object.keys(e).length))&&!t",
"return e.some((e=>0===Object.keys(e).length))",
),
] {
assert_eq!(
javascript.matches(original).count(),
1,
"the Scalar optional-auth correction should match exactly once"
);
javascript = javascript.replacen(original, corrected, 1);
}

Bytes::from(javascript)
});
/// The Scalar bundle.
static JAVASCRIPT: LazyLock<Bytes> =
LazyLock::new(|| Bytes::from(get_asset("scalar.js").expect("the Scalar bundle should exist")));

/// Serves a reference over `sources` at `path`, with the bundle it loads underneath.
pub(super) fn routes(path: &str, sources: &[Source]) -> Router {
Expand Down
Loading
Loading