From 9c50bafeaa903dc76cb6963418e921cf64061e7b Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Tue, 29 Oct 2024 10:20:30 +0100 Subject: [PATCH 1/6] H-3519: `harpc`: rework the `Connection` type (#5483) --- Cargo.lock | 2 + libs/@local/harpc/client/Cargo.toml | 2 + libs/@local/harpc/client/package.json | 1 + .../harpc/client/src/connection/default.rs | 102 ++++++++++++ .../@local/harpc/client/src/connection/mod.rs | 51 ++++++ .../{connection.rs => connection/service.rs} | 6 +- libs/@local/harpc/client/src/lib.rs | 64 +++++++- libs/@local/harpc/server/examples/account.rs | 150 +++++++++--------- libs/@local/harpc/tower/src/request.rs | 7 + 9 files changed, 298 insertions(+), 87 deletions(-) create mode 100644 libs/@local/harpc/client/src/connection/default.rs create mode 100644 libs/@local/harpc/client/src/connection/mod.rs rename libs/@local/harpc/client/src/{connection.rs => connection/service.rs} (94%) diff --git a/Cargo.lock b/Cargo.lock index 5e4bb6fcf2a..e12068afbb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2631,8 +2631,10 @@ name = "harpc-client" version = "0.0.0" dependencies = [ "bytes", + "derive-where", "error-stack", "futures", + "harpc-codec", "harpc-net", "harpc-tower", "multiaddr", diff --git a/libs/@local/harpc/client/Cargo.toml b/libs/@local/harpc/client/Cargo.toml index 367a596353b..b42a2d74da1 100644 --- a/libs/@local/harpc/client/Cargo.toml +++ b/libs/@local/harpc/client/Cargo.toml @@ -17,10 +17,12 @@ tower = { workspace = true, public = true } # Private workspace dependencies error-stack = { workspace = true } +harpc-codec = { workspace = true } harpc-net = { workspace = true } # Private third-party dependencies bytes = { workspace = true } +derive-where = { workspace = true } futures = { workspace = true } multiaddr = { workspace = true } thiserror = { workspace = true } diff --git a/libs/@local/harpc/client/package.json b/libs/@local/harpc/client/package.json index 9f7dca41e65..1a88c0269ca 100644 --- a/libs/@local/harpc/client/package.json +++ b/libs/@local/harpc/client/package.json @@ -5,6 +5,7 @@ "license": "AGPL-3", "dependencies": { "@rust/error-stack": "0.5.0", + "@rust/harpc-codec": "0.0.0-private", "@rust/harpc-net": "0.0.0-private", "@rust/harpc-tower": "0.0.0-private" } diff --git a/libs/@local/harpc/client/src/connection/default.rs b/libs/@local/harpc/client/src/connection/default.rs new file mode 100644 index 00000000000..4a084a3c75d --- /dev/null +++ b/libs/@local/harpc/client/src/connection/default.rs @@ -0,0 +1,102 @@ +use core::task::{Context, Poll}; + +use bytes::Buf; +use error_stack::Report; +use futures::{Stream, StreamExt, TryFutureExt, future, stream}; +use harpc_net::session::error::ConnectionPartiallyClosedError; +use harpc_tower::{ + body::{Frame, stream::StreamBody}, + net::{pack_error::PackError, unpack::Unpack}, + request::Request, + response::Response, +}; +use tower::{Layer, Service}; + +use super::service::ConnectionService; + +pub(crate) struct DefaultLayer { + _private: (), +} + +impl DefaultLayer { + pub(crate) const fn new() -> Self { + Self { _private: () } + } +} + +impl Layer for DefaultLayer { + type Service = DefaultService; + + fn layer(&self, inner: S) -> Self::Service { + DefaultService { inner } + } +} + +#[expect(clippy::unnecessary_wraps)] +const fn map_buffer(buffer: B) -> Result, !> { + Ok(Frame::Data(buffer)) +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct DefaultService { + inner: S, +} + +impl Service> for DefaultService +where + S: Service< + Request Result, !>>>>, + Response = Response, + >, + St: Stream + Send, +{ + type Error = S::Error; + type Future = future::MapOk) -> Response>>; + type Response = Response>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + let request = req + .map_body(|body| { + // See https://users.rust-lang.org/t/expected-fn-pointer-found-fn-item/67368 as to why we need the cast here + body.map(map_buffer as fn(St::Item) -> Result, !>) + }) + .map_body(StreamBody::new); + + self.inner + .call(request) + .map_ok(|response| response.map_body(PackError::new)) + } +} + +#[derive(Debug, Clone)] +pub struct Default { + inner: DefaultService, +} + +impl Default { + pub(crate) const fn new(inner: DefaultService) -> Self { + Self { inner } + } +} + +impl Service> for Default +where + St: Stream + Send + 'static, +{ + type Error = Report; + type Response = Response>; + + type Future = impl Future>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + as Service>>::poll_ready(&mut self.inner, cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + self.inner.call(req) + } +} diff --git a/libs/@local/harpc/client/src/connection/mod.rs b/libs/@local/harpc/client/src/connection/mod.rs new file mode 100644 index 00000000000..b4a42d43cdc --- /dev/null +++ b/libs/@local/harpc/client/src/connection/mod.rs @@ -0,0 +1,51 @@ +use core::task::{Context, Poll}; + +use bytes::Buf; +use futures::Stream; +use harpc_tower::request::Request; +use tower::Service; + +pub mod default; +pub mod service; + +pub type DefaultConnection = Connection; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Connection { + service: S, + codec: C, +} + +impl Connection { + pub(crate) const fn new(service: S, codec: C) -> Self { + Self { service, codec } + } + + pub const fn codec(&self) -> &C { + &self.codec + } + + pub fn into_parts(self) -> (S, C) { + (self.service, self.codec) + } +} + +// We specifically restrict the implementation to just `Request` to ensure that the `Connection` +// is only used in a client (it also simplifies trait bounds). +impl Service> for Connection +where + St: Stream, + S: Service>, +{ + type Error = S::Error; + type Future = S::Future; + type Response = S::Response; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + self.service.call(req) + } +} diff --git a/libs/@local/harpc/client/src/connection.rs b/libs/@local/harpc/client/src/connection/service.rs similarity index 94% rename from libs/@local/harpc/client/src/connection.rs rename to libs/@local/harpc/client/src/connection/service.rs index f3e1fddf062..77df4be0741 100644 --- a/libs/@local/harpc/client/src/connection.rs +++ b/libs/@local/harpc/client/src/connection/service.rs @@ -16,13 +16,13 @@ use tower::Service; use crate::TransportLayerGuard; #[derive(Debug, Clone)] -pub struct Connection { +pub struct ConnectionService { inner: Arc, _guard: TransportLayerGuard, } -impl Connection { +impl ConnectionService { pub(crate) fn new( connection: harpc_net::session::client::Connection, guard: TransportLayerGuard, @@ -34,7 +34,7 @@ impl Connection { } } -impl Service> for Connection +impl Service> for ConnectionService where ReqBody: Body + Send + 'static, { diff --git a/libs/@local/harpc/client/src/lib.rs b/libs/@local/harpc/client/src/lib.rs index 18380a02cb8..bb1cbe6b503 100644 --- a/libs/@local/harpc/client/src/lib.rs +++ b/libs/@local/harpc/client/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(never_type, impl_trait_in_assoc_type)] +#![feature(never_type, impl_trait_in_assoc_type, type_alias_impl_trait)] extern crate alloc; @@ -13,8 +13,13 @@ use harpc_net::{ }; use multiaddr::Multiaddr; use tokio_util::sync::{CancellationToken, DropGuard}; +use tower::Layer; -use self::connection::Connection; +use self::connection::{ + Connection, + default::{self, DefaultLayer}, + service::ConnectionService, +}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub struct ClientConfig { @@ -41,19 +46,20 @@ pub(crate) struct TransportLayerGuard( ); #[derive(Debug, Clone)] -pub struct Client { +pub struct Client { session: Arc, + codec: C, guard: TransportLayerGuard, } -impl Client { +impl Client { /// Creates a new `Client` with the given configuration. /// /// # Errors /// /// Returns a `ClientError::StartTransportLayer` if unable to start the transport layer. - pub fn new(config: ClientConfig) -> Result> { + pub fn new(config: ClientConfig, codec: C) -> Result> { let token = CancellationToken::new(); let transport = TransportLayer::tcp(config.transport, token.clone()) @@ -65,6 +71,7 @@ impl Client { Ok(Self { session: Arc::new(session), + codec, guard: TransportLayerGuard(guard), }) } @@ -74,13 +81,56 @@ impl Client { /// # Errors /// /// Returns a `ClientError::Connect` if unable to establish a connection to the server. - pub async fn connect(&self, target: Multiaddr) -> Result> { + pub async fn connect( + &self, + target: Multiaddr, + ) -> Result, Report> + where + C: Clone + Sync, + { + let connection = self + .connect_with_service(DefaultLayer::new(), target) + .await?; + + Ok(Connection::new( + default::Default::new(connection), + self.codec.clone(), + )) + } + + pub async fn connect_with( + &self, + layer: L, + target: Multiaddr, + ) -> Result, Report> + where + L: Layer + Send, + C: Clone + Sync, + { + let connection = self.connect_with_service(layer, target).await?; + + Ok(Connection::new(connection, self.codec.clone())) + } + + async fn connect_with_service( + &self, + + layer: L, + target: Multiaddr, + ) -> Result<>::Service, Report> + where + L: Layer + Send, + C: Sync, + { let connection = self .session .dial(target) .await .change_context(ClientError::Connect)?; - Ok(Connection::new(connection, self.guard.clone())) + let inner = ConnectionService::new(connection, self.guard.clone()); + let connection = layer.layer(inner); + + Ok(connection) } } diff --git a/libs/@local/harpc/server/examples/account.rs b/libs/@local/harpc/server/examples/account.rs index 1825200aff5..7f006fb09f9 100644 --- a/libs/@local/harpc/server/examples/account.rs +++ b/libs/@local/harpc/server/examples/account.rs @@ -1,4 +1,4 @@ -#![feature(never_type, impl_trait_in_assoc_type)] +#![feature(never_type, impl_trait_in_assoc_type, result_flattening)] #![expect( clippy::unwrap_used, clippy::print_stdout, @@ -10,18 +10,15 @@ extern crate alloc; use alloc::vec; -use core::{error::Error, fmt::Debug, future::ready}; +use core::{error::Error, fmt::Debug}; use std::time::Instant; use bytes::Buf; -use error_stack::{Report, ResultExt}; +use error_stack::{FutureExt as _, Report, ResultExt}; use frunk::HList; -use futures::{ - Stream, StreamExt, TryStreamExt, pin_mut, - stream::{self}, -}; +use futures::{Stream, StreamExt, TryFutureExt, TryStreamExt, pin_mut, stream}; use graph_types::account::AccountId; -use harpc_client::{Client, ClientConfig}; +use harpc_client::{Client, ClientConfig, connection::Connection}; use harpc_codec::{decode::Decoder, encode::Encoder, json::JsonCodec}; use harpc_net::session::server::SessionId; use harpc_server::{Server, ServerConfig, router::RouterBuilder, serve::serve}; @@ -34,14 +31,10 @@ use harpc_service::{ }; use harpc_tower::{ Extensions, - body::{Body, BodyExt, Frame, stream::StreamBody}, + body::{Body, BodyExt}, layer::{ - body_report::HandleBodyReportLayer, - boxed::BoxedResponseLayer, - map_body::{MapRequestBodyLayer, MapResponseBodyLayer}, - report::HandleReportLayer, + body_report::HandleBodyReportLayer, boxed::BoxedResponseLayer, report::HandleReportLayer, }, - net::pack_error::PackError, request::{self, Request}, response::{Parts, Response}, }; @@ -52,7 +45,7 @@ use harpc_types::{ version::Version, }; use multiaddr::multiaddr; -use tower::{ServiceBuilder, ServiceExt as _}; +use tower::ServiceExt as _; use uuid::Uuid; enum AccountProcedureId { @@ -160,39 +153,40 @@ where #[derive(Debug, Clone)] struct AccountServiceClient; -impl - AccountService> for AccountServiceClient +impl + AccountService>> for AccountServiceClient where // TODO: I want to get rid of the boxed stream here, the problem is just that `Output` has `` - // as a type parameter, therefore cannot parametrize over it, unless we box or duplicate the + // as a type parameter, therefore cannot parameterize over it, unless we box or duplicate the // trait requirement. both are not great solutions. - S: tower::Service< - Request::Buf, !>, !>>>>, + Svc: tower::Service< + Request>>, Response = Response, - Future: Send, Error = Report, + Future: Send, > + Clone + Send + Sync, - E: Encoder, Buf: Send + 'static> + St: Stream> + Send + Sync, + ResData: Buf, + C: Encoder, Buf: Send + 'static> + Decoder> + Clone + Send + Sync, - St: Stream> + Send + Sync, - ResData: Buf, - ServiceError: Error + Send + Sync + 'static, DecoderError: Error + Send + Sync + 'static, EncoderError: Error + Send + Sync + 'static, + ServiceError: Error + Send + Sync + 'static, { - async fn create_account( + fn create_account( &self, - session: &(S, E), + session: &Connection, payload: CreateAccount, - ) -> Result> { - let (service, codec) = session.clone(); + ) -> impl Future>> { + let codec = session.codec().clone(); + let connection = session.clone(); - // in theory we could also skip the allocation here, but the problem is that in that case we + // In theory we could also skip the allocation here, but the problem is that in that case we // would send data that *might* be malformed, or is missing data. Instead of skipping said // data we allocate. In future we might want to instead have something like // tracing::error or a panic instead, but this is sufficient for now. @@ -203,57 +197,63 @@ where // possible we would await yet another challenge, what happens if the transport layer // encounters an error? We can't very well send that error to the server just for us to // return it, the server might already be processing things and now suddenly needs to stop? - // So we'd need to panic or filter on the client and would have partially commited data on + // So we'd need to panic or filter on the client and would have partially committed data on // the server. + // // This circumvents the problem because we just return an error early, in the future - if // the need arises - we might want to investigate request cancellation (which should be - // possible in the protocol) + // possible in the protocol). + // // That'd allow us to cancel the request but would make response handling *a lot* more // complex. + // // This isn't a solved problem at all in e.g. rust in general, because there are some things - // you can't just cancel. How do you roll back a potentially already commited transaction? + // you can't just cancel. How do you roll back a potentially already committed transaction? // The current hypothesis is that the overhead required for one less allocation simply isn't // worth it, but in the future we might want to revisit this. - let body: Vec<_> = codec + codec .clone() .encode(stream::iter([payload])) - .map(|item| item.map(Frame::Data).map(Ok)) .try_collect() - .await - .change_context(AccountError::Encode)?; - - let request = Request::from_parts( - request::Parts { - service: ServiceDescriptor { - id: Account::ID, - version: Account::VERSION, - }, - procedure: ProcedureDescriptor { - id: CreateAccount::ID.into_id(), - }, - session: SessionId::CLIENT, - extensions: Extensions::new(), - }, - stream::iter(body), - ); - - let response = service - .oneshot(request) - .await - .change_context(AccountError::Connection)?; - - let (parts, body) = response.into_parts(); - - let data = codec.decode(body); - tokio::pin!(data); - - let item = data - .next() - .await - .ok_or_else(|| Report::new(AccountError::ExpectedResponse))? - .change_context(AccountError::Decode)?; - - Ok(item) + .change_context(AccountError::Encode) + .map_ok(|bytes: Vec<_>| { + Request::from_parts( + request::Parts { + service: ServiceDescriptor { + id: Account::ID, + version: Account::VERSION, + }, + procedure: ProcedureDescriptor { + id: CreateAccount::ID.into_id(), + }, + session: SessionId::CLIENT, + extensions: Extensions::new(), + }, + stream::iter(bytes), + ) + }) + .and_then(move |request| { + connection + .oneshot(request) + .change_context(AccountError::Connection) + }) + .and_then(move |response| { + let (parts, body) = response.into_parts(); + + let data = codec.decode(body); + + async move { + tokio::pin!(data); + + let data = data + .next() + .await + .ok_or_else(|| Report::new(AccountError::ExpectedResponse))? + .change_context(AccountError::Decode)?; + + Ok(data) + } + }) } } @@ -338,7 +338,8 @@ async fn server() { } async fn client() { - let client = Client::new(ClientConfig::default()).expect("should be able to start service"); + let client = + Client::new(ClientConfig::default(), JsonCodec).expect("should be able to start service"); let service = AccountServiceClient; @@ -347,15 +348,10 @@ async fn client() { .await .expect("should be able to connect"); - let connection = ServiceBuilder::new() - .layer(MapRequestBodyLayer::new(|req| ready(StreamBody::new(req)))) - .layer(MapResponseBodyLayer::new(|res| ready(PackError::new(res)))) - .service(connection); - for _ in 0..16 { let now = Instant::now(); let account_id = service - .create_account(&(connection.clone(), JsonCodec), CreateAccount { id: None }) + .create_account(&connection, CreateAccount { id: None }) .await .expect("should be able to create account"); diff --git a/libs/@local/harpc/tower/src/request.rs b/libs/@local/harpc/tower/src/request.rs index fe0f5e6b793..ae04cee731d 100644 --- a/libs/@local/harpc/tower/src/request.rs +++ b/libs/@local/harpc/tower/src/request.rs @@ -74,4 +74,11 @@ impl Request { pub fn extensions_mut(&mut self) -> &mut Extensions { &mut self.head.extensions } + + pub fn map_body(self, closure: impl FnOnce(B) -> B2) -> Request { + Request { + head: self.head, + body: closure(self.body), + } + } } From e9b14b55b10c30c0653ab89689fbe62fde9394a5 Mon Sep 17 00:00:00 2001 From: Tim Diekmann <21277928+TimDiekmann@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:07:11 +0100 Subject: [PATCH 2/6] H-3525: Update description of generated block code (#5515) --- .github/workflows/lint.yml | 2 +- blocks/chart/src/types/generated/block-entity.ts | 3 +++ blocks/faq/src/types/generated/block-entity.ts | 6 ++++++ blocks/table/src/types/generated/block-entity.ts | 3 +++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a889cdb802a..962ad4170b9 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -126,7 +126,7 @@ jobs: if: always() && steps.lints.outputs.codegen == 'true' run: | set -o pipefail - turbo run codegen --no-cache --filter "${{ matrix.package }}" + turbo run codegen --force --filter "${{ matrix.package }}" while IFS= read -r line; do if [[ -n "$line" ]]; then echo "Checking diff of ${{ matrix.directory }}/$line" diff --git a/blocks/chart/src/types/generated/block-entity.ts b/blocks/chart/src/types/generated/block-entity.ts index 33eb0d8f4e8..aa7659d079e 100644 --- a/blocks/chart/src/types/generated/block-entity.ts +++ b/blocks/chart/src/types/generated/block-entity.ts @@ -64,6 +64,9 @@ export type QueryOutgoingLinkAndTarget = never; export type QueryOutgoingLinksByLinkEntityTypeId = {}; +/** + * A structured query for data, including e.g. the types of filters to be applied in order to produce the data. + */ export type QueryProperties = { "https://blockprotocol.org/@hash/types/property-type/query/": QueryPropertyValue; }; diff --git a/blocks/faq/src/types/generated/block-entity.ts b/blocks/faq/src/types/generated/block-entity.ts index 7052fe3dc9b..ec0b5bf6d53 100644 --- a/blocks/faq/src/types/generated/block-entity.ts +++ b/blocks/faq/src/types/generated/block-entity.ts @@ -42,6 +42,9 @@ export type FAQBlockOutgoingLinksByLinkEntityTypeId = { "https://blockprotocol.org/@hash/types/entity-type/has-frequently-asked-question/v/1": FAQBlockHasFrequentlyAskedQuestionLink; }; +/** + * A block to display Frequently Asked Questions (FAQ). + */ export type FAQBlockProperties = { "https://blockprotocol.org/@blockprotocol/types/property-type/description/"?: DescriptionPropertyValue; "https://blockprotocol.org/@blockprotocol/types/property-type/title/"?: TitlePropertyValue; @@ -55,6 +58,9 @@ export type FrequentlyAskedQuestionOutgoingLinkAndTarget = never; export type FrequentlyAskedQuestionOutgoingLinksByLinkEntityTypeId = {}; +/** + * A question frequently asked about something – or, often, a question that someone anticipates will be asked about something. + */ export type FrequentlyAskedQuestionProperties = { "https://blockprotocol.org/@blockprotocol/types/property-type/question/"?: QuestionPropertyValue; "https://blockprotocol.org/@blockprotocol/types/property-type/answer/"?: AnswerPropertyValue; diff --git a/blocks/table/src/types/generated/block-entity.ts b/blocks/table/src/types/generated/block-entity.ts index 85893072d59..b0cb506dae5 100644 --- a/blocks/table/src/types/generated/block-entity.ts +++ b/blocks/table/src/types/generated/block-entity.ts @@ -46,6 +46,9 @@ export type QueryOutgoingLinkAndTarget = never; export type QueryOutgoingLinksByLinkEntityTypeId = {}; +/** + * A structured query for data, including e.g. the types of filters to be applied in order to produce the data. + */ export type QueryProperties = { "https://blockprotocol.org/@hash/types/property-type/query/": QueryPropertyValue; }; From 56fcecffab64e992a762d6388ab0e0ff29f0f678 Mon Sep 17 00:00:00 2001 From: Tim Diekmann <21277928+TimDiekmann@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:08:23 +0100 Subject: [PATCH 3/6] H-3524: Remove wrong input parameters from terraform execute action (#5514) --- .github/actions/terraform-exec/action.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/actions/terraform-exec/action.yml b/.github/actions/terraform-exec/action.yml index 1f5d077fa97..f813a271db9 100644 --- a/.github/actions/terraform-exec/action.yml +++ b/.github/actions/terraform-exec/action.yml @@ -10,12 +10,8 @@ inputs: required: false default: . command: - type: choice description: Terraform command to run required: true - options: - - plan - - apply env: description: Environment to run the command within required: true @@ -95,14 +91,14 @@ runs: await core.summary .addHeading(heading) .addDetails( - "Output of Terraform initialization ⚙️ (${{ steps.init.outcome }})", + "Output of Terraform initialization ⚙️ (${{ steps.init.outcome }})", `
${init}
` ) .addDetails( "Output of validation 🤖 (${{ steps.validate.outcome }})", `
${validate}
` ) - .addDetails("Output of ${{ inputs.command }} 📖 (${{ steps.cmd.outcome }})", + .addDetails("Output of ${{ inputs.command }} 📖 (${{ steps.cmd.outcome }})", `
${cmd}
` ) .write(); From ef0aa53f4ef3f05fb13f6c4ad5c37e8a102e3a27 Mon Sep 17 00:00:00 2001 From: Tim Diekmann <21277928+TimDiekmann@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:36:57 +0100 Subject: [PATCH 4/6] H-3518: Differentiate a `ClosedEntityType` and `ClosedMultiEntityType` (#5479) Co-authored-by: Ciaran Morinan <37743469+CiaranMn@users.noreply.github.com> --- .../bins/cli/src/subcommand/server.rs | 2 +- .../libs/graph/src/snapshot/entity/batch.rs | 4 +- .../snapshot/ontology/entity_type/channel.rs | 30 +- .../libs/graph/src/store/knowledge.rs | 4 +- .../store/postgres/knowledge/entity/mod.rs | 87 +- .../libs/graph/src/store/validation.rs | 46 +- .../libs/store/src/entity_type/query.rs | 4 +- .../src/schema/data_type/constraint/object.rs | 2 +- .../rust/src/schema/entity_type/closed.rs | 224 ++-- .../src/schema/entity_type/constraints.rs | 106 ++ .../rust/src/schema/entity_type/mod.rs | 109 +- .../rust/src/schema/entity_type/raw.rs | 201 --- .../rust/src/schema/entity_type/validation.rs | 4 +- .../type-system/rust/src/schema/mod.rs | 7 +- .../type-system/rust/src/schema/object/mod.rs | 12 +- .../rust/src/schema/object/validation.rs | 8 +- .../rust/src/knowledge/property/error.rs | 12 +- .../hash-graph-types/rust/src/ontology/mod.rs | 15 +- .../@local/hash-validation/src/entity_type.rs | 99 +- libs/@local/hash-validation/src/lib.rs | 45 +- tests/hash-graph-http/test.sh | 2 + .../tests/link-inheritance.http | 1144 +++++++++++++++++ 22 files changed, 1736 insertions(+), 431 deletions(-) create mode 100644 libs/@blockprotocol/type-system/rust/src/schema/entity_type/constraints.rs delete mode 100644 libs/@blockprotocol/type-system/rust/src/schema/entity_type/raw.rs create mode 100644 tests/hash-graph-http/tests/link-inheritance.http diff --git a/apps/hash-graph/bins/cli/src/subcommand/server.rs b/apps/hash-graph/bins/cli/src/subcommand/server.rs index 421becc09cd..1da20f89a5e 100644 --- a/apps/hash-graph/bins/cli/src/subcommand/server.rs +++ b/apps/hash-graph/bins/cli/src/subcommand/server.rs @@ -129,7 +129,7 @@ pub struct ServerArgs { #[clap(long, env = "HASH_TEMPORAL_SERVER_HOST")] pub temporal_host: Option, - /// The URL of the Temporal server. + /// The port of the Temporal server. #[clap(long, env = "HASH_TEMPORAL_SERVER_PORT", default_value_t = 7233)] pub temporal_port: u16, } diff --git a/apps/hash-graph/libs/graph/src/snapshot/entity/batch.rs b/apps/hash-graph/libs/graph/src/snapshot/entity/batch.rs index 06f279bd3c5..ea700383dce 100644 --- a/apps/hash-graph/libs/graph/src/snapshot/entity/batch.rs +++ b/apps/hash-graph/libs/graph/src/snapshot/entity/batch.rs @@ -10,7 +10,7 @@ use graph_types::{ }; use hash_graph_store::filter::Filter; use tokio_postgres::GenericClient; -use type_system::schema::ClosedEntityType; +use type_system::schema::{ClosedEntityType, ClosedMultiEntityType}; use validation::{EntityPreprocessor, Validate, ValidateEntityComponents}; use crate::{ @@ -311,7 +311,7 @@ where ) .change_context(InsertionError)?; - let entity_type = ClosedEntityType::from_multi_type_closed_schema( + let entity_type = ClosedMultiEntityType::from_multi_type_closed_schema( stream::iter(&entity.metadata.entity_type_ids) .then(|entity_type_url| async { OntologyTypeProvider::::provide_type( diff --git a/apps/hash-graph/libs/graph/src/snapshot/ontology/entity_type/channel.rs b/apps/hash-graph/libs/graph/src/snapshot/ontology/entity_type/channel.rs index b273c6a74a5..e5b81249701 100644 --- a/apps/hash-graph/libs/graph/src/snapshot/ontology/entity_type/channel.rs +++ b/apps/hash-graph/libs/graph/src/snapshot/ontology/entity_type/channel.rs @@ -2,6 +2,7 @@ use core::{ pin::Pin, task::{Context, Poll, ready}, }; +use std::collections::{HashMap, HashSet}; use authorization::schema::EntityTypeRelationAndSubject; use error_stack::{Report, ResultExt}; @@ -12,7 +13,8 @@ use futures::{ }; use type_system::{ Valid, - schema::{ClosedEntityType, EntityTypeUuid}, + schema::{ClosedEntityType, EntityConstraints, EntityTypeUuid, InverseEntityTypeMetadata}, + url::{OntologyTypeVersion, VersionedUrl}, }; use crate::{ @@ -75,14 +77,32 @@ impl Sink for EntityTypeSender { self.schema .start_send_unpin(EntityTypeRow { ontology_id, - // TODO: Validate ontology types in snapshots - // see https://linear.app/hash/issue/H-3038 - schema: Valid::new_unchecked(entity_type.schema.clone()), // An empty schema is inserted initially. This will be replaced later by the closed // schema. // TODO: Validate ontology types in snapshots // see https://linear.app/hash/issue/H-3038 - closed_schema: Valid::new_unchecked(ClosedEntityType::default()), + closed_schema: Valid::new_unchecked(ClosedEntityType { + id: VersionedUrl { + base_url: entity_type.schema.id.base_url.clone(), + version: OntologyTypeVersion::new(0), + }, + title: String::new(), + title_plural: None, + description: None, + inverse: InverseEntityTypeMetadata { + title: None, + title_plural: None, + }, + constraints: EntityConstraints { + properties: HashMap::new(), + required: HashSet::new(), + links: HashMap::new(), + }, + all_of: Vec::new(), + }), + // TODO: Validate ontology types in snapshots + // see https://linear.app/hash/issue/H-3038 + schema: Valid::new_unchecked(entity_type.schema), }) .change_context(SnapshotRestoreError::Read) .attach_printable("could not send schema")?; diff --git a/apps/hash-graph/libs/graph/src/store/knowledge.rs b/apps/hash-graph/libs/graph/src/store/knowledge.rs index 6fad8c164d9..3a337eb8b43 100644 --- a/apps/hash-graph/libs/graph/src/store/knowledge.rs +++ b/apps/hash-graph/libs/graph/src/store/knowledge.rs @@ -24,7 +24,7 @@ use hash_graph_store::{ }; use serde::{Deserialize, Serialize}; use temporal_versioning::{DecisionTime, Timestamp, TransactionTime}; -use type_system::{schema::ClosedEntityType, url::VersionedUrl}; +use type_system::{schema::ClosedMultiEntityType, url::VersionedUrl}; #[cfg(feature = "utoipa")] use utoipa::{ ToSchema, @@ -42,7 +42,7 @@ use crate::store::{ pub enum EntityValidationType<'a> { Id(Cow<'a, HashSet>), #[serde(skip)] - ClosedSchema(Cow<'a, ClosedEntityType>), + ClosedSchema(Cow<'a, ClosedMultiEntityType>), } #[cfg(feature = "utoipa")] diff --git a/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/mod.rs b/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/mod.rs index 473daa13725..7b31aa6fbd1 100644 --- a/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/mod.rs +++ b/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/mod.rs @@ -58,7 +58,9 @@ use temporal_versioning::{ }; use tokio_postgres::{GenericClient, Row, error::SqlState}; use type_system::{ - schema::{ClosedEntityType, EntityTypeUuid, InheritanceDepth, OntologyTypeUuid}, + schema::{ + ClosedEntityType, ClosedMultiEntityType, EntityTypeUuid, InheritanceDepth, OntologyTypeUuid, + }, url::VersionedUrl, }; use uuid::Uuid; @@ -620,6 +622,7 @@ where let mut relationships = Vec::with_capacity(params.len()); let mut entity_type_ids = HashMap::new(); let mut checked_web_ids = HashSet::new(); + let mut entity_edition_ids = Vec::with_capacity(params.len()); let mut entity_id_rows = Vec::with_capacity(params.len()); let mut entity_draft_rows = Vec::new(); @@ -641,7 +644,7 @@ where }; for mut params in params { - let entity_type = ClosedEntityType::from_multi_type_closed_schema( + let entity_type = ClosedMultiEntityType::from_multi_type_closed_schema( stream::iter(¶ms.entity_type_ids) .then(|entity_type_url| async { OntologyTypeProvider::::provide_type( @@ -733,6 +736,7 @@ where provenance: entity_provenance.edition.clone(), property_metadata: property_metadata.clone(), }); + entity_edition_ids.push(entity_edition_id); let temporal_versioning = EntityTemporalMetadata { decision_time: LeftClosedTemporalInterval::new( @@ -753,15 +757,13 @@ where transaction_time: temporal_versioning.transaction_time, }); - for (entity_type_url, (depth, _)) in &entity_type.schemas { - let entity_type_id = EntityTypeUuid::from_url(entity_type_url); - if depth.inner() == 0 { - entity_type_ids.insert(entity_type_id, entity_type_url.clone()); - } + for entity_type in &entity_type.all_of { + let entity_type_id = EntityTypeUuid::from_url(&entity_type.id); + entity_type_ids.insert(entity_type_id, entity_type.id.clone()); entity_is_of_type_rows.push(EntityIsOfTypeRow { entity_edition_id, entity_type_ontology_id: entity_type_id, - inheritance_depth: *depth, + inheritance_depth: InheritanceDepth::new(0), }); } @@ -925,6 +927,25 @@ where .change_context(InsertionError)?; } + transaction + .as_client() + .query( + " + INSERT INTO entity_is_of_type + SELECT entity_edition_id, + target_entity_type_ontology_id AS entity_type_ontology_id, + MIN(entity_type_inherits_from.depth + 1) AS inheritance_depth + FROM entity_is_of_type + JOIN entity_type_inherits_from + ON entity_type_ontology_id = source_entity_type_ontology_id + WHERE entity_edition_id = ANY($1) + GROUP BY entity_edition_id, target_entity_type_ontology_id; + ", + &[&entity_edition_ids], + ) + .await + .change_context(InsertionError)?; + transaction .authorization_api .modify_entity_relations(relationships.iter().copied().map( @@ -1009,7 +1030,7 @@ where let schema = match params.entity_types { EntityValidationType::ClosedSchema(schema) => schema, EntityValidationType::Id(entity_type_urls) => Cow::Owned( - ClosedEntityType::from_multi_type_closed_schema( + ClosedMultiEntityType::from_multi_type_closed_schema( stream::iter(entity_type_urls.as_ref()) .then(|entity_type_url| async { OntologyTypeProvider::::provide_type( @@ -1027,7 +1048,7 @@ where ), }; - if schema.schemas.is_empty() { + if schema.all_of.is_empty() { let error = Report::new(validation::EntityValidationError::EmptyEntityTypes); status.append(error); }; @@ -1487,7 +1508,7 @@ where cache: StoreCache::default(), authorization: Some((actor_id, Consistency::FullyConsistent)), }; - let entity_type = ClosedEntityType::from_multi_type_closed_schema( + let entity_type = ClosedMultiEntityType::from_multi_type_closed_schema( stream::iter(&entity_type_ids) .then(|entity_type_url| async { OntologyTypeProvider::::provide_type( @@ -1564,10 +1585,7 @@ where let edition_id = transaction .insert_entity_edition( archived, - entity_type - .schemas - .iter() - .map(|(entity_type_id, (depth, _))| (entity_type_id, *depth)), + &entity_type_ids, &properties, params.confidence, &edition_provenance, @@ -1858,10 +1876,11 @@ where INSERT INTO entity_is_of_type SELECT entity_edition_id, target_entity_type_ontology_id AS entity_type_ontology_id, - entity_type_inherits_from.depth + 1 AS inheritance_depth + MIN(entity_type_inherits_from.depth + 1) AS inheritance_depth FROM entity_is_of_type JOIN entity_type_inherits_from - ON entity_type_ontology_id = source_entity_type_ontology_id; + ON entity_type_ontology_id = source_entity_type_ontology_id + GROUP BY entity_edition_id, target_entity_type_ontology_id; ", ) .await @@ -1890,7 +1909,7 @@ where async fn insert_entity_edition( &self, archived: bool, - entity_type_ids: impl IntoIterator + Send, + entity_type_ids: impl IntoIterator + Send, properties: &PropertyObject, confidence: Option, provenance: &EntityEditionProvenance, @@ -1916,15 +1935,10 @@ where .change_context(InsertionError)? .get(0); - let (entity_type_ontology_ids, inheritance_depths): (Vec<_>, Vec<_>) = entity_type_ids + let entity_type_ontology_ids = entity_type_ids .into_iter() - .map(|(entity_type_id, depth)| { - ( - OntologyTypeUuid::from(EntityTypeUuid::from_url(entity_type_id)), - depth, - ) - }) - .unzip(); + .map(|entity_type_id| OntologyTypeUuid::from(EntityTypeUuid::from_url(entity_type_id))) + .collect::>(); self.as_client() .query( @@ -1933,9 +1947,26 @@ where entity_edition_id, entity_type_ontology_id, inheritance_depth - ) SELECT $1, UNNEST($2::UUID[]), UNNEST($3::Integer[]); + ) SELECT $1, UNNEST($2::UUID[]), 0; + ", + &[&edition_id, &entity_type_ontology_ids], + ) + .await + .change_context(InsertionError)?; + self.as_client() + .query( + " + INSERT INTO entity_is_of_type + SELECT entity_edition_id, + target_entity_type_ontology_id AS entity_type_ontology_id, + MIN(entity_type_inherits_from.depth + 1) AS inheritance_depth + FROM entity_is_of_type + JOIN entity_type_inherits_from + ON entity_type_ontology_id = source_entity_type_ontology_id + WHERE entity_edition_id = $1 + GROUP BY entity_edition_id, target_entity_type_ontology_id; ", - &[&edition_id, &entity_type_ontology_ids, &inheritance_depths], + &[&edition_id], ) .await .change_context(InsertionError)?; diff --git a/apps/hash-graph/libs/graph/src/store/validation.rs b/apps/hash-graph/libs/graph/src/store/validation.rs index fafac6c148c..d5d13927912 100644 --- a/apps/hash-graph/libs/graph/src/store/validation.rs +++ b/apps/hash-graph/libs/graph/src/store/validation.rs @@ -491,31 +491,63 @@ where A: AuthorizationApi, { #[expect(refining_impl_trait)] - async fn is_parent_of( + async fn is_super_type_of( &self, + parent: &VersionedUrl, child: &VersionedUrl, - parent: &BaseUrl, ) -> Result> { let client = self.store.as_client().client(); let child_id = EntityTypeUuid::from_url(child); + let parent_id = EntityTypeUuid::from_url(parent); Ok(client .query_one( " SELECT EXISTS ( - SELECT 1 FROM closed_entity_type_inherits_from - JOIN ontology_ids - ON ontology_ids.ontology_id = target_entity_type_ontology_id + SELECT 1 FROM entity_type_inherits_from WHERE source_entity_type_ontology_id = $1 - AND ontology_ids.base_url = $2 + AND target_entity_type_ontology_id = $2 ); ", - &[child_id.as_uuid(), &parent.as_str()], + &[&child_id, &parent_id], ) .await .change_context(QueryError)? .get(0)) } + + #[expect(refining_impl_trait)] + async fn find_parents( + &self, + entity_types: &[VersionedUrl], + ) -> Result, Report> { + let entity_type_ids = entity_types + .iter() + .map(EntityTypeUuid::from_url) + .collect::>(); + + Ok(self + .store + .as_client() + .query( + " + SELECT base_url, version + FROM entity_type_inherits_from + JOIN ontology_ids ON target_entity_type_ontology_id = ontology_id + WHERE source_entity_type_ontology_id = ANY($1) + ORDER BY depth ASC; + ", + &[&entity_type_ids], + ) + .await + .change_context(QueryError)? + .into_iter() + .map(|row| VersionedUrl { + base_url: row.get(0), + version: row.get(1), + }) + .collect()) + } } impl EntityProvider for StoreProvider<'_, PostgresStore> diff --git a/apps/hash-graph/libs/store/src/entity_type/query.rs b/apps/hash-graph/libs/store/src/entity_type/query.rs index 324f4566be0..06141658871 100644 --- a/apps/hash-graph/libs/store/src/entity_type/query.rs +++ b/apps/hash-graph/libs/store/src/entity_type/query.rs @@ -147,7 +147,7 @@ pub enum EntityTypeQueryPath<'p> { /// /// [`EntityType::examples()`]: type_system::schema::EntityType::examples Examples, - /// Corresponds to [`EntityType::required()`]. + /// Corresponds to [`EntityConstraints::required`]. /// /// ```rust /// # use serde::Deserialize; @@ -158,7 +158,7 @@ pub enum EntityTypeQueryPath<'p> { /// # Ok::<(), serde_json::Error>(()) /// ``` /// - /// [`EntityType::required()`]: type_system::schema::EntityType::required + /// [`EntityConstraints::required`]: type_system::schema::EntityConstraints::required Required, /// The label property metadata of the entity type. /// diff --git a/libs/@blockprotocol/type-system/rust/src/schema/data_type/constraint/object.rs b/libs/@blockprotocol/type-system/rust/src/schema/data_type/constraint/object.rs index 8caf53b9192..2af594ced6d 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/data_type/constraint/object.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/data_type/constraint/object.rs @@ -13,7 +13,7 @@ use crate::schema::{ #[derive(Debug, Error)] pub enum ObjectValidationError {} -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))] #[serde(rename_all = "camelCase")] pub enum ObjectTypeTag { diff --git a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/closed.rs b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/closed.rs index 0df7fc8beaf..25ff86bcded 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/closed.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/closed.rs @@ -4,35 +4,60 @@ use std::collections::{HashMap, HashSet, hash_map::Entry}; use error_stack::{Report, ensure}; use itertools::Itertools as _; -use serde::{Deserialize, Serialize}; use serde_json::json; use thiserror::Error; use crate::{ schema::{ EntityType, EntityTypeReference, EntityTypeToPropertyTypeEdge, EntityTypeUuid, - InheritanceDepth, PropertyTypeReference, PropertyTypeUuid, PropertyValueArray, - ValueOrArray, entity_type::extend_links, one_of::OneOfSchema, + InheritanceDepth, InverseEntityTypeMetadata, PropertyTypeUuid, + entity_type::{EntityConstraints, EntityTypeDisplayMetadata, extend_links}, }, url::{BaseUrl, VersionedUrl}, }; -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))] #[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct ClosedEntityTypeSchemaData { +pub struct ClosedEntityTypeMetadata { + #[serde(rename = "$id")] + pub id: VersionedUrl, pub title: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub title_plural: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, + #[serde(default, skip_serializing_if = "InverseEntityTypeMetadata::is_empty")] + pub inverse: InverseEntityTypeMetadata, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[cfg_attr( + target_arch = "wasm32", + tsify(type = "[EntityTypeDisplayMetadata, ...EntityTypeDisplayMetadata[]]") + )] + pub all_of: Vec, } -#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ClosedEntityType { - pub schemas: HashMap, - pub properties: HashMap>, - #[serde(default, skip_serializing_if = "HashSet::is_empty")] - pub required: HashSet, - #[serde(default, skip_serializing_if = "HashMap::is_empty")] - pub links: HashMap>>>, + #[serde(rename = "$id")] + pub id: VersionedUrl, + pub title: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub title_plural: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(flatten)] + pub constraints: EntityConstraints, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[cfg_attr( + target_arch = "wasm32", + tsify(type = "[EntityTypeDisplayMetadata, ...EntityTypeDisplayMetadata[]]") + )] + pub all_of: Vec, + #[serde(default, skip_serializing_if = "InverseEntityTypeMetadata::is_empty")] + pub inverse: InverseEntityTypeMetadata, } #[derive(Debug, Error)] @@ -44,9 +69,76 @@ pub enum ResolveClosedEntityTypeError { UnknownSchemas(HashSet), #[error("Resolving the entity type encountered incompatible property: {0}.")] IncompatibleProperty(BaseUrl), + #[error("The entity type has an empty schema")] + EmptySchema, } impl ClosedEntityType { + /// Create a closed entity type from an entity type and its resolve data. + /// + /// # Errors + /// + /// Returns an error if the entity type references unknown schema in `allOf`. + pub fn from_resolve_data( + mut schema: EntityType, + resolve_data: &EntityTypeResolveData, + ) -> Result> { + let mut closed_schema = Self { + id: schema.id, + constraints: schema.constraints, + title: schema.title, + title_plural: schema.title_plural, + description: schema.description, + inverse: schema.inverse, + all_of: Vec::new(), + }; + + for (_depth, entity_type) in resolve_data.ordered_schemas() { + schema.all_of.remove((&entity_type.id).into()); + closed_schema + .constraints + .properties + .extend(entity_type.constraints.properties.clone()); + closed_schema + .constraints + .required + .extend(entity_type.constraints.required.clone()); + extend_links( + &mut closed_schema.constraints.links, + entity_type.constraints.links.clone(), + ); + if entity_type.icon.is_some() || entity_type.label_property.is_some() { + closed_schema.all_of.push(EntityTypeDisplayMetadata { + id: entity_type.id.clone(), + label_property: entity_type.label_property.clone(), + icon: entity_type.icon.clone(), + }); + } + } + + ensure!( + schema.all_of.is_empty(), + ResolveClosedEntityTypeError::UnknownSchemas(schema.all_of) + ); + + Ok(closed_schema) + } +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct ClosedMultiEntityType { + #[serde(flatten)] + pub constraints: EntityConstraints, + #[cfg_attr( + target_arch = "wasm32", + tsify(type = "[ClosedEntityTypeMetadata, ...ClosedEntityTypeMetadata[]]") + )] + pub all_of: Vec, +} + +impl ClosedMultiEntityType { /// Creates a closed entity type from multiple closed entity types. /// /// This results in a closed entity type which is used for entities with multiple types. @@ -55,16 +147,20 @@ impl ClosedEntityType { /// /// Returns an error if the entity types have incompatible properties. pub fn from_multi_type_closed_schema( - closed_schemas: impl IntoIterator, + closed_schemas: impl IntoIterator, ) -> Result> { - let mut properties = HashMap::new(); - let mut required = HashSet::new(); - let mut links = HashMap::new(); - let mut schemas = HashMap::new(); + let mut this = Self { + constraints: EntityConstraints { + properties: HashMap::new(), + required: HashSet::new(), + links: HashMap::new(), + }, + all_of: Vec::new(), + }; for schema in closed_schemas { - for (base_url, property) in schema.properties { - match properties.entry(base_url) { + for (base_url, property) in schema.constraints.properties { + match this.constraints.properties.entry(base_url) { Entry::Occupied(entry) => { ensure!( property == *entry.get(), @@ -76,80 +172,26 @@ impl ClosedEntityType { } } } - required.extend(schema.required); - extend_links(&mut links, schema.links); - - for (url, (depth, schema_data)) in schema.schemas { - match schemas.entry(url) { - Entry::Occupied(mut entry) => { - let (existing_depth, _) = entry.get_mut(); - *existing_depth = cmp::min(*existing_depth, depth); - } - Entry::Vacant(entry) => { - entry.insert((depth, schema_data)); - } - } - } - } - - Ok(Self { - schemas, - properties, - required, - links, - }) - } - - /// Create a closed entity type from an entity type and its resolve data. - /// - /// # Errors - /// - /// Returns an error if the entity type references unknown schemas in `allOf`. - pub fn from_resolve_data( - entity_type: EntityType, - resolve_data: &EntityTypeResolveData, - ) -> Result> { - let mut all_of = entity_type.all_of; - - let mut closed_schema = Self { - schemas: HashMap::from([( - entity_type.id, - (InheritanceDepth::new(0), ClosedEntityTypeSchemaData { - title: entity_type.title, - description: entity_type.description, - }), - )]), - properties: entity_type.properties, - required: entity_type.required, - links: entity_type.links, - }; - - for (depth, entity_type) in resolve_data.ordered_schemas() { - all_of.extend(entity_type.all_of.clone()); - closed_schema.schemas.insert( - entity_type.id.clone(), - ( - InheritanceDepth::new(depth.inner() + 1), - ClosedEntityTypeSchemaData { - title: entity_type.title.clone(), - description: entity_type.description.clone(), - }, - ), - ); - closed_schema - .properties - .extend(entity_type.properties.clone()); - closed_schema.required.extend(entity_type.required.clone()); - extend_links(&mut closed_schema.links, entity_type.links.clone()); - all_of.remove((&entity_type.id).into()); + this.constraints + .required + .extend(schema.constraints.required); + extend_links(&mut this.constraints.links, schema.constraints.links); + this.all_of.push(ClosedEntityTypeMetadata { + id: schema.id, + title: schema.title, + title_plural: schema.title_plural, + description: schema.description, + inverse: schema.inverse, + all_of: schema.all_of, + }); } ensure!( - all_of.is_empty(), - ResolveClosedEntityTypeError::UnknownSchemas(all_of) + !this.all_of.is_empty(), + ResolveClosedEntityTypeError::EmptySchema ); - Ok(closed_schema) + Ok(this) } } @@ -306,7 +348,7 @@ mod tests { use alloc::sync::Arc; use crate::{ - schema::{ClosedEntityType, EntityType, EntityTypeUuid, OntologyTypeResolver}, + schema::{EntityType, EntityTypeUuid, OntologyTypeResolver, entity_type::ClosedEntityType}, url::BaseUrl, utils::tests::{JsonEqualityCheck, ensure_serialization_from_str}, }; @@ -336,7 +378,7 @@ mod tests { .expect("Could not close church"); assert!( - closed_church.properties.contains_key( + closed_church.constraints.properties.contains_key( &BaseUrl::new( "https://blockprotocol.org/@alice/types/property-type/built-at/".to_owned() ) @@ -344,7 +386,7 @@ mod tests { ) ); assert!( - closed_church.properties.contains_key( + closed_church.constraints.properties.contains_key( &BaseUrl::new( "https://blockprotocol.org/@alice/types/property-type/number-bells/".to_owned() ) diff --git a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/constraints.rs b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/constraints.rs new file mode 100644 index 00000000000..8353eaf6e71 --- /dev/null +++ b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/constraints.rs @@ -0,0 +1,106 @@ +use std::collections::{HashMap, HashSet}; + +use crate::{ + schema::{ + EntityTypeReference, OneOfSchema, PropertyTypeReference, PropertyValueArray, ValueOrArray, + }, + url::{BaseUrl, VersionedUrl}, +}; + +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct EntityConstraints { + pub properties: HashMap>, + #[serde(default, skip_serializing_if = "HashSet::is_empty")] + #[cfg_attr(target_arch = "wasm32", tsify(type = "[BaseUrl, ...BaseUrl[]]"))] + pub required: HashSet, + #[cfg_attr( + target_arch = "wasm32", + tsify( + type = "Record | \ + Record>>" + ) + )] + #[serde(with = "links", default, skip_serializing_if = "HashMap::is_empty")] + pub links: HashMap>>>, +} + +mod links { + use core::fmt; + use std::collections::HashMap; + + use serde::{Deserialize, Serialize}; + + use crate::{ + schema::{EntityTypeReference, OneOfSchema, PropertyValueArray}, + url::VersionedUrl, + }; + + type Links = + HashMap>>>; + + // This struct is needed because it's used inside generic parameters of other structs like + // `Array`. Those structs can't apply serde's `default` or `skip_serializing_if` which means + // the option doesn't de/serialize as required unless wrapped in an intermediary struct. + #[derive(Serialize, Deserialize)] + struct Maybe { + #[serde(flatten, default = "None", skip_serializing_if = "Option::is_none")] + inner: Option, + } + + pub(super) fn serialize(links: &Links, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeMap; + + let mut map = serializer.serialize_map(Some(links.len()))?; + for (url, val) in links { + map.serialize_entry(&url, &PropertyValueArray { + items: Maybe { + inner: val.items.as_ref(), + }, + min_items: val.min_items, + max_items: val.max_items, + })?; + } + map.end() + } + + pub(super) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use serde::de::MapAccess; + + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = + HashMap>>>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a map") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut links = HashMap::new(); + while let Some((key, value)) = map.next_entry::>>>()? { + links.insert(key, PropertyValueArray { + items: value.items.inner, + min_items: value.min_items, + max_items: value.max_items, + }); + } + + Ok(links) + } + } + + deserializer.deserialize_map(Visitor) + } +} diff --git a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/mod.rs b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/mod.rs index 0bc27109e14..592ec905fe3 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/mod.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/mod.rs @@ -1,21 +1,27 @@ pub use self::{ - closed::{ClosedEntityType, ClosedEntityTypeSchemaData, EntityTypeResolveData}, + closed::{ + ClosedEntityType, ClosedEntityTypeMetadata, ClosedMultiEntityType, EntityTypeResolveData, + }, + constraints::EntityConstraints, reference::EntityTypeReference, validation::{EntityTypeValidationError, EntityTypeValidator}, }; mod closed; -mod raw; +mod constraints; mod reference; mod validation; use core::iter; use std::collections::{HashMap, HashSet, hash_map::Entry}; -use serde::{Deserialize, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; +use serde_json::Value as JsonValue; use crate::{ - schema::{PropertyTypeReference, PropertyValueArray, ValueOrArray, one_of::OneOfSchema}, + schema::{ + ObjectTypeTag, PropertyTypeReference, PropertyValueArray, ValueOrArray, one_of::OneOfSchema, + }, url::{BaseUrl, VersionedUrl}, }; @@ -36,35 +42,86 @@ impl InverseEntityTypeMetadata { } } -#[derive(Debug, Clone, Deserialize)] -#[serde(from = "raw::EntityType")] -pub struct EntityType { - pub id: VersionedUrl, +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))] +#[serde(rename_all = "camelCase")] +enum EntityTypeKindTag { + EntityType, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))] +#[serde(rename_all = "camelCase")] +enum EntityTypeSchemaTag { + #[serde(rename = "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type")] + V3, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct EntityTypeSchemaMetadata { pub title: String, + #[serde(default, skip_serializing_if = "Option::is_none")] pub title_plural: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, - pub properties: HashMap>, - pub required: HashSet, - pub all_of: HashSet, - pub links: HashMap>>>, + #[serde(default, skip_serializing_if = "InverseEntityTypeMetadata::is_empty")] pub inverse: InverseEntityTypeMetadata, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct EntityTypeDisplayMetadata { + #[serde(rename = "$id")] + pub id: VersionedUrl, #[serde(default, skip_serializing_if = "Option::is_none")] pub label_property: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub icon: Option, - #[deprecated] - pub examples: Vec>, } -impl Serialize for EntityType { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - raw::EntityType::from(self).serialize(serializer) +impl EntityTypeDisplayMetadata { + pub const fn is_empty(&self) -> bool { + self.label_property.is_none() && self.icon.is_none() } } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct EntityType { + #[serde(rename = "$schema")] + schema: EntityTypeSchemaTag, + kind: EntityTypeKindTag, + r#type: ObjectTypeTag, + #[serde(rename = "$id")] + pub id: VersionedUrl, + pub title: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub title_plural: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "InverseEntityTypeMetadata::is_empty")] + pub inverse: InverseEntityTypeMetadata, + #[serde(flatten)] + pub constraints: EntityConstraints, + #[serde(default, skip_serializing_if = "HashSet::is_empty")] + #[cfg_attr( + target_arch = "wasm32", + tsify(type = "[EntityTypeReference, ...EntityTypeReference[]]") + )] + pub all_of: HashSet, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub label_property: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub icon: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[deprecated] + pub examples: Vec>, +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum EntityTypeToEntityTypeEdge { Inheritance, @@ -84,7 +141,7 @@ impl EntityType { self.all_of .iter() .map(|reference| (reference, EntityTypeToEntityTypeEdge::Inheritance)) - .chain(self.links.iter().flat_map( + .chain(self.constraints.links.iter().flat_map( |(link_entity_type, destination_constraint_entity_types)| { iter::once((link_entity_type.into(), EntityTypeToEntityTypeEdge::Link)).chain( destination_constraint_entity_types @@ -103,7 +160,7 @@ impl EntityType { pub fn property_type_references( &self, ) -> impl Iterator { - self.properties.values().map(|property_def| { + self.constraints.properties.values().map(|property_def| { ( match property_def { ValueOrArray::Value(url) => url, @@ -117,9 +174,8 @@ impl EntityType { pub fn link_mappings( &self, ) -> impl Iterator)> { - self.links - .iter() - .map(|(link_entity_type, destination_constraint_entity_types)| { + self.constraints.links.iter().map( + |(link_entity_type, destination_constraint_entity_types)| { ( <&EntityTypeReference>::from(link_entity_type), destination_constraint_entity_types @@ -127,7 +183,8 @@ impl EntityType { .as_ref() .map(|one_of| one_of.possibilities.as_slice()), ) - }) + }, + ) } } diff --git a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/raw.rs b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/raw.rs deleted file mode 100644 index b44f158630c..00000000000 --- a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/raw.rs +++ /dev/null @@ -1,201 +0,0 @@ -use alloc::borrow::Cow; -use std::collections::{HashMap, HashSet}; - -use serde::{Deserialize, Serialize}; -use serde_json::Value as JsonValue; -#[cfg(target_arch = "wasm32")] -use tsify::Tsify; - -use crate::{ - schema::{ - EntityTypeReference, OneOfSchema, PropertyTypeReference, PropertyValueArray, ValueOrArray, - entity_type::InverseEntityTypeMetadata, - }, - url::{BaseUrl, VersionedUrl}, -}; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(target_arch = "wasm32", derive(Tsify))] -#[serde(rename_all = "camelCase")] -enum EntityTypeKindTag { - EntityType, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(target_arch = "wasm32", derive(Tsify))] -#[serde(rename_all = "camelCase")] -enum EntityTypeTag { - Object, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(target_arch = "wasm32", derive(Tsify))] -#[serde(rename_all = "camelCase")] -enum EntityTypeSchemaTag { - #[serde(rename = "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type")] - V3, -} - -type Links = HashMap>>>; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(target_arch = "wasm32", derive(Tsify))] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct EntityType<'a> { - #[serde(rename = "$schema")] - schema: EntityTypeSchemaTag, - kind: EntityTypeKindTag, - r#type: EntityTypeTag, - #[serde(rename = "$id")] - id: Cow<'a, VersionedUrl>, - title: Cow<'a, str>, - #[serde(default, skip_serializing_if = "Option::is_none")] - title_plural: Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] - description: Option>, - #[serde(default, skip_serializing_if = "HashSet::is_empty")] - #[cfg_attr( - target_arch = "wasm32", - tsify(type = "[EntityTypeReference, ...EntityTypeReference[]]") - )] - all_of: Cow<'a, HashSet>, - properties: Cow<'a, HashMap>>, - #[serde(default, skip_serializing_if = "HashSet::is_empty")] - #[cfg_attr(target_arch = "wasm32", tsify(type = "[BaseUrl, ...BaseUrl[]]"))] - required: Cow<'a, HashSet>, - #[cfg_attr( - target_arch = "wasm32", - tsify( - type = "Record | \ - Record>>" - ) - )] - #[serde(with = "links", default, skip_serializing_if = "HashMap::is_empty")] - links: Links, - #[serde(default, skip_serializing_if = "InverseEntityTypeMetadata::is_empty")] - inverse: InverseEntityTypeMetadata, - #[serde(default, skip_serializing_if = "Option::is_none")] - label_property: Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] - icon: Option>, - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - examples: Cow<'a, [HashMap]>, -} - -mod links { - use core::fmt; - use std::collections::HashMap; - - use serde::{Deserialize, Serialize}; - - use crate::{ - schema::{EntityTypeReference, OneOfSchema, PropertyValueArray, entity_type::raw::Links}, - url::VersionedUrl, - }; - - // This struct is needed because it's used inside generic parameters of other structs like - // `Array`. Those structs can't apply serde's `default` or `skip_serializing_if` which means - // the option doesn't de/serialize as required unless wrapped in an intermediary struct. - #[derive(Serialize, Deserialize)] - struct Maybe { - #[serde(flatten, default = "None", skip_serializing_if = "Option::is_none")] - inner: Option, - } - - pub(super) fn serialize(links: &Links, serializer: S) -> Result - where - S: serde::Serializer, - { - use serde::ser::SerializeMap; - - let mut map = serializer.serialize_map(Some(links.len()))?; - for (url, val) in links { - map.serialize_entry(&url, &PropertyValueArray { - items: Maybe { - inner: val.items.as_ref(), - }, - min_items: val.min_items, - max_items: val.max_items, - })?; - } - map.end() - } - - pub(super) fn deserialize<'de, D>(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - use serde::de::MapAccess; - - struct Visitor; - - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = Links; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a map") - } - - fn visit_map(self, mut map: A) -> Result - where - A: MapAccess<'de>, - { - let mut links = HashMap::new(); - while let Some((key, value)) = map.next_entry::>>>()? { - links.insert(key, PropertyValueArray { - items: value.items.inner, - min_items: value.min_items, - max_items: value.max_items, - }); - } - - Ok(links) - } - } - - deserializer.deserialize_map(Visitor) - } -} - -impl From> for super::EntityType { - fn from(entity_type: EntityType<'_>) -> Self { - #[expect(deprecated)] - Self { - id: entity_type.id.into_owned(), - title: entity_type.title.into_owned(), - title_plural: entity_type.title_plural.map(Cow::into_owned), - description: entity_type.description.map(Cow::into_owned), - properties: entity_type.properties.into_owned(), - required: entity_type.required.into_owned(), - all_of: entity_type.all_of.into_owned(), - links: entity_type.links, - inverse: entity_type.inverse, - label_property: entity_type.label_property.map(Cow::into_owned), - icon: entity_type.icon.map(Cow::into_owned), - examples: entity_type.examples.into_owned(), - } - } -} - -impl<'a> From<&'a super::EntityType> for EntityType<'a> { - fn from(entity_type: &'a super::EntityType) -> Self { - Self { - schema: EntityTypeSchemaTag::V3, - kind: EntityTypeKindTag::EntityType, - r#type: EntityTypeTag::Object, - id: Cow::Borrowed(&entity_type.id), - title: Cow::Borrowed(&entity_type.title), - title_plural: entity_type.title_plural.as_deref().map(Cow::Borrowed), - description: entity_type.description.as_deref().map(Cow::Borrowed), - properties: Cow::Borrowed(&entity_type.properties), - required: Cow::Borrowed(&entity_type.required), - all_of: Cow::Borrowed(&entity_type.all_of), - links: entity_type.links.clone(), - inverse: entity_type.inverse.clone(), - label_property: entity_type.label_property.as_ref().map(Cow::Borrowed), - icon: entity_type.icon.as_deref().map(Cow::Borrowed), - #[expect(deprecated)] - examples: Cow::Borrowed(&entity_type.examples), - } - } -} diff --git a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/validation.rs b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/validation.rs index ccb2dd88dc9..82373364b42 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/validation.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/validation.rs @@ -31,7 +31,7 @@ impl Validator for EntityTypeValidator { ) -> Result<&'v Valid, Self::Error> { ObjectSchemaValidator.validate_ref(value).await?; - for (property, value) in &value.properties { + for (property, value) in &value.constraints.properties { let reference = match value { ValueOrArray::Value(value) => value, ValueOrArray::Array(array) => &array.items, @@ -59,7 +59,7 @@ impl Validator for EntityTypeValidator { ) -> Result<&'v Valid, Self::Error> { ObjectSchemaValidator.validate_ref(value).await?; - for (property, value) in &value.properties { + for (property, value) in &value.constraints.properties { let reference = match value { ValueOrArray::Value(value) => value, ValueOrArray::Array(array) => &array.items, diff --git a/libs/@blockprotocol/type-system/rust/src/schema/mod.rs b/libs/@blockprotocol/type-system/rust/src/schema/mod.rs index 5ad95b73187..e676536fcc6 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/mod.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/mod.rs @@ -32,9 +32,10 @@ pub use self::{ ValueSchemaMetadata, Variable, }, entity_type::{ - ClosedEntityType, ClosedEntityTypeSchemaData, EntityType, EntityTypeReference, - EntityTypeResolveData, EntityTypeToEntityTypeEdge, EntityTypeToPropertyTypeEdge, - EntityTypeValidationError, EntityTypeValidator, + ClosedEntityType, ClosedEntityTypeMetadata, ClosedMultiEntityType, EntityConstraints, + EntityType, EntityTypeReference, EntityTypeResolveData, EntityTypeSchemaMetadata, + EntityTypeToEntityTypeEdge, EntityTypeToPropertyTypeEdge, EntityTypeValidationError, + EntityTypeValidator, InverseEntityTypeMetadata, }, identifier::{DataTypeUuid, EntityTypeUuid, OntologyTypeUuid, PropertyTypeUuid}, object::{ diff --git a/libs/@blockprotocol/type-system/rust/src/schema/object/mod.rs b/libs/@blockprotocol/type-system/rust/src/schema/object/mod.rs index 4b4128d8911..284feec9db7 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/object/mod.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/object/mod.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize, Serializer}; pub use self::validation::{ObjectSchemaValidationError, ObjectSchemaValidator}; use crate::{ - schema::{ClosedEntityType, EntityType, PropertyTypeReference, ValueOrArray}, + schema::{ClosedMultiEntityType, EntityType, PropertyTypeReference, ValueOrArray}, url::BaseUrl, }; @@ -53,23 +53,23 @@ impl PropertyObjectSchema for EntityType { type Value = ValueOrArray; fn properties(&self) -> &HashMap { - &self.properties + &self.constraints.properties } fn required(&self) -> &HashSet { - &self.required + &self.constraints.required } } -impl PropertyObjectSchema for ClosedEntityType { +impl PropertyObjectSchema for ClosedMultiEntityType { type Value = ValueOrArray; fn properties(&self) -> &HashMap { - &self.properties + &self.constraints.properties } fn required(&self) -> &HashSet { - &self.required + &self.constraints.required } } diff --git a/libs/@blockprotocol/type-system/rust/src/schema/object/validation.rs b/libs/@blockprotocol/type-system/rust/src/schema/object/validation.rs index 07ed8e1993c..c6b6e95c8be 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/object/validation.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/object/validation.rs @@ -60,8 +60,8 @@ impl Validator for ObjectSchemaValidator { value: &'v EntityType, ) -> Result<&'v Valid, Self::Error> { ObjectSchemaRef { - properties: &value.properties, - required: &value.required, + properties: &value.constraints.properties, + required: &value.constraints.required, } .validate()?; @@ -77,8 +77,8 @@ impl Validator for ObjectSchemaValidator { value: &'v ClosedEntityType, ) -> Result<&'v Valid, Self::Error> { ObjectSchemaRef { - properties: &value.properties, - required: &value.required, + properties: &value.constraints.properties, + required: &value.constraints.required, } .validate()?; diff --git a/libs/@local/hash-graph-types/rust/src/knowledge/property/error.rs b/libs/@local/hash-graph-types/rust/src/knowledge/property/error.rs index 3cca577c4c1..0c242b8ba4f 100644 --- a/libs/@local/hash-graph-types/rust/src/knowledge/property/error.rs +++ b/libs/@local/hash-graph-types/rust/src/knowledge/property/error.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use error_stack::Report; use serde_json::Value as JsonValue; use type_system::{ - schema::{ClosedEntityType, DataType, PropertyType}, + schema::{ClosedMultiEntityType, DataType, PropertyType}, url::VersionedUrl, }; @@ -27,9 +27,11 @@ pub fn install_error_stack_hooks() { struct AttachedSchemas(HashSet); let ids = match expected { - Expected::EntityType(entity_type) => { - entity_type.schemas.keys().cloned().collect::>() - } + Expected::EntityType(entity_type) => entity_type + .all_of + .iter() + .map(|entity_type| entity_type.id.clone()) + .collect::>(), Expected::PropertyType(property_type) => vec![property_type.id.clone()], Expected::DataType(data_type) => vec![data_type.id.clone()], }; @@ -74,7 +76,7 @@ pub enum Actual { #[derive(Debug)] pub enum Expected { - EntityType(Box), + EntityType(Box), PropertyType(PropertyType), DataType(DataType), } diff --git a/libs/@local/hash-graph-types/rust/src/ontology/mod.rs b/libs/@local/hash-graph-types/rust/src/ontology/mod.rs index 4929ff84672..aa44429590a 100644 --- a/libs/@local/hash-graph-types/rust/src/ontology/mod.rs +++ b/libs/@local/hash-graph-types/rust/src/ontology/mod.rs @@ -191,10 +191,21 @@ pub trait DataTypeProvider: OntologyTypeProvider { pub trait PropertyTypeProvider: OntologyTypeProvider {} +pub enum EntityTypeVariance { + Covariant, + Contravariant, + Invariant, +} + pub trait EntityTypeProvider: OntologyTypeProvider { - fn is_parent_of( + fn is_super_type_of( &self, + parent: &VersionedUrl, child: &VersionedUrl, - parent: &BaseUrl, ) -> impl Future>> + Send; + + fn find_parents( + &self, + entity_types: &[VersionedUrl], + ) -> impl Future, Report>> + Send; } diff --git a/libs/@local/hash-validation/src/entity_type.rs b/libs/@local/hash-validation/src/entity_type.rs index 90b73045dc5..78f1395afd4 100644 --- a/libs/@local/hash-validation/src/entity_type.rs +++ b/libs/@local/hash-validation/src/entity_type.rs @@ -25,9 +25,9 @@ use serde_json::Value as JsonValue; use thiserror::Error; use type_system::{ schema::{ - ClosedEntityType, ConstraintValidator, DataTypeReference, JsonSchemaValueType, - PropertyObjectSchema, PropertyType, PropertyTypeReference, PropertyValueArray, - PropertyValueSchema, PropertyValues, ValueOrArray, + ClosedEntityType, ClosedMultiEntityType, ConstraintValidator, DataTypeReference, + JsonSchemaValueType, PropertyObjectSchema, PropertyType, PropertyTypeReference, + PropertyValueArray, PropertyValueSchema, PropertyValues, ValueOrArray, }, url::{BaseUrl, OntologyTypeVersion, VersionedUrl}, }; @@ -56,7 +56,7 @@ pub enum EntityValidationError { InvalidPropertyPath { path: PropertyPath<'static> }, } -impl

Validate for Option<&LinkData> +impl

Validate for Option<&LinkData> where P: EntityProvider + EntityTypeProvider @@ -68,7 +68,7 @@ where async fn validate( &self, - schema: &ClosedEntityType, + schema: &ClosedMultiEntityType, components: ValidateEntityComponents, context: &P, ) -> Result<(), Report<[Self::Error]>> { @@ -88,7 +88,20 @@ where .expect("Not a valid URL"), version: OntologyTypeVersion::new(1), }; - let is_link = schema.schemas.contains_key(&link_type_id); + + let mut is_link = false; + for entity_type in &schema.all_of { + if context + .is_super_type_of(&link_type_id, &entity_type.id) + .await + .change_context_lazy(|| EntityValidationError::EntityTypeRetrieval { + ids: HashSet::from([entity_type.id.clone()]), + })? + { + is_link = true; + break; + } + } if let Some(link_data) = self { if !is_link { @@ -106,7 +119,7 @@ where } } -impl

Validate for Entity +impl

Validate for Entity where P: EntityProvider + EntityTypeProvider @@ -118,7 +131,7 @@ where async fn validate( &self, - schema: &ClosedEntityType, + schema: &ClosedMultiEntityType, components: ValidateEntityComponents, context: &P, ) -> Result<(), Report<[Self::Error]>> { @@ -149,7 +162,7 @@ where } } -impl

Schema for ClosedEntityType +impl

Schema for ClosedMultiEntityType where P: EntityProvider + EntityTypeProvider + Sync, { @@ -157,6 +170,8 @@ where // TODO: validate link data // see https://linear.app/hash/issue/H-972 + // TODO: Optimize reading of left/right parent types and/or cache them + #[expect(clippy::too_many_lines)] async fn validate_value<'a>( &'a self, value: &'a LinkData, @@ -180,7 +195,7 @@ where .await .map(|entity_type| entity_type.borrow().clone()) }) - .try_collect::>() + .try_collect::>() .await .change_context_lazy(|| EntityValidationError::EntityRetrieval { id: value.left_entity_id, @@ -205,7 +220,7 @@ where .await .map(|entity_type| entity_type.borrow().clone()) }) - .try_collect::>() + .try_collect::>() .await .change_context_lazy(|| EntityValidationError::EntityRetrieval { id: value.right_entity_id, @@ -218,8 +233,20 @@ where // We track that at least one link type was found to avoid reporting an error if no // link type was found. let mut found_link_target = false; - for link_type_id in self.schemas.keys() { - let Some(maybe_allowed_targets) = left_entity_type.links.get(link_type_id) else { + let entity_type_ids = self + .all_of + .iter() + .map(|entity_type| entity_type.id.clone()) + .collect::>(); + let parent_entity_type_ids = provider + .find_parents(&entity_type_ids) + .await + .change_context_lazy(|| EntityValidationError::EntityTypeRetrieval { + ids: entity_type_ids.iter().cloned().collect(), + })?; + for link_type_id in entity_type_ids.into_iter().chain(parent_entity_type_ids) { + let Some(maybe_allowed_targets) = left_entity_type.constraints.links.get(&link_type_id) + else { continue; }; @@ -227,32 +254,58 @@ where found_link_target = true; let Some(allowed_targets) = &maybe_allowed_targets.items else { - continue; + // For a given target there was an unconstrained link destination, so we can + // skip the rest of the checks + break; }; // Link destinations are constrained, search for the right entity's type let mut found_match = false; - for allowed_target in &allowed_targets.possibilities { + 'targets: for allowed_target in &allowed_targets.possibilities { if right_entity_type - .schemas - .keys() - .any(|right_type| right_type.base_url == allowed_target.url.base_url) + .all_of + .iter() + .any(|entity_type| entity_type.id.base_url == allowed_target.url.base_url) { found_match = true; break; } + for right_entity_type in &right_entity_type.all_of { + if provider + .is_super_type_of(&allowed_target.url, &right_entity_type.id) + .await + .change_context_lazy(|| EntityValidationError::EntityTypeRetrieval { + ids: HashSet::from([ + right_entity_type.id.clone(), + allowed_target.url.clone(), + ]), + })? + { + found_match = true; + break 'targets; + } + } } - if !found_match { - status.capture(EntityValidationError::InvalidLinkTargetId { - target_types: right_entity_type.schemas.keys().cloned().collect(), - }); + if found_match { + break; } + status.capture(EntityValidationError::InvalidLinkTargetId { + target_types: right_entity_type + .all_of + .iter() + .map(|entity_type| entity_type.id.clone()) + .collect(), + }); } if !found_link_target { status.capture(EntityValidationError::InvalidLinkTypeId { - link_types: self.schemas.keys().cloned().collect(), + link_types: self + .all_of + .iter() + .map(|entity_type| entity_type.id.clone()) + .collect(), }); } diff --git a/libs/@local/hash-validation/src/lib.rs b/libs/@local/hash-validation/src/lib.rs index b5e53f16e7b..2c43c2969d6 100644 --- a/libs/@local/hash-validation/src/lib.rs +++ b/libs/@local/hash-validation/src/lib.rs @@ -93,6 +93,7 @@ pub trait EntityProvider { #[cfg(test)] mod tests { use alloc::sync::Arc; + use core::iter; use std::collections::HashMap; use graph_types::{ @@ -116,8 +117,8 @@ mod tests { use thiserror::Error; use type_system::{ schema::{ - ClosedEntityType, ConversionExpression, DataType, EntityType, EntityTypeUuid, - OntologyTypeResolver, PropertyType, + ClosedEntityType, ClosedMultiEntityType, ConversionExpression, DataType, EntityType, + EntityTypeUuid, OntologyTypeResolver, PropertyType, }, url::{BaseUrl, VersionedUrl}, }; @@ -161,7 +162,7 @@ mod tests { impl Provider { fn new( entities: impl IntoIterator, - entity_types: impl IntoIterator, + entity_types: impl IntoIterator, property_types: impl IntoIterator, data_types: impl IntoIterator, ) -> Self { @@ -172,7 +173,7 @@ mod tests { .collect(), entity_types: entity_types .into_iter() - .map(|(url, schema)| (url, Arc::new(schema))) + .map(|schema| (schema.id.clone(), Arc::new(schema))) .collect(), property_types: property_types .into_iter() @@ -228,18 +229,22 @@ mod tests { impl EntityTypeProvider for Provider { #[expect(refining_impl_trait)] - async fn is_parent_of( + async fn is_super_type_of( &self, - child: &VersionedUrl, - parent: &BaseUrl, + _: &VersionedUrl, + _: &VersionedUrl, ) -> Result> { - Ok( - OntologyTypeProvider::::provide_type(self, child) - .await? - .schemas - .keys() - .any(|id| id.base_url == *parent), - ) + // Not used in tests + Ok(false) + } + + #[expect(refining_impl_trait)] + async fn find_parents( + &self, + _: &[VersionedUrl], + ) -> Result, Report> { + // Not used in tests + Ok(Vec::new()) } } @@ -361,6 +366,9 @@ mod tests { .expect("entity type not resolved"); let closed_entity_type = ClosedEntityType::from_resolve_data(entity_type, &resolved_data) .expect("Could not close entity type"); + let closed_multi_entity_type = + ClosedMultiEntityType::from_multi_type_closed_schema(iter::once(closed_entity_type)) + .expect("Could not close multi entity type"); let entity_types = entity_types .into_iter() @@ -368,11 +376,8 @@ mod tests { let resolved_data = ontology_type_resolver .resolve_entity_type_metadata(entity_type_uuid) .expect("entity type not resolved"); - let entity_type_id = entity_type.id.clone(); - let closed_entity_type = - ClosedEntityType::from_resolve_data(entity_type, &resolved_data) - .expect("Could not close church"); - (entity_type_id, closed_entity_type) + ClosedEntityType::from_resolve_data(entity_type, &resolved_data) + .expect("Could not close entity type") }) .collect::>(); @@ -394,7 +399,7 @@ mod tests { .expect("failed to create property with metadata"); EntityPreprocessor { components } - .visit_object(&closed_entity_type, &mut properties, &provider) + .visit_object(&closed_multi_entity_type, &mut properties, &provider) .await?; Ok(properties) diff --git a/tests/hash-graph-http/test.sh b/tests/hash-graph-http/test.sh index 1ad2ed7811d..2c4095333cd 100755 --- a/tests/hash-graph-http/test.sh +++ b/tests/hash-graph-http/test.sh @@ -9,3 +9,5 @@ yarn httpyac send --all tests/circular-links.http yarn reset-database yarn httpyac send --all tests/ambiguous.http yarn reset-database +yarn httpyac send --all tests/link-inheritance.http +yarn reset-database diff --git a/tests/hash-graph-http/tests/link-inheritance.http b/tests/hash-graph-http/tests/link-inheritance.http new file mode 100644 index 00000000000..fb9082ae9f6 --- /dev/null +++ b/tests/hash-graph-http/tests/link-inheritance.http @@ -0,0 +1,1144 @@ +# This file either runs with JetBrains' http requests or using httpYac (https://httpyac.github.io). + +### Create account +POST http://127.0.0.1:4000/accounts +Content-Type: application/json +X-Authenticated-User-Actor-Id: 00000000-0000-0000-0000-000000000000 + +{} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.global.set("account_id", response.body.toString()); +%} + +### Create account web +POST http://127.0.0.1:4000/webs +Content-Type: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "owner": { + "kind": "account", + "subjectId": "{{account_id}}" + } +} + +> {% + client.test("status", function() { + client.assert(response.status === 204, "Response status is not 204"); + }); +%} + +### Insert link entity type +POST http://127.0.0.1:4000/entity-types +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "schema": [{ + "$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type", + "kind": "entityType", + "$id": "http://localhost:3000/@snapshot/types/entity-type/left1/v/1", + "type": "object", + "title": "Object", + "properties": {}, + "links": { + "http://localhost:3000/@snapshot/types/entity-type/link1/v/1": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "http://localhost:3000/@snapshot/types/entity-type/right1/v/1" + } + ] + } + } + } + }, { + "$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type", + "kind": "entityType", + "$id": "http://localhost:3000/@snapshot/types/entity-type/left2/v/1", + "type": "object", + "title": "Object", + "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/left1/v/1" }], + "properties": {}, + "links": { + "http://localhost:3000/@snapshot/types/entity-type/link2/v/1": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "http://localhost:3000/@snapshot/types/entity-type/right2/v/1" + } + ] + } + } + } + }, { + "$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type", + "kind": "entityType", + "$id": "http://localhost:3000/@snapshot/types/entity-type/left3/v/1", + "type": "object", + "title": "Object", + "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/left2/v/1" }], + "properties": {}, + "links": { + "http://localhost:3000/@snapshot/types/entity-type/link3/v/1": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "http://localhost:3000/@snapshot/types/entity-type/right3/v/1" + } + ] + } + } + } + }, + { + "$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type", + "kind": "entityType", + "$id": "http://localhost:3000/@snapshot/types/entity-type/link1/v/1", + "type": "object", + "title": "Object", + "allOf": [{ "$ref": "https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1" }], + "properties": {} + }, { + "$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type", + "kind": "entityType", + "$id": "http://localhost:3000/@snapshot/types/entity-type/link2/v/1", + "type": "object", + "title": "Object", + "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/link1/v/1" }], + "properties": {} + }, { + "$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type", + "kind": "entityType", + "$id": "http://localhost:3000/@snapshot/types/entity-type/link3/v/1", + "type": "object", + "title": "Object", + "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/link2/v/1" }], + "properties": {} + }, + { + "$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type", + "kind": "entityType", + "$id": "http://localhost:3000/@snapshot/types/entity-type/right1/v/1", + "type": "object", + "title": "Object", + "properties": {} + }, { + "$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type", + "kind": "entityType", + "$id": "http://localhost:3000/@snapshot/types/entity-type/right2/v/1", + "type": "object", + "title": "Object", + "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/right1/v/1" }], + "properties": {} + }, { + "$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type", + "kind": "entityType", + "$id": "http://localhost:3000/@snapshot/types/entity-type/right3/v/1", + "type": "object", + "title": "Object", + "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/right2/v/1" }], + "properties": {} + }], + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "updateFromWeb" + } + },{ + "relation": "instantiator", + "subject": { + "kind": "account", + "subjectId": "{{account_id}}" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert entity Left1 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/left1/v/1"], + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.global.set("left1", response.body.metadata.recordId.entityId); +%} + +### Insert entity Left2 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/left2/v/1"], + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.global.set("left2", response.body.metadata.recordId.entityId); +%} + +### Insert entity Left3 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/left3/v/1"], + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.global.set("left3", response.body.metadata.recordId.entityId); +%} + +### Insert entity Right1 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/right1/v/1"], + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.global.set("right1", response.body.metadata.recordId.entityId); +%} + +### Insert entity Right2 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/right2/v/1"], + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.global.set("right2", response.body.metadata.recordId.entityId); +%} + +### Insert entity Right3 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/right3/v/1"], + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.global.set("right3", response.body.metadata.recordId.entityId); +%} + +### Insert link1 between left1 and right1 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link1/v/1"], + "linkData": { + "leftEntityId": "{{left1}}", + "rightEntityId": "{{right1}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link1 between left1 and right2 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link1/v/1"], + "linkData": { + "leftEntityId": "{{left1}}", + "rightEntityId": "{{right2}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link1 between left1 and right3 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link1/v/1"], + "linkData": { + "leftEntityId": "{{left1}}", + "rightEntityId": "{{right3}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link1 between left2 and right1 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link1/v/1"], + "linkData": { + "leftEntityId": "{{left2}}", + "rightEntityId": "{{right1}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link1 between left2 and right2 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link1/v/1"], + "linkData": { + "leftEntityId": "{{left2}}", + "rightEntityId": "{{right2}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link1 between left2 and right3 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link1/v/1"], + "linkData": { + "leftEntityId": "{{left2}}", + "rightEntityId": "{{right3}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link1 between left3 and right1 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link1/v/1"], + "linkData": { + "leftEntityId": "{{left3}}", + "rightEntityId": "{{right1}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link1 between left3 and right2 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link1/v/1"], + "linkData": { + "leftEntityId": "{{left3}}", + "rightEntityId": "{{right2}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link1 between left3 and right3 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link1/v/1"], + "linkData": { + "leftEntityId": "{{left3}}", + "rightEntityId": "{{right3}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link2 between left1 and right1 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link2/v/1"], + "linkData": { + "leftEntityId": "{{left1}}", + "rightEntityId": "{{right1}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link2 between left1 and right2 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link2/v/1"], + "linkData": { + "leftEntityId": "{{left1}}", + "rightEntityId": "{{right2}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link2 between left1 and right3 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link2/v/1"], + "linkData": { + "leftEntityId": "{{left1}}", + "rightEntityId": "{{right3}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link2 between left2 and right1 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link2/v/1"], + "linkData": { + "leftEntityId": "{{left2}}", + "rightEntityId": "{{right1}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 500, "Response status is not 200"); + }); +%} + +### Insert link2 between left2 and right2 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link2/v/1"], + "linkData": { + "leftEntityId": "{{left2}}", + "rightEntityId": "{{right2}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link2 between left2 and right3 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link2/v/1"], + "linkData": { + "leftEntityId": "{{left2}}", + "rightEntityId": "{{right3}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link2 between left3 and right1 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link2/v/1"], + "linkData": { + "leftEntityId": "{{left3}}", + "rightEntityId": "{{right1}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 500, "Response status is not 200"); + }); +%} + +### Insert link2 between left3 and right2 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link2/v/1"], + "linkData": { + "leftEntityId": "{{left3}}", + "rightEntityId": "{{right2}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link2 between left3 and right3 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link2/v/1"], + "linkData": { + "leftEntityId": "{{left3}}", + "rightEntityId": "{{right3}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link3 between left1 and right1 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link3/v/1"], + "linkData": { + "leftEntityId": "{{left1}}", + "rightEntityId": "{{right1}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link3 between left1 and right2 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link3/v/1"], + "linkData": { + "leftEntityId": "{{left1}}", + "rightEntityId": "{{right2}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link3 between left1 and right3 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link3/v/1"], + "linkData": { + "leftEntityId": "{{left1}}", + "rightEntityId": "{{right3}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link3 between left2 and right1 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link3/v/1"], + "linkData": { + "leftEntityId": "{{left2}}", + "rightEntityId": "{{right1}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 500, "Response status is not 200"); + }); +%} + +### Insert link3 between left2 and right2 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link3/v/1"], + "linkData": { + "leftEntityId": "{{left2}}", + "rightEntityId": "{{right2}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link3 between left2 and right3 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link3/v/1"], + "linkData": { + "leftEntityId": "{{left2}}", + "rightEntityId": "{{right3}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### Insert link3 between left3 and right1 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link3/v/1"], + "linkData": { + "leftEntityId": "{{left3}}", + "rightEntityId": "{{right1}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 500, "Response status is not 200"); + }); +%} + +### Insert link3 between left3 and right2 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link3/v/1"], + "linkData": { + "leftEntityId": "{{left3}}", + "rightEntityId": "{{right2}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 500, "Response status is not 200"); + }); +%} + +### Insert link3 between left3 and right3 +POST http://127.0.0.1:4000/entities +Content-Type: application/json +Accept: application/json +X-Authenticated-User-Actor-Id: {{account_id}} + +{ + "ownedById": "{{account_id}}", + "properties": {}, + "entityTypeIds": ["http://localhost:3000/@snapshot/types/entity-type/link3/v/1"], + "linkData": { + "leftEntityId": "{{left3}}", + "rightEntityId": "{{right3}}" + }, + "draft": false, + "relationships": [{ + "relation": "setting", + "subject": { + "kind": "setting", + "subjectId": "administratorFromWeb" + } + }] +} + +> {% + client.test("status", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} From edb4b27a7b0a1736106f8a6866765a2440878bed Mon Sep 17 00:00:00 2001 From: Tim Diekmann <21277928+TimDiekmann@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:39:38 +0100 Subject: [PATCH 5/6] H-3531: Properly run `lint:tsc` for eslint-config (#5517) --- .../@local/eslint-config/generate-ignore-patterns.cjs | 11 ++++++++--- libs/@local/eslint-config/package.json | 8 +++++++- .../eslint-config/temporarily-disable-rules.cjs | 1 + .../@local/hash-graph-client/typescript/tsconfig.json | 1 + .../tsconfig/legacy-base-tsconfig-to-refactor.json | 3 ++- yarn.lock | 10 +++++++++- 6 files changed, 28 insertions(+), 6 deletions(-) diff --git a/libs/@local/eslint-config/generate-ignore-patterns.cjs b/libs/@local/eslint-config/generate-ignore-patterns.cjs index 87ef2f00863..f5acc672f9d 100644 --- a/libs/@local/eslint-config/generate-ignore-patterns.cjs +++ b/libs/@local/eslint-config/generate-ignore-patterns.cjs @@ -18,13 +18,18 @@ const monorepoRoot = path.resolve(__dirname, "../../.."); * Given a path from a .gitignore in a parent directory, this returns the equivalent paths if written in the current * directory * - * @param pattern - * @param workspaceDirPrefix - * @returns {[string]} + * @param {string} pattern + * @param {string} workspaceDirPrefix + * @returns {string[]} */ const getEquivalentIgnorePaths = (pattern, workspaceDirPrefix) => { // We want to traverse the components of the workspaceDirPrefix, and consume wild cards whenever they match. // On some wildcards there may be a branch of equivalent paths, e.g. for a "**" wildcard + /** + * @param {string[]} pathComponents + * @param {string[]} patternComponents + * @returns {Set} + */ const getEquivalentPaths = (pathComponents, patternComponents) => { const equivalentPaths = new Set(); diff --git a/libs/@local/eslint-config/package.json b/libs/@local/eslint-config/package.json index 3f230fdeb68..3bcdf7e1fd3 100644 --- a/libs/@local/eslint-config/package.json +++ b/libs/@local/eslint-config/package.json @@ -4,7 +4,8 @@ "private": true, "scripts": { "fix:eslint": "eslint --fix .", - "lint:eslint": "eslint --report-unused-disable-directives ." + "lint:eslint": "eslint --report-unused-disable-directives .", + "lint:tsc": "tsc --noEmit" }, "dependencies": { "@babel/core": "7.26.0", @@ -24,5 +25,10 @@ "eslint-plugin-typescript-sort-keys": "3.2.0", "eslint-plugin-unicorn": "51.0.1", "typescript": "5.6.3" + }, + "devDependencies": { + "@local/tsconfig": "0.0.0-private", + "@types/eslint": "8.56.12", + "@types/node": "20.17.0" } } diff --git a/libs/@local/eslint-config/temporarily-disable-rules.cjs b/libs/@local/eslint-config/temporarily-disable-rules.cjs index b7dbc8be61b..9cd480bc28d 100644 --- a/libs/@local/eslint-config/temporarily-disable-rules.cjs +++ b/libs/@local/eslint-config/temporarily-disable-rules.cjs @@ -4,6 +4,7 @@ * @see https://github.com/hashintel/hash/pull/1384 */ module.exports = (ruleNames) => { + /** @type {import("eslint").Linter.RulesRecord} */ const result = {}; if (process.env.CHECK_TEMPORARILY_DISABLED_RULES === "true") { diff --git a/libs/@local/hash-graph-client/typescript/tsconfig.json b/libs/@local/hash-graph-client/typescript/tsconfig.json index 79eb8e02165..6656c3bd951 100644 --- a/libs/@local/hash-graph-client/typescript/tsconfig.json +++ b/libs/@local/hash-graph-client/typescript/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "@local/tsconfig/legacy-base-tsconfig-to-refactor.json", "include": ["./"], + "exclude": ["./dist"], "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext" diff --git a/libs/@local/tsconfig/legacy-base-tsconfig-to-refactor.json b/libs/@local/tsconfig/legacy-base-tsconfig-to-refactor.json index d692c3a7608..ce5b126fb19 100644 --- a/libs/@local/tsconfig/legacy-base-tsconfig-to-refactor.json +++ b/libs/@local/tsconfig/legacy-base-tsconfig-to-refactor.json @@ -33,7 +33,8 @@ "@local/status": ["../status/typescript/pkg/src/main.ts"], "@local/hash-subgraph": ["../hash-subgraph/src/main.ts"], "@local/hash-subgraph/*": ["../hash-subgraph/src/*.ts"] - } + }, + "outDir": "./dist" }, "ts-node": { "transpileOnly": true diff --git a/yarn.lock b/yarn.lock index 7b3bec918c6..2ca49d7ecfd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10663,6 +10663,14 @@ resolved "https://registry.yarnpkg.com/@types/escodegen/-/escodegen-0.0.6.tgz#5230a9ce796e042cda6f086dbf19f22ea330659c" integrity sha512-AjwI4MvWx3HAOaZqYsjKWyEObT9lcVV0Y0V8nXo6cXzN8ZiMxVhf6F3d/UNvXVGKrEzL/Dluc5p+y9GkzlTWig== +"@types/eslint@8.56.12": + version "8.56.12" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.12.tgz#1657c814ffeba4d2f84c0d4ba0f44ca7ea1ca53a" + integrity sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + "@types/estree-jsx@^0.0.1": version "0.0.1" resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-0.0.1.tgz#c36d7a1afeb47a95a8ee0b7bc8bc705db38f919d" @@ -10878,7 +10886,7 @@ "@types/tough-cookie" "*" parse5 "^7.0.0" -"@types/json-schema@^7.0.11", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.11", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== From 56bbdbf36d4714e54ed43bd871ea951624ac6814 Mon Sep 17 00:00:00 2001 From: Tim Diekmann <21277928+TimDiekmann@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:22:18 +0100 Subject: [PATCH 6/6] H-3521: Make description required in ontology types (#5511) --- .../migrate-ontology-types/util.ts | 4 +- .../edit-pinned-entity-types-modal.tsx | 1 + .../src/pages/shared/entity-type-selector.tsx | 2 +- .../libs/api/openapi/models/data_type.json | 2 +- .../libs/api/openapi/models/entity_type.json | 16 ++-- .../api/openapi/models/property_type.json | 2 +- .../api/openapi/models/update_data_type.json | 2 +- .../openapi/models/update_entity_type.json | 13 +--- .../openapi/models/update_property_type.json | 9 ++- apps/hash-graph/libs/api/openapi/openapi.json | 1 - .../api/src/rest/json_schemas/data_type.json | 2 +- .../src/rest/json_schemas/entity_type.json | 16 ++-- .../src/rest/json_schemas/property_type.json | 2 +- .../rest/json_schemas/update_data_type.json | 2 +- .../rest/json_schemas/update_entity_type.json | 13 +--- .../json_schemas/update_property_type.json | 9 ++- .../snapshot/ontology/entity_type/channel.rs | 2 +- .../store/postgres/knowledge/entity/query.rs | 8 +- .../src/store/postgres/query/entity_type.rs | 5 -- .../libs/store/src/entity_type/query.rs | 24 +----- .../entity_types/has_query_v1.json | 1 - .../entity_types/link_v1.json | 1 + .../entity_types/query_v1.json | 1 - .../entity_types/thing_v1.json | 1 - blocks/address/package.json | 1 - .../rust/src/schema/data_type/closed.rs | 74 +++++-------------- .../rust/src/schema/data_type/mod.rs | 13 +--- .../rust/src/schema/entity_type/closed.rs | 6 +- .../rust/src/schema/entity_type/mod.rs | 11 +-- .../rust/src/schema/property_type/mod.rs | 7 +- .../rust/src/schema/property_type/raw.rs | 7 +- .../type-system/rust/src/utils.rs | 5 +- .../src/get-form-data-from-entity-type.ts | 2 +- .../src/simplified-graph.ts | 7 +- libs/@local/hash-validation/src/lib.rs | 9 ++- .../hash-validation/src/test_data_type.rs | 16 ++++ .../graph/knowledge/primitive/entity.test.ts | 3 + .../knowledge/primitive/link-entity.test.ts | 1 + .../knowledge/system-types/block.test.ts | 1 + .../ontology/primitive/data-type.test.ts | 1 + .../ontology/primitive/entity-type.test.ts | 5 ++ .../ontology/primitive/property-type.test.ts | 1 + .../src/tests/subgraph/pass/circular.jsonl | 6 +- .../src/tests/subgraph/pass/friendship.jsonl | 16 ++-- .../hash-graph-http/tests/circular-links.http | 3 + tests/hash-graph-http/tests/friendship.http | 1 + .../tests/link-inheritance.http | 29 +++++--- .../rust/src/entity_type/block.json | 1 + .../rust/src/entity_type/book.json | 1 + .../rust/src/entity_type/building.json | 1 + .../rust/src/entity_type/church.json | 1 + .../rust/src/entity_type/link.json | 1 + .../rust/src/entity_type/organization.json | 1 + .../rust/src/entity_type/page_v1.json | 1 + .../rust/src/entity_type/page_v2.json | 1 + .../rust/src/entity_type/person.json | 1 + .../rust/src/entity_type/playlist.json | 1 + .../rust/src/entity_type/song.json | 1 + .../rust/src/entity_type/uk_address.json | 1 + .../src/property_type/address_line_1.json | 1 + .../rust/src/property_type/age.json | 1 + .../rust/src/property_type/blurb.json | 1 + .../rust/src/property_type/built_at.json | 1 + .../rust/src/property_type/city.json | 1 + .../property_type/contact_information.json | 1 + .../src/property_type/contrived_property.json | 1 + .../rust/src/property_type/email.json | 1 + .../rust/src/property_type/favorite_film.json | 1 + .../src/property_type/favorite_quote.json | 1 + .../rust/src/property_type/favorite_song.json | 1 + .../rust/src/property_type/hobby.json | 1 + .../rust/src/property_type/interests.json | 1 + .../rust/src/property_type/name.json | 1 + .../rust/src/property_type/numbers.json | 1 + .../rust/src/property_type/phone_number.json | 1 + .../rust/src/property_type/postcode.json | 1 + .../rust/src/property_type/published_on.json | 1 + .../rust/src/property_type/text.json | 1 + .../rust/src/property_type/user_id_v1.json | 1 + .../rust/src/property_type/user_id_v2.json | 1 + 80 files changed, 204 insertions(+), 193 deletions(-) diff --git a/apps/hash-api/src/graph/ensure-system-graph-is-initialized/migrate-ontology-types/util.ts b/apps/hash-api/src/graph/ensure-system-graph-is-initialized/migrate-ontology-types/util.ts index 36b8175a63e..2f7d6804a91 100644 --- a/apps/hash-api/src/graph/ensure-system-graph-is-initialized/migrate-ontology-types/util.ts +++ b/apps/hash-api/src/graph/ensure-system-graph-is-initialized/migrate-ontology-types/util.ts @@ -113,7 +113,7 @@ export const generateSystemTypeBaseUrl = ({ export type PropertyTypeDefinition = { propertyTypeId: VersionedUrl; title: string; - description?: string; + description: string; possibleValues: { dataTypeId?: VersionedUrl; primitiveDataType?: PrimitiveDataTypeKey; @@ -411,7 +411,7 @@ export type EntityTypeDefinition = { allOf?: VersionedUrl[]; entityTypeId: VersionedUrl; title: string; - description?: string; + description: string; labelProperty?: BaseUrl; icon?: string; properties?: { diff --git a/apps/hash-frontend/src/pages/[shortname].page/edit-pinned-entity-types-modal.tsx b/apps/hash-frontend/src/pages/[shortname].page/edit-pinned-entity-types-modal.tsx index bdbccb227ea..2717f0bc70d 100644 --- a/apps/hash-frontend/src/pages/[shortname].page/edit-pinned-entity-types-modal.tsx +++ b/apps/hash-frontend/src/pages/[shortname].page/edit-pinned-entity-types-modal.tsx @@ -199,6 +199,7 @@ export const EditPinnedEntityTypesModal: FunctionComponent< entityType: { title, type: "object", + description: "", properties: {}, }, }, diff --git a/apps/hash-frontend/src/pages/shared/entity-type-selector.tsx b/apps/hash-frontend/src/pages/shared/entity-type-selector.tsx index 541ef760322..73a975617b6 100644 --- a/apps/hash-frontend/src/pages/shared/entity-type-selector.tsx +++ b/apps/hash-frontend/src/pages/shared/entity-type-selector.tsx @@ -86,7 +86,7 @@ export const EntityTypeSelector = ({ const { title, description } = option.schema; const lowercaseInput = inputValue.toLowerCase(); return ( - !!description?.toLowerCase().includes(lowercaseInput) || + !!description.toLowerCase().includes(lowercaseInput) || title.toLowerCase().includes(lowercaseInput) ); }); diff --git a/apps/hash-graph/libs/api/openapi/models/data_type.json b/apps/hash-graph/libs/api/openapi/models/data_type.json index 2702137fb05..59419c06dec 100644 --- a/apps/hash-graph/libs/api/openapi/models/data_type.json +++ b/apps/hash-graph/libs/api/openapi/models/data_type.json @@ -37,6 +37,6 @@ ] } }, - "required": ["$schema", "kind", "$id", "title"], + "required": ["$schema", "kind", "$id", "title", "description"], "additionalProperties": true } diff --git a/apps/hash-graph/libs/api/openapi/models/entity_type.json b/apps/hash-graph/libs/api/openapi/models/entity_type.json index 9100c471fbb..c24d3406f1c 100644 --- a/apps/hash-graph/libs/api/openapi/models/entity_type.json +++ b/apps/hash-graph/libs/api/openapi/models/entity_type.json @@ -32,12 +32,6 @@ "$ref": "./shared.json#/definitions/EntityTypeReference" } }, - "examples": { - "type": "array", - "items": { - "type": "object" - } - }, "properties": { "$ref": "./shared.json#/definitions/PropertyTypeObject" }, @@ -52,5 +46,13 @@ } }, "additionalProperties": false, - "required": ["$schema", "kind", "type", "$id", "title", "properties"] + "required": [ + "$schema", + "kind", + "type", + "$id", + "title", + "description", + "properties" + ] } diff --git a/apps/hash-graph/libs/api/openapi/models/property_type.json b/apps/hash-graph/libs/api/openapi/models/property_type.json index a5772a809d8..3e55bfd9850 100644 --- a/apps/hash-graph/libs/api/openapi/models/property_type.json +++ b/apps/hash-graph/libs/api/openapi/models/property_type.json @@ -29,6 +29,6 @@ } } }, - "required": ["$schema", "kind", "$id", "title", "oneOf"], + "required": ["$schema", "kind", "$id", "title", "description", "oneOf"], "additionalProperties": false } diff --git a/apps/hash-graph/libs/api/openapi/models/update_data_type.json b/apps/hash-graph/libs/api/openapi/models/update_data_type.json index a82d63033c8..48dcb128fa4 100644 --- a/apps/hash-graph/libs/api/openapi/models/update_data_type.json +++ b/apps/hash-graph/libs/api/openapi/models/update_data_type.json @@ -28,6 +28,6 @@ ] } }, - "required": ["$schema", "kind", "title"], + "required": ["$schema", "kind", "title", "description"], "additionalProperties": true } diff --git a/apps/hash-graph/libs/api/openapi/models/update_entity_type.json b/apps/hash-graph/libs/api/openapi/models/update_entity_type.json index eed871dc74e..3f6a07dbeed 100644 --- a/apps/hash-graph/libs/api/openapi/models/update_entity_type.json +++ b/apps/hash-graph/libs/api/openapi/models/update_entity_type.json @@ -16,17 +16,6 @@ }, "title": { "type": "string" }, "description": { "type": "string" }, - "examples": { - "type": "array", - "items": { - "type": "object", - "x-propertyNames": { - "$comment": "Property names must be a valid URL to a Property Type", - "type": "string", - "format": "uri" - } - } - }, "properties": { "$ref": "./shared.json#/definitions/PropertyTypeObject" }, "required": { "type": "array", @@ -37,5 +26,5 @@ "links": { "$ref": "./shared.json#/definitions/LinkTypeObject" } }, "additionalProperties": false, - "required": ["$schema", "kind", "type", "title", "properties"] + "required": ["$schema", "kind", "type", "title", "description", "properties"] } diff --git a/apps/hash-graph/libs/api/openapi/models/update_property_type.json b/apps/hash-graph/libs/api/openapi/models/update_property_type.json index 2d442b2b0d6..e47f556c0dd 100644 --- a/apps/hash-graph/libs/api/openapi/models/update_property_type.json +++ b/apps/hash-graph/libs/api/openapi/models/update_property_type.json @@ -24,6 +24,13 @@ } } }, - "required": ["$schema", "kind", "title", "oneOf"], + "required": [ + "$schema", + "kind", + "title", + "description", + "description", + "oneOf" + ], "additionalProperties": false } diff --git a/apps/hash-graph/libs/api/openapi/openapi.json b/apps/hash-graph/libs/api/openapi/openapi.json index eb022521fbf..16d279ed396 100644 --- a/apps/hash-graph/libs/api/openapi/openapi.json +++ b/apps/hash-graph/libs/api/openapi/openapi.json @@ -4459,7 +4459,6 @@ "ownedById", "title", "description", - "examples", "properties", "required", "labelProperty", diff --git a/apps/hash-graph/libs/api/src/rest/json_schemas/data_type.json b/apps/hash-graph/libs/api/src/rest/json_schemas/data_type.json index 2702137fb05..59419c06dec 100644 --- a/apps/hash-graph/libs/api/src/rest/json_schemas/data_type.json +++ b/apps/hash-graph/libs/api/src/rest/json_schemas/data_type.json @@ -37,6 +37,6 @@ ] } }, - "required": ["$schema", "kind", "$id", "title"], + "required": ["$schema", "kind", "$id", "title", "description"], "additionalProperties": true } diff --git a/apps/hash-graph/libs/api/src/rest/json_schemas/entity_type.json b/apps/hash-graph/libs/api/src/rest/json_schemas/entity_type.json index 9100c471fbb..c24d3406f1c 100644 --- a/apps/hash-graph/libs/api/src/rest/json_schemas/entity_type.json +++ b/apps/hash-graph/libs/api/src/rest/json_schemas/entity_type.json @@ -32,12 +32,6 @@ "$ref": "./shared.json#/definitions/EntityTypeReference" } }, - "examples": { - "type": "array", - "items": { - "type": "object" - } - }, "properties": { "$ref": "./shared.json#/definitions/PropertyTypeObject" }, @@ -52,5 +46,13 @@ } }, "additionalProperties": false, - "required": ["$schema", "kind", "type", "$id", "title", "properties"] + "required": [ + "$schema", + "kind", + "type", + "$id", + "title", + "description", + "properties" + ] } diff --git a/apps/hash-graph/libs/api/src/rest/json_schemas/property_type.json b/apps/hash-graph/libs/api/src/rest/json_schemas/property_type.json index a5772a809d8..3e55bfd9850 100644 --- a/apps/hash-graph/libs/api/src/rest/json_schemas/property_type.json +++ b/apps/hash-graph/libs/api/src/rest/json_schemas/property_type.json @@ -29,6 +29,6 @@ } } }, - "required": ["$schema", "kind", "$id", "title", "oneOf"], + "required": ["$schema", "kind", "$id", "title", "description", "oneOf"], "additionalProperties": false } diff --git a/apps/hash-graph/libs/api/src/rest/json_schemas/update_data_type.json b/apps/hash-graph/libs/api/src/rest/json_schemas/update_data_type.json index a82d63033c8..48dcb128fa4 100644 --- a/apps/hash-graph/libs/api/src/rest/json_schemas/update_data_type.json +++ b/apps/hash-graph/libs/api/src/rest/json_schemas/update_data_type.json @@ -28,6 +28,6 @@ ] } }, - "required": ["$schema", "kind", "title"], + "required": ["$schema", "kind", "title", "description"], "additionalProperties": true } diff --git a/apps/hash-graph/libs/api/src/rest/json_schemas/update_entity_type.json b/apps/hash-graph/libs/api/src/rest/json_schemas/update_entity_type.json index eed871dc74e..3f6a07dbeed 100644 --- a/apps/hash-graph/libs/api/src/rest/json_schemas/update_entity_type.json +++ b/apps/hash-graph/libs/api/src/rest/json_schemas/update_entity_type.json @@ -16,17 +16,6 @@ }, "title": { "type": "string" }, "description": { "type": "string" }, - "examples": { - "type": "array", - "items": { - "type": "object", - "x-propertyNames": { - "$comment": "Property names must be a valid URL to a Property Type", - "type": "string", - "format": "uri" - } - } - }, "properties": { "$ref": "./shared.json#/definitions/PropertyTypeObject" }, "required": { "type": "array", @@ -37,5 +26,5 @@ "links": { "$ref": "./shared.json#/definitions/LinkTypeObject" } }, "additionalProperties": false, - "required": ["$schema", "kind", "type", "title", "properties"] + "required": ["$schema", "kind", "type", "title", "description", "properties"] } diff --git a/apps/hash-graph/libs/api/src/rest/json_schemas/update_property_type.json b/apps/hash-graph/libs/api/src/rest/json_schemas/update_property_type.json index 2d442b2b0d6..e47f556c0dd 100644 --- a/apps/hash-graph/libs/api/src/rest/json_schemas/update_property_type.json +++ b/apps/hash-graph/libs/api/src/rest/json_schemas/update_property_type.json @@ -24,6 +24,13 @@ } } }, - "required": ["$schema", "kind", "title", "oneOf"], + "required": [ + "$schema", + "kind", + "title", + "description", + "description", + "oneOf" + ], "additionalProperties": false } diff --git a/apps/hash-graph/libs/graph/src/snapshot/ontology/entity_type/channel.rs b/apps/hash-graph/libs/graph/src/snapshot/ontology/entity_type/channel.rs index e5b81249701..a5857517664 100644 --- a/apps/hash-graph/libs/graph/src/snapshot/ontology/entity_type/channel.rs +++ b/apps/hash-graph/libs/graph/src/snapshot/ontology/entity_type/channel.rs @@ -88,7 +88,7 @@ impl Sink for EntityTypeSender { }, title: String::new(), title_plural: None, - description: None, + description: String::new(), inverse: InverseEntityTypeMetadata { title: None, title_plural: None, diff --git a/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/query.rs b/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/query.rs index dd5388e7a8b..343dbcfd08c 100644 --- a/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/query.rs +++ b/apps/hash-graph/libs/graph/src/store/postgres/knowledge/entity/query.rs @@ -2,6 +2,7 @@ use graph_types::{ knowledge::{ entity::{Entity, EntityId, EntityMetadata, EntityProvenance, EntityRecordId, EntityUuid}, link::LinkData, + property::PropertyMetadataObject, }, owned_by_id::OwnedById, }; @@ -9,6 +10,7 @@ use hash_graph_store::{ entity::EntityQueryPath, subgraph::edges::{EdgeDirection, KnowledgeGraphEdgeKind}, }; +use serde::Deserialize; use tokio_postgres::Row; use tracing::instrument; use type_system::url::{BaseUrl, OntologyTypeVersion, VersionedUrl}; @@ -140,7 +142,11 @@ impl QueryRecordDecode for Entity { } let property_metadata = row - .get::<_, Option<_>>(indices.property_metadata) + .get::<_, Option>(indices.property_metadata) + .map(|value| { + PropertyMetadataObject::deserialize(value) + .expect("Failed to deserialize property metadata") + }) .unwrap_or_default(); Self { diff --git a/apps/hash-graph/libs/graph/src/store/postgres/query/entity_type.rs b/apps/hash-graph/libs/graph/src/store/postgres/query/entity_type.rs index 1f6a2026a4a..69798ceef2b 100644 --- a/apps/hash-graph/libs/graph/src/store/postgres/query/entity_type.rs +++ b/apps/hash-graph/libs/graph/src/store/postgres/query/entity_type.rs @@ -21,7 +21,6 @@ impl PostgresQueryPath for EntityTypeQueryPath<'_> { | Self::VersionedUrl | Self::Title | Self::Description - | Self::Examples | Self::Required | Self::LabelProperty | Self::Icon @@ -151,10 +150,6 @@ impl PostgresQueryPath for EntityTypeQueryPath<'_> { Column::EntityTypes(EntityTypes::Schema), (Some(JsonField::StaticText("description"))), ), - Self::Examples => ( - Column::EntityTypes(EntityTypes::Schema), - (Some(JsonField::StaticText("examples"))), - ), Self::Required => ( Column::EntityTypes(EntityTypes::Schema), (Some(JsonField::StaticText("required"))), diff --git a/apps/hash-graph/libs/store/src/entity_type/query.rs b/apps/hash-graph/libs/store/src/entity_type/query.rs index 06141658871..05e709ccc93 100644 --- a/apps/hash-graph/libs/store/src/entity_type/query.rs +++ b/apps/hash-graph/libs/store/src/entity_type/query.rs @@ -134,19 +134,6 @@ pub enum EntityTypeQueryPath<'p> { /// /// [`EntityType::description()`]: type_system::schema::EntityType::description Description, - /// Corresponds to [`EntityType::examples()`]. - /// - /// ```rust - /// # use serde::Deserialize; - /// # use serde_json::json; - /// # use hash_graph_store::entity_type::EntityTypeQueryPath; - /// let path = EntityTypeQueryPath::deserialize(json!(["examples"]))?; - /// assert_eq!(path, EntityTypeQueryPath::Examples); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - /// - /// [`EntityType::examples()`]: type_system::schema::EntityType::examples - Examples, /// Corresponds to [`EntityConstraints::required`]. /// /// ```rust @@ -476,7 +463,7 @@ impl QueryPath for EntityTypeQueryPath<'_> { Self::Schema(_) | Self::ClosedSchema(_) | Self::AdditionalMetadata => { ParameterType::Object } - Self::Examples | Self::Required | Self::EditionProvenance(_) => ParameterType::Any, + Self::Required | Self::EditionProvenance(_) => ParameterType::Any, Self::BaseUrl | Self::LabelProperty => ParameterType::BaseUrl, Self::VersionedUrl => ParameterType::VersionedUrl, Self::Version => ParameterType::OntologyTypeVersion, @@ -505,7 +492,6 @@ impl fmt::Display for EntityTypeQueryPath<'_> { Self::ClosedSchema(None) => fmt.write_str("closedSchema"), Self::Title => fmt.write_str("title"), Self::Description => fmt.write_str("description"), - Self::Examples => fmt.write_str("examples"), Self::Required => fmt.write_str("required"), Self::LabelProperty => fmt.write_str("labelProperty"), Self::Icon => fmt.write_str("icon"), @@ -598,7 +584,6 @@ pub enum EntityTypeQueryToken { OwnedById, Title, Description, - Examples, Properties, Required, LabelProperty, @@ -623,8 +608,8 @@ pub(crate) struct EntityTypeQueryPathVisitor { impl EntityTypeQueryPathVisitor { pub(crate) const EXPECTING: &'static str = "one of `baseUrl`, `version`, `versionedUrl`, `ownedById`, `title`, `description`, \ - `examples`, `properties`, `required`, `labelProperty`, `icon`, `editionProvenance`, \ - `links`, `inheritsFrom`, `children`, `embedding`"; + `properties`, `required`, `labelProperty`, `icon`, `editionProvenance`, `links`, \ + `inheritsFrom`, `children`, `embedding`"; #[must_use] pub(crate) const fn new(position: usize) -> Self { @@ -657,7 +642,6 @@ impl<'de> Visitor<'de> for EntityTypeQueryPathVisitor { EntityTypeQueryToken::Version => EntityTypeQueryPath::Version, EntityTypeQueryToken::Title => EntityTypeQueryPath::Title, EntityTypeQueryToken::Description => EntityTypeQueryPath::Description, - EntityTypeQueryToken::Examples => EntityTypeQueryPath::Examples, EntityTypeQueryToken::Properties => { seq.next_element::()? .ok_or_else(|| de::Error::invalid_length(self.position, &self))?; @@ -799,7 +783,6 @@ impl EntityTypeQueryPath<'_> { Self::OwnedById => EntityTypeQueryPath::OwnedById, Self::Title => EntityTypeQueryPath::Title, Self::Description => EntityTypeQueryPath::Description, - Self::Examples => EntityTypeQueryPath::Examples, Self::Required => EntityTypeQueryPath::Required, Self::LabelProperty => EntityTypeQueryPath::LabelProperty, Self::Icon => EntityTypeQueryPath::Icon, @@ -873,7 +856,6 @@ mod tests { deserialize(["description"]), EntityTypeQueryPath::Description ); - assert_eq!(deserialize(["examples"]), EntityTypeQueryPath::Examples); assert_eq!( deserialize(["properties", "*", "version"]), EntityTypeQueryPath::PropertyTypeEdge { diff --git a/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/has_query_v1.json b/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/has_query_v1.json index 0877c5cff7b..8362d68670d 100644 --- a/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/has_query_v1.json +++ b/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/has_query_v1.json @@ -10,7 +10,6 @@ } ], "description": "The query that something has.", - "examples": [], "links": {}, "properties": {}, "required": [] diff --git a/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/link_v1.json b/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/link_v1.json index 204c38f4ee6..67790f8edd0 100644 --- a/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/link_v1.json +++ b/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/link_v1.json @@ -4,5 +4,6 @@ "$id": "https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1", "type": "object", "title": "Link", + "description": "The most generic connection between two entities, defining a relationship from a source to a target. It serves as a parent type for more specific link entity types, enabling consistent and interoperable data relationships.", "properties": {} } diff --git a/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/query_v1.json b/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/query_v1.json index 80487f57283..3363aa39be8 100644 --- a/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/query_v1.json +++ b/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/query_v1.json @@ -6,7 +6,6 @@ "type": "object", "allOf": [], "description": "", - "examples": [], "links": {}, "properties": { "https://blockprotocol.org/@hash/types/property-type/query/": { diff --git a/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/thing_v1.json b/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/thing_v1.json index ac6fefbc326..7a979897092 100644 --- a/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/thing_v1.json +++ b/apps/hash-graph/libs/type-fetcher/predefined_types/entity_types/thing_v1.json @@ -1,7 +1,6 @@ { "allOf": [], "description": "A generic thing", - "examples": [], "$id": "https://blockprotocol.org/@blockprotocol/types/entity-type/thing/v/1", "kind": "entityType", "links": {}, diff --git a/blocks/address/package.json b/blocks/address/package.json index fb3cd509d8d..be1eba61e67 100644 --- a/blocks/address/package.json +++ b/blocks/address/package.json @@ -67,7 +67,6 @@ "entryPoint": "react" }, "displayName": "Address", - "examples": [], "icon": "public/address.svg", "image": "public/block-preview.png", "name": "@hash/address", diff --git a/libs/@blockprotocol/type-system/rust/src/schema/data_type/closed.rs b/libs/@blockprotocol/type-system/rust/src/schema/data_type/closed.rs index bb01495292e..bfa8f5665c8 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/data_type/closed.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/data_type/closed.rs @@ -89,21 +89,20 @@ impl ClosedDataType { data_type: DataType, resolve_data: &DataTypeResolveData, ) -> Result> { - let (description, label) = if data_type.description.is_some() || !data_type.label.is_empty() - { - (data_type.description, data_type.label) - } else { + let label = if data_type.label.is_empty() { resolve_data .find_metadata_schema()? - .map(|schema| (schema.description.clone(), schema.label.clone())) + .map(|schema| schema.label.clone()) .unwrap_or_default() + } else { + data_type.label }; Ok(Self { id: data_type.id.clone(), title: data_type.title.clone(), title_plural: data_type.title_plural.clone(), - description: description.ok_or(ResolveClosedDataTypeError::MissingDescription)?, + description: data_type.description.clone(), label, all_of: ValueConstraints::fold_intersections( iter::once(data_type.constraints).chain(resolve_data.constraints().cloned()), @@ -187,7 +186,7 @@ impl DataTypeResolveData { ) -> Result, Report> { let mut found_schema_data = None::<(InheritanceDepth, &DataType)>; for (depth, stored_schema) in self.ordered_schemas() { - if stored_schema.description.is_some() || !stored_schema.label.is_empty() { + if !stored_schema.label.is_empty() { if let Some((found_depth, found_schema)) = found_schema_data { match depth.cmp(&found_depth) { cmp::Ordering::Less => { @@ -253,13 +252,14 @@ mod tests { .await .into_inner(); - let number = ensure_validation_from_str::( + let mut number = ensure_validation_from_str::( graph_test_data::data_type::NUMBER_V1, DataTypeValidator, JsonEqualityCheck::Yes, ) .await .into_inner(); + number.label.right = Some("f64".to_owned()); let integer = ensure_validation::( json!({ @@ -267,6 +267,7 @@ mod tests { "kind": "dataType", "$id": "https://example.com/data-type/integer/v/1", "title": "Integer", + "description": "A signed integer.", "allOf": [{ "$ref": number.id }], "type": "number", "abstract": false, @@ -284,6 +285,7 @@ mod tests { "kind": "dataType", "$id": "https://example.com/data-type/unsigned/v/1", "title": "Unsigned", + "description": "An unsigned number.", "allOf": [{ "$ref": number.id }], "type": "number", "abstract": false, @@ -301,6 +303,7 @@ mod tests { "kind": "dataType", "$id": "https://example.com/data-type/unsigned-int/v/1", "title": "Unsigned Integer", + "description": "An unsigned integer.", "allOf": [{ "$ref": integer.id }, { "$ref": unsigned.id }], "type": "number", "maximum": 4_294_967_295.0, @@ -319,6 +322,7 @@ mod tests { "$id": "https://example.com/data-type/very-small/v/1", "title": "Small number", "description": "A small number", + "label": { "right": "i8" }, "allOf": [{ "$ref": number.id }], "type": "number", "maximum": 255.0, @@ -336,6 +340,7 @@ mod tests { "kind": "dataType", "$id": "https://example.com/data-type/unsigned-small-int/v/1", "title": "Unsigned Integer", + "description": "An unsigned integer.", "allOf": [{ "$ref": unsigned_int.id }, { "$ref": small.id }], "type": "number", "maximum": 100.0, @@ -362,13 +367,7 @@ mod tests { assert_eq!(value.id, defs.value.id); assert_eq!(value.title, defs.value.title); assert_eq!(value.title_plural, defs.value.title_plural); - assert_eq!( - value.description, - defs.value - .description - .as_deref() - .expect("Missing description") - ); + assert_eq!(value.description, defs.value.description); assert_eq!(value.label, defs.value.label); assert_eq!(value.r#abstract, defs.value.r#abstract); assert_eq!(json!(value.all_of), json!([defs.value.constraints])); @@ -378,13 +377,7 @@ mod tests { assert_eq!(number.id, defs.number.id); assert_eq!(number.title, defs.number.title); assert_eq!(number.title_plural, defs.number.title_plural); - assert_eq!( - number.description, - defs.number - .description - .as_deref() - .expect("Missing description") - ); + assert_eq!(number.description, defs.number.description); assert_eq!(number.label, defs.number.label); assert_eq!(number.r#abstract, defs.number.r#abstract); assert_eq!(json!(number.all_of), json!([defs.number.constraints])); @@ -394,13 +387,7 @@ mod tests { assert_eq!(integer.id, defs.integer.id); assert_eq!(integer.title, defs.integer.title); assert_eq!(integer.title_plural, defs.integer.title_plural); - assert_eq!( - integer.description, - defs.number - .description - .as_deref() - .expect("Missing description") - ); + assert_eq!(integer.description, defs.integer.description); assert_eq!(integer.label, defs.number.label); assert_eq!(integer.r#abstract, defs.integer.r#abstract); assert_eq!(json!(integer.all_of), json!([defs.integer.constraints])); @@ -410,13 +397,7 @@ mod tests { assert_eq!(unsigned.id, defs.unsigned.id); assert_eq!(unsigned.title, defs.unsigned.title); assert_eq!(unsigned.title_plural, defs.unsigned.title_plural); - assert_eq!( - unsigned.description, - defs.number - .description - .as_deref() - .expect("Missing description") - ); + assert_eq!(unsigned.description, defs.unsigned.description); assert_eq!(unsigned.label, defs.number.label); assert_eq!(unsigned.r#abstract, defs.unsigned.r#abstract); assert_eq!(json!(unsigned.all_of), json!([defs.unsigned.constraints])); @@ -426,13 +407,7 @@ mod tests { assert_eq!(unsigned_int.id, defs.unsigned_int.id); assert_eq!(unsigned_int.title, defs.unsigned_int.title); assert_eq!(unsigned_int.title_plural, defs.unsigned_int.title_plural); - assert_eq!( - unsigned_int.description, - defs.number - .description - .as_deref() - .expect("Missing description") - ); + assert_eq!(unsigned_int.description, defs.unsigned_int.description); assert_eq!(unsigned_int.label, defs.number.label); assert_eq!(unsigned_int.r#abstract, defs.unsigned_int.r#abstract); assert_eq!( @@ -452,13 +427,7 @@ mod tests { assert_eq!(small.id, defs.small.id); assert_eq!(small.title, defs.small.title); assert_eq!(small.title_plural, defs.small.title_plural); - assert_eq!( - small.description, - defs.small - .description - .as_deref() - .expect("Missing description") - ); + assert_eq!(small.description, defs.small.description); assert_eq!(small.label, defs.small.label); assert_eq!(small.r#abstract, defs.small.r#abstract); assert_eq!(json!(small.all_of), json!([defs.small.constraints])); @@ -476,10 +445,7 @@ mod tests { ); assert_eq!( unsigned_small_int.description, - defs.small - .description - .as_deref() - .expect("Missing description") + defs.unsigned_small_int.description ); assert_eq!(unsigned_small_int.label, defs.small.label); assert_eq!( diff --git a/libs/@blockprotocol/type-system/rust/src/schema/data_type/mod.rs b/libs/@blockprotocol/type-system/rust/src/schema/data_type/mod.rs index 4c354a464b6..f15cecfcb57 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/data_type/mod.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/data_type/mod.rs @@ -105,19 +105,11 @@ pub enum DataTypeSchemaTag { #[cfg_attr(target_arch = "wasm32", derive(tsify::Tsify))] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ValueSchemaMetadata { - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, + pub description: String, #[serde(default, skip_serializing_if = "ValueLabel::is_empty")] pub label: ValueLabel, } -impl ValueSchemaMetadata { - #[must_use] - pub const fn is_empty(&self) -> bool { - self.description.is_none() && self.label.is_empty() - } -} - mod raw { use alloc::collections::BTreeSet; use std::collections::HashSet; @@ -453,8 +445,7 @@ pub struct DataType { pub title: String, #[serde(skip_serializing_if = "Option::is_none")] pub title_plural: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, + pub description: String, #[serde(default, skip_serializing_if = "ValueLabel::is_empty")] pub label: ValueLabel, // Lexicographically ordered so we have a deterministic order for inheriting parent diff --git a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/closed.rs b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/closed.rs index 25ff86bcded..b8e537392f0 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/closed.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/closed.rs @@ -25,8 +25,7 @@ pub struct ClosedEntityTypeMetadata { pub title: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub title_plural: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, + pub description: String, #[serde(default, skip_serializing_if = "InverseEntityTypeMetadata::is_empty")] pub inverse: InverseEntityTypeMetadata, #[serde(default, skip_serializing_if = "Vec::is_empty")] @@ -46,8 +45,7 @@ pub struct ClosedEntityType { pub title: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub title_plural: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, + pub description: String, #[serde(flatten)] pub constraints: EntityConstraints, #[serde(default, skip_serializing_if = "Vec::is_empty")] diff --git a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/mod.rs b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/mod.rs index 592ec905fe3..7565e5deade 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/entity_type/mod.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/entity_type/mod.rs @@ -16,7 +16,6 @@ use core::iter; use std::collections::{HashMap, HashSet, hash_map::Entry}; use serde::{Deserialize, Serialize}; -use serde_json::Value as JsonValue; use crate::{ schema::{ @@ -64,8 +63,7 @@ pub struct EntityTypeSchemaMetadata { pub title: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub title_plural: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, + pub description: String, #[serde(default, skip_serializing_if = "InverseEntityTypeMetadata::is_empty")] pub inverse: InverseEntityTypeMetadata, } @@ -101,8 +99,7 @@ pub struct EntityType { pub title: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub title_plural: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, + pub description: String, #[serde(default, skip_serializing_if = "InverseEntityTypeMetadata::is_empty")] pub inverse: InverseEntityTypeMetadata, #[serde(flatten)] @@ -117,9 +114,6 @@ pub struct EntityType { pub label_property: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub icon: Option, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - #[deprecated] - pub examples: Vec>, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -499,6 +493,7 @@ mod tests { "$id": "https://blockprotocol.org/@alice/types/entity-type/invalid/v/1", "type": "object", "title": "Invalid", + "description": "An invalid entity type", "properties": { "https://example.com/property_type_a/": { "$ref": "https://example.com/property_type_b/v/1" } } diff --git a/libs/@blockprotocol/type-system/rust/src/schema/property_type/mod.rs b/libs/@blockprotocol/type-system/rust/src/schema/property_type/mod.rs index e4317b967dd..3c5dbcda6e6 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/property_type/mod.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/property_type/mod.rs @@ -24,7 +24,7 @@ pub struct PropertyType { pub id: VersionedUrl, pub title: String, pub title_plural: Option, - pub description: Option, + pub description: String, pub one_of: Vec, } @@ -299,6 +299,7 @@ mod tests { "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/age/v/", "title": "Age", + "description": "The age of a person.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/number/v/1" @@ -320,6 +321,7 @@ mod tests { "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/age/v/1", "title": "Age", + "description": "The age of a person.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/number/v/1" @@ -340,6 +342,7 @@ mod tests { "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/age/v/1", "title": "Age", + "description": "The age of a person.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/number" @@ -361,6 +364,7 @@ mod tests { "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/age/v/1", "title": "Age", + "description": "The age of a person.", "oneOf": [] }), PropertyTypeValidator, @@ -389,6 +393,7 @@ mod tests { "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/contact-information/v/1", "title": "Contact Information", + "description": "A contact information property type that can be either an email or a phone number.", "oneOf": [ { "type": "object", diff --git a/libs/@blockprotocol/type-system/rust/src/schema/property_type/raw.rs b/libs/@blockprotocol/type-system/rust/src/schema/property_type/raw.rs index 3ae2a7bf50e..ee1c2122ba0 100644 --- a/libs/@blockprotocol/type-system/rust/src/schema/property_type/raw.rs +++ b/libs/@blockprotocol/type-system/rust/src/schema/property_type/raw.rs @@ -31,8 +31,7 @@ pub struct PropertyType<'a> { title: Cow<'a, str>, #[serde(default, skip_serializing_if = "Option::is_none")] title_plural: Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] - description: Option>, + description: Cow<'a, str>, #[cfg_attr( target_arch = "wasm32", tsify(type = "[PropertyValues, ...PropertyValues[]]") @@ -46,7 +45,7 @@ impl From> for super::PropertyType { id: property_type.id.into_owned(), title: property_type.title.into_owned(), title_plural: property_type.title_plural.map(Cow::into_owned), - description: property_type.description.map(Cow::into_owned), + description: property_type.description.into_owned(), one_of: property_type.one_of.into_owned(), } } @@ -60,7 +59,7 @@ impl<'a> From<&'a super::PropertyType> for PropertyType<'a> { id: Cow::Borrowed(&property_type.id), title: Cow::Borrowed(&property_type.title), title_plural: property_type.title_plural.as_deref().map(Cow::Borrowed), - description: property_type.description.as_deref().map(Cow::Borrowed), + description: Cow::Borrowed(&property_type.description), one_of: Cow::Borrowed(&property_type.one_of), } } diff --git a/libs/@blockprotocol/type-system/rust/src/utils.rs b/libs/@blockprotocol/type-system/rust/src/utils.rs index 2f3eef87b4c..2c645c19c8b 100644 --- a/libs/@blockprotocol/type-system/rust/src/utils.rs +++ b/libs/@blockprotocol/type-system/rust/src/utils.rs @@ -46,6 +46,7 @@ mod wasm { pub(crate) mod tests { use core::fmt::Debug; + use error_stack::ResultExt; use serde::{Deserialize, Serialize}; use crate::{Valid, Validator}; @@ -77,7 +78,9 @@ pub(crate) mod tests { where for<'de> T: Serialize + Deserialize<'de>, { - let deserialized: T = serde_json::from_value(value.clone()).expect("failed to deserialize"); + let deserialized: T = serde_json::from_value(value.clone()) + .attach_printable_lazy(|| value.clone()) + .expect("failed to deserialize"); let re_serialized = serde_json::to_value(deserialized).expect("failed to serialize"); if equality == JsonEqualityCheck::Yes { diff --git a/libs/@hashintel/type-editor/src/get-form-data-from-entity-type.ts b/libs/@hashintel/type-editor/src/get-form-data-from-entity-type.ts index 677f8c6332e..88c9794f252 100644 --- a/libs/@hashintel/type-editor/src/get-form-data-from-entity-type.ts +++ b/libs/@hashintel/type-editor/src/get-form-data-from-entity-type.ts @@ -10,7 +10,7 @@ export const getFormDataFromEntityType = ( schema: EntityType, ): EntityTypeEditorFormData => ({ allOf: schema.allOf?.map((ref) => ref.$ref) ?? [], - description: schema.description ?? "", + description: schema.description, labelProperty: schema.labelProperty, icon: schema.icon, properties: Object.entries(schema.properties).map(([propertyId, ref]) => { diff --git a/libs/@local/hash-backend-utils/src/simplified-graph.ts b/libs/@local/hash-backend-utils/src/simplified-graph.ts index 939f39ac8c5..793fcd7c37f 100644 --- a/libs/@local/hash-backend-utils/src/simplified-graph.ts +++ b/libs/@local/hash-backend-utils/src/simplified-graph.ts @@ -66,8 +66,7 @@ export const getSimpleEntityType = ( throw new Error("Property type not found in subgraph"); } - properties[propertyType.schema.title] = - propertyType.schema.description ?? ""; + properties[propertyType.schema.title] = propertyType.schema.description; } const links: SimpleEntityType["links"] = {}; @@ -77,12 +76,12 @@ export const getSimpleEntityType = ( throw new Error("Link type not found in subgraph"); } - links[linkType.schema.title] = linkType.schema.description ?? ""; + links[linkType.schema.title] = linkType.schema.description; } return { title: typeSchema.title, - description: typeSchema.description ?? "", + description: typeSchema.description, entityTypeId, properties, links, diff --git a/libs/@local/hash-validation/src/lib.rs b/libs/@local/hash-validation/src/lib.rs index 2c43c2969d6..924dab47780 100644 --- a/libs/@local/hash-validation/src/lib.rs +++ b/libs/@local/hash-validation/src/lib.rs @@ -96,6 +96,7 @@ mod tests { use core::iter; use std::collections::HashMap; + use error_stack::ResultExt; use graph_types::{ account::{AccountId, EditionCreatedById}, knowledge::property::{ @@ -451,12 +452,16 @@ mod tests { [], [], data_types.into_iter().map(|data_type| { - serde_json::from_str(data_type).expect("failed to parse data type") + serde_json::from_str(data_type) + .attach_printable(data_type) + .expect("failed to parse data type") }), ); let data_type = generate_data_type_metadata( - serde_json::from_str(data_type).expect("failed to parse data type"), + serde_json::from_str(data_type) + .attach_printable_lazy(|| data_type.to_owned()) + .expect("failed to parse data type"), ); let mut metadata = ValueMetadata { diff --git a/libs/@local/hash-validation/src/test_data_type.rs b/libs/@local/hash-validation/src/test_data_type.rs index 9286f7f8b6a..126ad053cf6 100644 --- a/libs/@local/hash-validation/src/test_data_type.rs +++ b/libs/@local/hash-validation/src/test_data_type.rs @@ -48,6 +48,7 @@ async fn integer() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/integer/v/1", "title": "Integer", + "description": "An integral value", "type": "number", "multipleOf": 1, })) @@ -154,6 +155,7 @@ async fn temperature_unit() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/temperature-unit/v/1", "title": "Temperature Unit", + "description": "A unit of temperature", "type": "string", "enum": ["Celsius", "Fahrenheit", "Kelvin"] })) @@ -194,6 +196,7 @@ async fn meter() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/meter/v/1", "title": "Meter", + "description": "The base unit of length in the International System of Units (SI).", "type": "number", "minimum": 0, })) @@ -234,6 +237,7 @@ async fn uri() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/url/v/1", "title": "Url", + "description": "A unique identifier for a resource (e.g. a URL, or URN).", "type": "string", "format": "uri", })) @@ -274,6 +278,7 @@ async fn uuid() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/uuid/v/1", "title": "UUID", + "description": "Universally Unique Identifier (UUID)", "type": "string", "format": "uuid", })) @@ -341,6 +346,7 @@ async fn email() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/email/v/1", "title": "E-Mail", + "description": "An identifier for an email box to which messages are delivered.", "type": "string", "format": "email", })) @@ -381,6 +387,7 @@ async fn zip_code_us() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/zip-code-us/v/1", "title": "Zip code (US)", + "description": "A zip code in the United States", "type": "string", "pattern": "^[0-9]{5}(?:-[0-9]{4})?$", })) @@ -421,6 +428,7 @@ async fn ipv4() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/ipv4/v/1", "title": "IPv4", + "description": "An IPv4 address", "type": "string", "format": "ipv4", })) @@ -479,6 +487,7 @@ async fn ipv6() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/ipv6/v/1", "title": "IPv6", + "description": "An IPv6 address", "type": "string", "format": "ipv6", })) @@ -537,6 +546,7 @@ async fn hostname() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/hostname/v/1", "title": "Hostname", + "description": "A hostname", "type": "string", "format": "hostname", })) @@ -622,6 +632,7 @@ async fn regex() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/regex/v/1", "title": "Regex", + "description": "A regular expression", "type": "string", "format": "regex", })) @@ -662,6 +673,7 @@ async fn short_string() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/short-string/v/1", "title": "Short string", + "description": "A short string", "type": "string", "minLength": 1, "maxLength": 10, @@ -1075,6 +1087,7 @@ async fn date_time() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/date-time/v/1", "title": "Date Time", + "description": "A reference to a particular date and time, formatted according to RFC-3339, ISO-8601, and HTML.", "type": "string", "format": "date-time", })) @@ -1155,6 +1168,7 @@ async fn date() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/date/v/1", "title": "Date", + "description": "A reference to a particular date, formatted according to RFC-3339, ISO-8601, and HTML.", "type": "string", "format": "date", })) @@ -1420,6 +1434,7 @@ async fn time() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/time/v/1", "title": "Time", + "description": "A reference to a particular clockk time, formatted according to RFC-3339, ISO-8601, and HTML.", "type": "string", "format": "time", })) @@ -1519,6 +1534,7 @@ async fn duration() { "kind": "dataType", "$id": "https://localhost:4000/@alice/types/data-type/duration/v/1", "title": "Duration", + "description": "A reference to a particular duration, formatted according to ISO-8601.", "type": "string", "format": "duration", })) diff --git a/tests/hash-backend-integration/src/tests/graph/knowledge/primitive/entity.test.ts b/tests/hash-backend-integration/src/tests/graph/knowledge/primitive/entity.test.ts index 11ac1b30bbc..d54b6693b5a 100644 --- a/tests/hash-backend-integration/src/tests/graph/knowledge/primitive/entity.test.ts +++ b/tests/hash-backend-integration/src/tests/graph/knowledge/primitive/entity.test.ts @@ -113,6 +113,7 @@ describe("Entity CRU", () => { ownedById: testUser.accountId as OwnedById, schema: { title: "Favorite Book", + description: "The favorite book of a person", oneOf: [{ $ref: textDataTypeId }], }, relationships: [ @@ -135,6 +136,7 @@ describe("Entity CRU", () => { ownedById: testUser.accountId as OwnedById, schema: { title: "Name", + description: "The name of a person", oneOf: [{ $ref: textDataTypeId }], }, relationships: [ @@ -164,6 +166,7 @@ describe("Entity CRU", () => { title: "Person", }), title: "Person", + description: "A person", properties: [ { propertyType: favoriteBookPropertyType }, { propertyType: namePropertyType }, diff --git a/tests/hash-backend-integration/src/tests/graph/knowledge/primitive/link-entity.test.ts b/tests/hash-backend-integration/src/tests/graph/knowledge/primitive/link-entity.test.ts index 66c4a0b5baf..d629ea0dc99 100644 --- a/tests/hash-backend-integration/src/tests/graph/knowledge/primitive/link-entity.test.ts +++ b/tests/hash-backend-integration/src/tests/graph/knowledge/primitive/link-entity.test.ts @@ -147,6 +147,7 @@ describe("Link entity", () => { testEntityType = await createTestEntityType({ title: "Person", + description: "A person", properties: [], outgoingLinks: [ { diff --git a/tests/hash-backend-integration/src/tests/graph/knowledge/system-types/block.test.ts b/tests/hash-backend-integration/src/tests/graph/knowledge/system-types/block.test.ts index 614f4bd3b7f..b709cba7baa 100644 --- a/tests/hash-backend-integration/src/tests/graph/knowledge/system-types/block.test.ts +++ b/tests/hash-backend-integration/src/tests/graph/knowledge/system-types/block.test.ts @@ -60,6 +60,7 @@ describe("Block", () => { webShortname: testUser.shortname!, }), title: "Dummy", + description: "A dummy entity type for testing purposes.", properties: [], outgoingLinks: [], }), diff --git a/tests/hash-backend-integration/src/tests/graph/ontology/primitive/data-type.test.ts b/tests/hash-backend-integration/src/tests/graph/ontology/primitive/data-type.test.ts index c07ba20094c..8f9749a7ed2 100644 --- a/tests/hash-backend-integration/src/tests/graph/ontology/primitive/data-type.test.ts +++ b/tests/hash-backend-integration/src/tests/graph/ontology/primitive/data-type.test.ts @@ -40,6 +40,7 @@ let testUser2: User; const dataTypeSchema: ConstructDataTypeParams = { title: "Text", + description: "A string of text.", type: "string", allOf: [ { diff --git a/tests/hash-backend-integration/src/tests/graph/ontology/primitive/entity-type.test.ts b/tests/hash-backend-integration/src/tests/graph/ontology/primitive/entity-type.test.ts index ad8bae8e87d..ffdbb1c617a 100644 --- a/tests/hash-backend-integration/src/tests/graph/ontology/primitive/entity-type.test.ts +++ b/tests/hash-backend-integration/src/tests/graph/ontology/primitive/entity-type.test.ts @@ -81,6 +81,7 @@ beforeAll(async () => { ownedById: testUser.accountId as OwnedById, schema: { title: "Worker", + description: "A worker", type: "object", properties: {}, }, @@ -99,6 +100,7 @@ beforeAll(async () => { ownedById: testUser.accountId as OwnedById, schema: { title: "Address", + description: "An address", type: "object", properties: {}, }, @@ -117,6 +119,7 @@ beforeAll(async () => { ownedById: testUser.accountId as OwnedById, schema: { title: "Favorite Book", + description: "Favorite book of the user", oneOf: [{ $ref: textDataTypeId }], }, relationships: [ @@ -134,6 +137,7 @@ beforeAll(async () => { ownedById: testUser.accountId as OwnedById, schema: { title: "Name", + description: "The name of the user", oneOf: [{ $ref: textDataTypeId }], }, relationships: [ @@ -192,6 +196,7 @@ beforeAll(async () => { entityTypeSchema = { title: "Some", + description: "An object", type: "object", properties: { [favoriteBookPropertyType.metadata.recordId.baseUrl]: { diff --git a/tests/hash-backend-integration/src/tests/graph/ontology/primitive/property-type.test.ts b/tests/hash-backend-integration/src/tests/graph/ontology/primitive/property-type.test.ts index 28e414a44ca..17be1ff7dda 100644 --- a/tests/hash-backend-integration/src/tests/graph/ontology/primitive/property-type.test.ts +++ b/tests/hash-backend-integration/src/tests/graph/ontology/primitive/property-type.test.ts @@ -62,6 +62,7 @@ beforeAll(async () => { propertyTypeSchema = { title: "A property type", + description: "A property type for testing", oneOf: [ { $ref: textDataTypeId, diff --git a/tests/hash-backend-integration/src/tests/subgraph/pass/circular.jsonl b/tests/hash-backend-integration/src/tests/subgraph/pass/circular.jsonl index 4317b9272cc..9c54a082258 100644 --- a/tests/hash-backend-integration/src/tests/subgraph/pass/circular.jsonl +++ b/tests/hash-backend-integration/src/tests/subgraph/pass/circular.jsonl @@ -1,9 +1,9 @@ {"type":"snapshot","blockProtocolModuleVersions":{"graph":"0.3.0"}} {"type":"account","id":"00000000-0001-0000-0000-000000000000"} {"type":"web","id":"00000000-0001-0000-0000-000000000000","relations":[{"relation":"owner","subject":{"kind":"account","subjectId":"00000000-0001-0000-0000-000000000000"}},{"relation":"entityTypeViewer","subject":{"kind":"public"}},{"relation":"propertyTypeViewer","subject":{"kind":"public"}},{"relation":"dataTypeViewer","subject":{"kind":"public"}}]} -{"type":"entityType","metadata":{"fetchedAt":"2000-01-01T00:00:00Z","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-01-01T00:00:00Z"}}},"recordId":{"baseUrl":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/","version":1}},"schema":{"$id":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","kind":"entityType","properties":{},"title":"Link","type":"object"},"relations":[{"relation":"viewer","subject":{"kind":"public"}}]} -{"type":"entityType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2023-11-23T16:30:41.983146000Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@snapshot/types/entity-type/link/","version":1}},"schema":{"$id":"http://localhost:3000/@snapshot/types/entity-type/link/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","allOf":[{"$ref":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1"}],"kind":"entityType","properties":{},"title":"Object","type":"object"},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}]} -{"type":"entityType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-02-01T00:00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@snapshot/types/entity-type/object/","version":1}},"schema":{"$id":"http://localhost:3000/@snapshot/types/entity-type/object/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","kind":"entityType","links":{"http://localhost:3000/@snapshot/types/entity-type/link/v/1":{"items":{"oneOf":[{"$ref":"http://localhost:3000/@snapshot/types/entity-type/object/v/1"}]},"type":"array"}},"properties":{},"title":"Object","type":"object"},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}]} +{"type":"entityType","metadata":{"fetchedAt":"2000-01-01T00:00:00Z","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-01-01T00:00:00Z"}}},"recordId":{"baseUrl":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/","version":1}},"schema":{"$id":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","kind":"entityType","properties":{},"title":"Link","type":"object","description":"The most generic connection between two entities, defining a relationship from a source to a target. It serves as a parent type for more specific link entity types, enabling consistent and interoperable data relationships."},"relations":[{"relation":"viewer","subject":{"kind":"public"}}]} +{"type":"entityType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2023-11-23T16:30:41.983146000Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@snapshot/types/entity-type/link/","version":1}},"schema":{"$id":"http://localhost:3000/@snapshot/types/entity-type/link/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","allOf":[{"$ref":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1"}],"kind":"entityType","properties":{},"title":"Object","type":"object","description":"A link"},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}]} +{"type":"entityType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-02-01T00:00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@snapshot/types/entity-type/object/","version":1}},"schema":{"$id":"http://localhost:3000/@snapshot/types/entity-type/object/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","kind":"entityType","links":{"http://localhost:3000/@snapshot/types/entity-type/link/v/1":{"items":{"oneOf":[{"$ref":"http://localhost:3000/@snapshot/types/entity-type/object/v/1"}]},"type":"array"}},"properties":{},"title":"Object","type":"object","description":"A link"},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}]} {"type":"entity","metadata":{"archived":false,"provenance":{"createdById":"00000000-0001-0000-0000-000000000000","createdAtTransactionTime":"2001-01-01T00:00:00Z","createdAtDecisionTime":"2001-01-01T00:00:00Z","edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"entityTypeIds":["http://localhost:3000/@snapshot/types/entity-type/object/v/1"],"recordId":{"editionId":"0000000A-0001-0000-0000-000000000001","entityId":"00000000-0001-0000-0000-000000000000~0000000A-0001-0000-0000-000000000000"},"temporalVersioning":{"decisionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2001-01-01T00:00:00Z"}},"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2001-01-01T00:00:00Z"}}}},"properties":{}} {"type":"relation","namespace":"entity","object":"0000000A-0001-0000-0000-000000000000","relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}} {"type":"relation","namespace":"entity","object":"0000000A-0001-0000-0000-000000000000","relation":"administrator","subject":{"kind":"account","subjectId":"00000000-0001-0000-0000-000000000000"}} diff --git a/tests/hash-backend-integration/src/tests/subgraph/pass/friendship.jsonl b/tests/hash-backend-integration/src/tests/subgraph/pass/friendship.jsonl index e503f8c8019..e44398a9d24 100644 --- a/tests/hash-backend-integration/src/tests/subgraph/pass/friendship.jsonl +++ b/tests/hash-backend-integration/src/tests/subgraph/pass/friendship.jsonl @@ -1,15 +1,15 @@ {"type":"snapshot","blockProtocolModuleVersions":{"graph":"0.3.0"}} {"type":"account","id":"00000000-0001-0000-0000-000000000000"} {"type":"web","id":"00000000-0001-0000-0000-000000000000","relations":[{"relation":"owner","subject":{"kind":"account","subjectId":"00000000-0001-0000-0000-000000000000"}},{"relation":"entityTypeViewer","subject":{"kind":"public"}},{"relation":"propertyTypeViewer","subject":{"kind":"public"}},{"relation":"dataTypeViewer","subject":{"kind":"public"}}]} -{"type":"dataType","metadata":{"fetchedAt":"2000-01-01T00:00:00Z","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-01-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/data-type/number/","version":1}},"schema":{"$id":"http://localhost:3000/@alice/types/data-type/number/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/data-type","kind":"dataType","title":"Number","type":"number"},"relations":[{"relation":"viewer","subject":{"kind":"public"}}]} -{"type":"dataType","metadata":{"fetchedAt":"2000-01-01T00:00:00Z","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-01-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/data-type/text/","version":1}},"schema":{"$id":"http://localhost:3000/@alice/types/data-type/text/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/data-type","kind":"dataType","title":"Text","type":"string"},"relations":[{"relation":"viewer","subject":{"kind":"public"}}]} +{"type":"dataType","metadata":{"fetchedAt":"2000-01-01T00:00:00Z","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-01-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/data-type/number/","version":1}},"schema":{"$id":"http://localhost:3000/@alice/types/data-type/number/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/data-type","kind":"dataType","title":"Number","type":"number","description":"A number"},"relations":[{"relation":"viewer","subject":{"kind":"public"}}]} +{"type":"dataType","metadata":{"fetchedAt":"2000-01-01T00:00:00Z","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-01-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/data-type/text/","version":1}},"schema":{"$id":"http://localhost:3000/@alice/types/data-type/text/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/data-type","kind":"dataType","title":"Text","type":"string","description":"A text"},"relations":[{"relation":"viewer","subject":{"kind":"public"}}]} {"type":"dataType","metadata":{"fetchedAt":"2000-01-01T00:00:00Z","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-02-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/data-type/text/","version":2}},"schema":{"$id":"http://localhost:3000/@alice/types/data-type/text/v/2","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/data-type","description":"An ordered sequence of characters","kind":"dataType","title":"Text","type":"string"},"relations":[{"relation":"viewer","subject":{"kind":"public"}}]} -{"type":"propertyType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-03-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/property-type/name/","version":1}},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}],"schema":{"$id":"http://localhost:3000/@alice/types/property-type/name/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/property-type","kind":"propertyType","oneOf":[{"$ref":"http://localhost:3000/@alice/types/data-type/text/v/1"}],"title":"Name"}} -{"type":"propertyType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-04-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/property-type/name/","version":2}},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}],"schema":{"$id":"http://localhost:3000/@alice/types/property-type/name/v/2","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/property-type","kind":"propertyType","oneOf":[{"$ref":"http://localhost:3000/@alice/types/data-type/text/v/2"}],"title":"Name"}} -{"type":"entityType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-05-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/entity-type/friendship/","version":1}},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}],"schema":{"$id":"http://localhost:3000/@alice/types/entity-type/friendship/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","allOf":[{"$ref":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1"}],"kind":"entityType","properties":{},"title":"Friendship","type":"object"}} -{"type":"entityType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-06-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/entity-type/person/","version":1}},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}],"schema":{"$id":"http://localhost:3000/@alice/types/entity-type/person/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","kind":"entityType","properties":{"http://localhost:3000/@alice/types/property-type/name/":{"$ref":"http://localhost:3000/@alice/types/property-type/name/v/1"}},"title":"Person","type":"object"}} -{"type":"entityType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-07-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/entity-type/person/","version":2}},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}],"schema":{"$id":"http://localhost:3000/@alice/types/entity-type/person/v/2","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","kind":"entityType","links":{"http://localhost:3000/@alice/types/entity-type/friendship/v/1":{"items":{"oneOf":[{"$ref":"http://localhost:3000/@alice/types/entity-type/person/v/2"}]},"type":"array"}},"properties":{"http://localhost:3000/@alice/types/property-type/name/":{"$ref":"http://localhost:3000/@alice/types/property-type/name/v/2"}},"title":"Person","type":"object","labelProperty":"http://localhost:3000/@alice/types/property-type/name/"}} -{"type":"entityType","metadata":{"fetchedAt":"2000-08-15T00:00Z","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"+002023-05-10T10:39:35.172559000Z"}}},"recordId":{"baseUrl":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/","version":1}},"relations":[{"relation":"viewer","subject":{"kind":"public"}}],"schema":{"$id":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","kind":"entityType","properties":{},"title":"Link","type":"object"}} +{"type":"propertyType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-03-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/property-type/name/","version":1}},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}],"schema":{"$id":"http://localhost:3000/@alice/types/property-type/name/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/property-type","kind":"propertyType","oneOf":[{"$ref":"http://localhost:3000/@alice/types/data-type/text/v/1"}],"title":"Name","description":"A name"}} +{"type":"propertyType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-04-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/property-type/name/","version":2}},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}],"schema":{"$id":"http://localhost:3000/@alice/types/property-type/name/v/2","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/property-type","kind":"propertyType","oneOf":[{"$ref":"http://localhost:3000/@alice/types/data-type/text/v/2"}],"title":"Name","description":"A name"}} +{"type":"entityType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-05-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/entity-type/friendship/","version":1}},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}],"schema":{"$id":"http://localhost:3000/@alice/types/entity-type/friendship/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","allOf":[{"$ref":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1"}],"kind":"entityType","properties":{},"title":"Friendship","type":"object","description":"A friendship"}} +{"type":"entityType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-06-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/entity-type/person/","version":1}},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}],"schema":{"$id":"http://localhost:3000/@alice/types/entity-type/person/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","kind":"entityType","properties":{"http://localhost:3000/@alice/types/property-type/name/":{"$ref":"http://localhost:3000/@alice/types/property-type/name/v/1"}},"title":"Person","type":"object","description":"A person"}} +{"type":"entityType","metadata":{"ownedById":"00000000-0001-0000-0000-000000000000","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2000-07-15T00:00Z"}}},"recordId":{"baseUrl":"http://localhost:3000/@alice/types/entity-type/person/","version":2}},"relations":[{"relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}}],"schema":{"$id":"http://localhost:3000/@alice/types/entity-type/person/v/2","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","kind":"entityType","links":{"http://localhost:3000/@alice/types/entity-type/friendship/v/1":{"items":{"oneOf":[{"$ref":"http://localhost:3000/@alice/types/entity-type/person/v/2"}]},"type":"array"}},"properties":{"http://localhost:3000/@alice/types/property-type/name/":{"$ref":"http://localhost:3000/@alice/types/property-type/name/v/2"}},"title":"Person","type":"object","labelProperty":"http://localhost:3000/@alice/types/property-type/name/","description":"A person"}} +{"type":"entityType","metadata":{"fetchedAt":"2000-08-15T00:00Z","provenance":{"edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"temporalVersioning":{"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"+002023-05-10T10:39:35.172559000Z"}}},"recordId":{"baseUrl":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/","version":1}},"relations":[{"relation":"viewer","subject":{"kind":"public"}}],"schema":{"$id":"https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1","$schema":"https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type","kind":"entityType","properties":{},"title":"Link","type":"object","description":"The most generic connection between two entities, defining a relationship from a source to a target. It serves as a parent type for more specific link entity types, enabling consistent and interoperable data relationships."}} {"type":"entity","metadata":{"archived":false,"provenance":{"createdById":"00000000-0001-0000-0000-000000000000","createdAtTransactionTime":"2001-01-01T00:00Z","createdAtDecisionTime":"2001-01-01T00:00Z","edition":{"createdById":"00000000-0001-0000-0000-000000000000"}},"entityTypeIds":["http://localhost:3000/@alice/types/entity-type/person/v/1"],"recordId":{"editionId":"00000001-0001-0000-0000-000000000001","entityId":"00000000-0001-0000-0000-000000000000~00000001-0001-0000-0000-000000000000~00000000-0000-0000-0000-000000000001"},"temporalVersioning":{"decisionTime":{"end":{"kind":"exclusive","limit":"2001-02-01T00:00Z"},"start":{"kind":"inclusive","limit":"2001-01-01T00:00Z"}},"transactionTime":{"end":{"kind":"unbounded"},"start":{"kind":"inclusive","limit":"2001-02-15T00:00Z"}}}},"properties":{"http://localhost:3000/@alice/types/property-type/name/":"Alice"}} {"type":"relation","namespace":"entity","object":"00000001-0001-0000-0000-000000000000","relation":"owner","subject":{"kind":"web","subjectId":"00000000-0001-0000-0000-000000000000"}} {"type":"relation","namespace":"entity","object":"00000001-0001-0000-0000-000000000000","relation":"administrator","subject":{"kind":"account","subjectId":"00000000-0001-0000-0000-000000000000"}} diff --git a/tests/hash-graph-http/tests/circular-links.http b/tests/hash-graph-http/tests/circular-links.http index b313eee6123..f232d1d9a5d 100644 --- a/tests/hash-graph-http/tests/circular-links.http +++ b/tests/hash-graph-http/tests/circular-links.http @@ -46,6 +46,7 @@ X-Authenticated-User-Actor-Id: {{account_id}} "$id": "https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1", "type": "object", "title": "Link", + "description": "The most generic connection between two entities, defining a relationship from a source to a target. It serves as a parent type for more specific link entity types, enabling consistent and interoperable data relationships.", "properties": {} }, "relationships": [{ @@ -76,6 +77,7 @@ X-Authenticated-User-Actor-Id: {{account_id}} "$id": "http://localhost:3000/@snapshot/types/entity-type/link/v/1", "type": "object", "title": "Object", + "description": "A link entity type", "allOf": [{ "$ref": "https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1" }], "properties": {} }, @@ -129,6 +131,7 @@ X-Authenticated-User-Actor-Id: {{account_id}} "$id": "http://localhost:3000/@snapshot/types/entity-type/object/v/1", "type": "object", "title": "Object", + "description": "An object", "properties": {}, "links": { "http://localhost:3000/@snapshot/types/entity-type/link/v/1": { diff --git a/tests/hash-graph-http/tests/friendship.http b/tests/hash-graph-http/tests/friendship.http index f6efcf488eb..f414a350492 100644 --- a/tests/hash-graph-http/tests/friendship.http +++ b/tests/hash-graph-http/tests/friendship.http @@ -588,6 +588,7 @@ X-Authenticated-User-Actor-Id: {{account_id}} "$id": "https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1", "type": "object", "title": "Link", + "description": "The most generic connection between two entities, defining a relationship from a source to a target. It serves as a parent type for more specific link entity types, enabling consistent and interoperable data relationships.", "properties": {} }, "relationships": [{ diff --git a/tests/hash-graph-http/tests/link-inheritance.http b/tests/hash-graph-http/tests/link-inheritance.http index fb9082ae9f6..e0bc0dc9eff 100644 --- a/tests/hash-graph-http/tests/link-inheritance.http +++ b/tests/hash-graph-http/tests/link-inheritance.http @@ -33,7 +33,7 @@ X-Authenticated-User-Actor-Id: {{account_id}} }); %} -### Insert link entity type +### Insert entity types POST http://127.0.0.1:4000/entity-types Content-Type: application/json Accept: application/json @@ -46,7 +46,8 @@ X-Authenticated-User-Actor-Id: {{account_id}} "kind": "entityType", "$id": "http://localhost:3000/@snapshot/types/entity-type/left1/v/1", "type": "object", - "title": "Object", + "title": "Left1", + "description": "The first left entity type", "properties": {}, "links": { "http://localhost:3000/@snapshot/types/entity-type/link1/v/1": { @@ -65,7 +66,8 @@ X-Authenticated-User-Actor-Id: {{account_id}} "kind": "entityType", "$id": "http://localhost:3000/@snapshot/types/entity-type/left2/v/1", "type": "object", - "title": "Object", + "title": "Left2", + "description": "The second left entity type", "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/left1/v/1" }], "properties": {}, "links": { @@ -85,7 +87,8 @@ X-Authenticated-User-Actor-Id: {{account_id}} "kind": "entityType", "$id": "http://localhost:3000/@snapshot/types/entity-type/left3/v/1", "type": "object", - "title": "Object", + "title": "Left3", + "description": "The third left entity type", "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/left2/v/1" }], "properties": {}, "links": { @@ -106,7 +109,8 @@ X-Authenticated-User-Actor-Id: {{account_id}} "kind": "entityType", "$id": "http://localhost:3000/@snapshot/types/entity-type/link1/v/1", "type": "object", - "title": "Object", + "title": "Link1", + "description": "A link between `Left1` and `Right1`", "allOf": [{ "$ref": "https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1" }], "properties": {} }, { @@ -114,7 +118,8 @@ X-Authenticated-User-Actor-Id: {{account_id}} "kind": "entityType", "$id": "http://localhost:3000/@snapshot/types/entity-type/link2/v/1", "type": "object", - "title": "Object", + "title": "Link2", + "description": "A link between `Left2` and `Right2`", "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/link1/v/1" }], "properties": {} }, { @@ -122,7 +127,8 @@ X-Authenticated-User-Actor-Id: {{account_id}} "kind": "entityType", "$id": "http://localhost:3000/@snapshot/types/entity-type/link3/v/1", "type": "object", - "title": "Object", + "title": "Link3", + "description": "A link between `Left3` and `Right3`", "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/link2/v/1" }], "properties": {} }, @@ -131,14 +137,16 @@ X-Authenticated-User-Actor-Id: {{account_id}} "kind": "entityType", "$id": "http://localhost:3000/@snapshot/types/entity-type/right1/v/1", "type": "object", - "title": "Object", + "title": "Right1", + "description": "The first right entity type", "properties": {} }, { "$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type", "kind": "entityType", "$id": "http://localhost:3000/@snapshot/types/entity-type/right2/v/1", "type": "object", - "title": "Object", + "title": "Right2", + "description": "The second right entity type", "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/right1/v/1" }], "properties": {} }, { @@ -146,7 +154,8 @@ X-Authenticated-User-Actor-Id: {{account_id}} "kind": "entityType", "$id": "http://localhost:3000/@snapshot/types/entity-type/right3/v/1", "type": "object", - "title": "Object", + "title": "Right3", + "description": "The third right entity type", "allOf": [{ "$ref": "http://localhost:3000/@snapshot/types/entity-type/right2/v/1" }], "properties": {} }], diff --git a/tests/hash-graph-test-data/rust/src/entity_type/block.json b/tests/hash-graph-test-data/rust/src/entity_type/block.json index 3e2acc9055a..0ea2ebb4804 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/block.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/block.json @@ -4,6 +4,7 @@ "$id": "https://blockprotocol.org/@alice/types/entity-type/block/v/1", "title": "Block", "type": "object", + "description": "A block as defined by the Block Protocol.", "properties": { "https://blockprotocol.org/@alice/types/property-type/name/": { "$ref": "https://blockprotocol.org/@alice/types/property-type/name/v/1" diff --git a/tests/hash-graph-test-data/rust/src/entity_type/book.json b/tests/hash-graph-test-data/rust/src/entity_type/book.json index 50e40e4ddae..d29ee812c2c 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/book.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/book.json @@ -3,6 +3,7 @@ "kind": "entityType", "$id": "https://blockprotocol.org/@alice/types/entity-type/book/v/1", "title": "Book", + "description": "A book is a written work that is published in print or electronic form.", "type": "object", "properties": { "https://blockprotocol.org/@alice/types/property-type/name/": { diff --git a/tests/hash-graph-test-data/rust/src/entity_type/building.json b/tests/hash-graph-test-data/rust/src/entity_type/building.json index 8380f542830..a6590095d01 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/building.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/building.json @@ -4,6 +4,7 @@ "$id": "https://blockprotocol.org/@alice/types/entity-type/building/v/1", "type": "object", "title": "Building", + "description": "A building", "properties": { "https://blockprotocol.org/@alice/types/property-type/built-at/": { "$ref": "https://blockprotocol.org/@alice/types/property-type/built-at/v/1" diff --git a/tests/hash-graph-test-data/rust/src/entity_type/church.json b/tests/hash-graph-test-data/rust/src/entity_type/church.json index 7ae872e9bc7..458bd8b5fa5 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/church.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/church.json @@ -4,6 +4,7 @@ "$id": "https://blockprotocol.org/@alice/types/entity-type/church/v/1", "type": "object", "title": "Building", + "description": "A church is a building that is used for religious activities, particularly worship services.", "allOf": [ { "$ref": "https://blockprotocol.org/@alice/types/entity-type/building/v/1" diff --git a/tests/hash-graph-test-data/rust/src/entity_type/link.json b/tests/hash-graph-test-data/rust/src/entity_type/link.json index 204c38f4ee6..67790f8edd0 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/link.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/link.json @@ -4,5 +4,6 @@ "$id": "https://blockprotocol.org/@blockprotocol/types/entity-type/link/v/1", "type": "object", "title": "Link", + "description": "The most generic connection between two entities, defining a relationship from a source to a target. It serves as a parent type for more specific link entity types, enabling consistent and interoperable data relationships.", "properties": {} } diff --git a/tests/hash-graph-test-data/rust/src/entity_type/organization.json b/tests/hash-graph-test-data/rust/src/entity_type/organization.json index a2afe2be8f1..dfa8e92ce17 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/organization.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/organization.json @@ -4,6 +4,7 @@ "$id": "https://blockprotocol.org/@alice/types/entity-type/organization/v/1", "type": "object", "title": "Organization", + "description": "An organization is a group of people who come together to achieve a common goal.", "properties": { "https://blockprotocol.org/@alice/types/property-type/name/": { "$ref": "https://blockprotocol.org/@alice/types/property-type/name/v/1" diff --git a/tests/hash-graph-test-data/rust/src/entity_type/page_v1.json b/tests/hash-graph-test-data/rust/src/entity_type/page_v1.json index 658ca643d9c..56cdcc948fa 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/page_v1.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/page_v1.json @@ -4,6 +4,7 @@ "$id": "https://blockprotocol.org/@alice/types/entity-type/page/v/1", "type": "object", "title": "Page", + "description": "A page is a single page of content.", "properties": { "https://blockprotocol.org/@alice/types/property-type/text/": { "$ref": "https://blockprotocol.org/@alice/types/property-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/entity_type/page_v2.json b/tests/hash-graph-test-data/rust/src/entity_type/page_v2.json index 7734f5e2317..d518f654214 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/page_v2.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/page_v2.json @@ -4,6 +4,7 @@ "$id": "https://blockprotocol.org/@alice/types/entity-type/page/v/2", "type": "object", "title": "Page", + "description": "A page is a single page of content.", "properties": { "https://blockprotocol.org/@alice/types/property-type/text/": { "$ref": "https://blockprotocol.org/@alice/types/property-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/entity_type/person.json b/tests/hash-graph-test-data/rust/src/entity_type/person.json index ee87f60f386..ae79c31393d 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/person.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/person.json @@ -4,6 +4,7 @@ "$id": "https://blockprotocol.org/@alice/types/entity-type/person/v/1", "type": "object", "title": "Person", + "description": "A person.", "properties": { "https://blockprotocol.org/@alice/types/property-type/name/": { "$ref": "https://blockprotocol.org/@alice/types/property-type/name/v/1" diff --git a/tests/hash-graph-test-data/rust/src/entity_type/playlist.json b/tests/hash-graph-test-data/rust/src/entity_type/playlist.json index 9e0e69cc8d9..47c262fe78b 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/playlist.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/playlist.json @@ -4,6 +4,7 @@ "$id": "https://blockprotocol.org/@alice/types/entity-type/playlist/v/1", "type": "object", "title": "Playlist", + "description": "A collection of songs.", "properties": { "https://blockprotocol.org/@alice/types/property-type/name/": { "$ref": "https://blockprotocol.org/@alice/types/property-type/name/v/1" diff --git a/tests/hash-graph-test-data/rust/src/entity_type/song.json b/tests/hash-graph-test-data/rust/src/entity_type/song.json index 289d5b23e0c..b74cfc92376 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/song.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/song.json @@ -4,6 +4,7 @@ "$id": "https://blockprotocol.org/@alice/types/entity-type/song/v/1", "type": "object", "title": "Song", + "description": "A song", "properties": { "https://blockprotocol.org/@alice/types/property-type/name/": { "$ref": "https://blockprotocol.org/@alice/types/property-type/name/v/1" diff --git a/tests/hash-graph-test-data/rust/src/entity_type/uk_address.json b/tests/hash-graph-test-data/rust/src/entity_type/uk_address.json index e1fffbcd096..56a57e89cee 100644 --- a/tests/hash-graph-test-data/rust/src/entity_type/uk_address.json +++ b/tests/hash-graph-test-data/rust/src/entity_type/uk_address.json @@ -4,6 +4,7 @@ "$id": "https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1", "type": "object", "title": "UK Address", + "description": "A UK address", "properties": { "https://blockprotocol.org/@alice/types/property-type/address-line-1/": { "$ref": "https://blockprotocol.org/@alice/types/property-type/address-line-1/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/address_line_1.json b/tests/hash-graph-test-data/rust/src/property_type/address_line_1.json index dbcba1b980a..f94c034fb6d 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/address_line_1.json +++ b/tests/hash-graph-test-data/rust/src/property_type/address_line_1.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/address-line-1/v/1", "title": "Address Line 1", + "description": "The first line of an address.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/age.json b/tests/hash-graph-test-data/rust/src/property_type/age.json index 03ec23c3054..d2b2caab776 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/age.json +++ b/tests/hash-graph-test-data/rust/src/property_type/age.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/age/v/1", "title": "Age", + "description": "The age of a person in years.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/number/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/blurb.json b/tests/hash-graph-test-data/rust/src/property_type/blurb.json index f97e064818a..1dbc8d6c1b9 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/blurb.json +++ b/tests/hash-graph-test-data/rust/src/property_type/blurb.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/blurb/v/1", "title": "Blurb", + "description": "A short description of a property.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/built_at.json b/tests/hash-graph-test-data/rust/src/property_type/built_at.json index e519b38c6e0..45a0d3359d7 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/built_at.json +++ b/tests/hash-graph-test-data/rust/src/property_type/built_at.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/built-at/v/1", "title": "Built At", + "description": "The location where the property was built.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/city.json b/tests/hash-graph-test-data/rust/src/property_type/city.json index a943393076f..8ce51064734 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/city.json +++ b/tests/hash-graph-test-data/rust/src/property_type/city.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/city/v/1", "title": "City", + "description": "A city", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/contact_information.json b/tests/hash-graph-test-data/rust/src/property_type/contact_information.json index f80f1792db1..e81caa44154 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/contact_information.json +++ b/tests/hash-graph-test-data/rust/src/property_type/contact_information.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/contact-information/v/1", "title": "Contact Information", + "description": "A contact information property type that can be either an email or a phone number.", "oneOf": [ { "type": "object", diff --git a/tests/hash-graph-test-data/rust/src/property_type/contrived_property.json b/tests/hash-graph-test-data/rust/src/property_type/contrived_property.json index 8ae0b4b8ee2..7850b5404dc 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/contrived_property.json +++ b/tests/hash-graph-test-data/rust/src/property_type/contrived_property.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/contrived-property/v/1", "title": "Contrived Property", + "description": "A contrived property for testing purposes.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/number/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/email.json b/tests/hash-graph-test-data/rust/src/property_type/email.json index 7ec0f56cc4a..bb0141c417a 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/email.json +++ b/tests/hash-graph-test-data/rust/src/property_type/email.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/email/v/1", "title": "Email", + "description": "An email address.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/favorite_film.json b/tests/hash-graph-test-data/rust/src/property_type/favorite_film.json index bd7876d3203..c4f997cfe82 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/favorite_film.json +++ b/tests/hash-graph-test-data/rust/src/property_type/favorite_film.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/favorite-film/v/1", "title": "Favorite Film", + "description": "The favorite film of a person.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/favorite_quote.json b/tests/hash-graph-test-data/rust/src/property_type/favorite_quote.json index cea266daf33..fc1d3cf2ac6 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/favorite_quote.json +++ b/tests/hash-graph-test-data/rust/src/property_type/favorite_quote.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/favorite-quote/v/1", "title": "Favorite Quote", + "description": "A favorite quote of a person.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/favorite_song.json b/tests/hash-graph-test-data/rust/src/property_type/favorite_song.json index 0b13db3c4a4..57376255f70 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/favorite_song.json +++ b/tests/hash-graph-test-data/rust/src/property_type/favorite_song.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/favorite-song/v/1", "title": "Favorite Song", + "description": "The favorite song of a person.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/hobby.json b/tests/hash-graph-test-data/rust/src/property_type/hobby.json index 46e3e90bd3a..e426ca35bf1 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/hobby.json +++ b/tests/hash-graph-test-data/rust/src/property_type/hobby.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/hobby/v/1", "title": "Hobby", + "description": "An activity done regularly in one's leisure time for pleasure.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/interests.json b/tests/hash-graph-test-data/rust/src/property_type/interests.json index 96b1be524e4..bc1db656bfe 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/interests.json +++ b/tests/hash-graph-test-data/rust/src/property_type/interests.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/interests/v/1", "title": "Interests", + "description": "A collection of interests", "oneOf": [ { "type": "object", diff --git a/tests/hash-graph-test-data/rust/src/property_type/name.json b/tests/hash-graph-test-data/rust/src/property_type/name.json index 736dd253820..b3f8e626926 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/name.json +++ b/tests/hash-graph-test-data/rust/src/property_type/name.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/name/v/1", "title": "Name", + "description": "The name of something.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/numbers.json b/tests/hash-graph-test-data/rust/src/property_type/numbers.json index 52b64d6dc3b..af1451f4d86 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/numbers.json +++ b/tests/hash-graph-test-data/rust/src/property_type/numbers.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/numbers/v/1", "title": "Numbers", + "description": "A list of numbers.", "oneOf": [ { "type": "array", diff --git a/tests/hash-graph-test-data/rust/src/property_type/phone_number.json b/tests/hash-graph-test-data/rust/src/property_type/phone_number.json index 3964dc154ce..2410727d048 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/phone_number.json +++ b/tests/hash-graph-test-data/rust/src/property_type/phone_number.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/phone-number/v/1", "title": "Phone Number", + "description": "A phone number.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/postcode.json b/tests/hash-graph-test-data/rust/src/property_type/postcode.json index 8fa5f9cb112..0c055a1f78d 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/postcode.json +++ b/tests/hash-graph-test-data/rust/src/property_type/postcode.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/postcode/v/1", "title": "Postcode", + "description": "A postcode", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/published_on.json b/tests/hash-graph-test-data/rust/src/property_type/published_on.json index 1ac30d88c68..43dd0025e4c 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/published_on.json +++ b/tests/hash-graph-test-data/rust/src/property_type/published_on.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/published-on/v/1", "title": "Published On", + "description": "The date and time the content was published.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/text.json b/tests/hash-graph-test-data/rust/src/property_type/text.json index 4c6547e66ae..45dd36ac845 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/text.json +++ b/tests/hash-graph-test-data/rust/src/property_type/text.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/text/v/1", "title": "Text", + "description": "A text property type.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/user_id_v1.json b/tests/hash-graph-test-data/rust/src/property_type/user_id_v1.json index ce0a6f64750..24ceffca479 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/user_id_v1.json +++ b/tests/hash-graph-test-data/rust/src/property_type/user_id_v1.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/user-id/v/1", "title": "User ID", + "description": "A unique identifier for a user.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1" diff --git a/tests/hash-graph-test-data/rust/src/property_type/user_id_v2.json b/tests/hash-graph-test-data/rust/src/property_type/user_id_v2.json index 5d8050d1523..d367ba96a66 100644 --- a/tests/hash-graph-test-data/rust/src/property_type/user_id_v2.json +++ b/tests/hash-graph-test-data/rust/src/property_type/user_id_v2.json @@ -3,6 +3,7 @@ "kind": "propertyType", "$id": "https://blockprotocol.org/@alice/types/property-type/user-id/v/2", "title": "User ID", + "description": "A unique identifier for a user.", "oneOf": [ { "$ref": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1"