# SHACL Shapes generated by @inferal-relay/rdf-emitter

@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix tsps: <https://relay.inferal.com/ontology/typespec/shapes/> .
@prefix tspv: <https://relay.inferal.com/ontology/typespec/versioning/> .
@prefix relay: <https://relay.inferal.com/ontology/> .
@prefix schema: <https://schema.org/> .
@prefix prov: <http://www.w3.org/ns/prov#> .
@prefix ldes: <https://w3id.org/ldes#> .
@prefix tree: <https://w3id.org/tree#> .
@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix http: <http://www.w3.org/2011/http#> .
@prefix ontology: <https://relay.inferal.com/ontology/> .

# Class hierarchy
relay:Error rdfs:subClassOf schema:Thing .
relay:WebhookEvent rdfs:subClassOf http:Request, prov:Activity .
relay:Endpoint rdfs:subClassOf schema:EntryPoint .
relay:FormatConfig rdfs:subClassOf dcat:Distribution .
relay:Stream rdfs:subClassOf dcat:Dataset, ldes:EventStream .
tree:Node rdfs:subClassOf ldes:EventStream .

<https://relay.inferal.com/ontology/shapes> a tsps:ShapeGeneration ;
  tsps:shapeReferenceChainLimit 5 ;
  tsps:omittedShapeReferenceCount 0 .

# Shape for Relay Error
relay:ErrorShape a sh:NodeShape ;
  sh:targetClass relay:Error ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.RelayError/error> ;
    sh:name "error" ;
    sh:description "Error code (e.g., 'unauthorized', 'not_found')" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.RelayError/message> ;
    sh:name "message" ;
    sh:description "Human-readable error message" ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# Shape for Unauthorized Error
relay:UnauthorizedErrorShape a sh:NodeShape ;
  sh:targetClass relay:UnauthorizedError ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.UnauthorizedError/statusCode> ;
    sh:name "status code" ;
    sh:datatype xsd:integer ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue "401"^^xsd:integer ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.UnauthorizedError/error> ;
    sh:name "error" ;
    sh:description "Error code" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue "unauthorized" ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.UnauthorizedError/message> ;
    sh:name "message" ;
    sh:description "Error message" ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# Shape for Not Found Error
relay:NotFoundErrorShape a sh:NodeShape ;
  sh:targetClass relay:NotFoundError ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.NotFoundError/statusCode> ;
    sh:name "status code" ;
    sh:datatype xsd:integer ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue "404"^^xsd:integer ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.NotFoundError/error> ;
    sh:name "error" ;
    sh:description "Error code" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue "not_found" ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.NotFoundError/message> ;
    sh:name "message" ;
    sh:description "Error message" ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# Shape for Forbidden Error
relay:ForbiddenErrorShape a sh:NodeShape ;
  sh:targetClass relay:ForbiddenError ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.ForbiddenError/statusCode> ;
    sh:name "status code" ;
    sh:datatype xsd:integer ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue "403"^^xsd:integer ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.ForbiddenError/error> ;
    sh:name "error" ;
    sh:description "Error code" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue "forbidden" ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.ForbiddenError/message> ;
    sh:name "message" ;
    sh:description "Error message" ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# Shape for Bad Request Error
relay:BadRequestErrorShape a sh:NodeShape ;
  sh:targetClass relay:BadRequestError ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.BadRequestError/statusCode> ;
    sh:name "status code" ;
    sh:datatype xsd:integer ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue "400"^^xsd:integer ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.BadRequestError/error> ;
    sh:name "error" ;
    sh:description "Error code" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue "bad_request" ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.BadRequestError/message> ;
    sh:name "message" ;
    sh:description "Error message" ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# Shape for Not Acceptable Error
relay:NotAcceptableErrorShape a sh:NodeShape ;
  sh:targetClass relay:NotAcceptableError ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.NotAcceptableError/statusCode> ;
    sh:name "status code" ;
    sh:datatype xsd:integer ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue "406"^^xsd:integer ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.NotAcceptableError/error> ;
    sh:name "error" ;
    sh:description "Error code" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue "not_acceptable" ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.NotAcceptableError/message> ;
    sh:name "message" ;
    sh:description "Error message" ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# Shape for Stream Health Info
relay:StreamHealthShape a sh:NodeShape ;
  sh:targetClass relay:StreamHealth ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.StreamHealthInfo/gapDetectionEnabled> ;
    sh:name "gap detection enabled" ;
    sh:description "Whether gap detection is enabled for this stream" ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.StreamHealthInfo/knownGaps> ;
    sh:name "known gaps" ;
    sh:description "List of known gap window epochs in seconds" ;
    sh:datatype xsd:long ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.StreamHealthInfo/oldestTrackedWindow> ;
    sh:name "oldest tracked window" ;
    sh:description "Oldest window still being tracked (epoch in seconds)" ;
    sh:datatype xsd:long ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.StreamHealthInfo/highestTrackedWindow> ;
    sh:name "highest tracked window" ;
    sh:description "Highest window seen so far (epoch in seconds)" ;
    sh:datatype xsd:long ;
    sh:maxCount 1 ;
  ] .

# Shape for Webhook Event
relay:WebhookEventShape a sh:NodeShape ;
  sh:targetClass relay:WebhookEvent ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.WebhookEvent/id> ;
    sh:name "id" ;
    sh:description "Auto-incrementing event ID" ;
    sh:datatype xsd:long ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.WebhookEvent/timestamp> ;
    sh:name "timestamp" ;
    sh:description "Timestamp when the event was received (epoch milliseconds)" ;
    sh:datatype xsd:long ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.WebhookEvent/method> ;
    sh:name "method" ;
    sh:description "HTTP method of the request" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.WebhookEvent/path> ;
    sh:name "path" ;
    sh:description "Request path including query string" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.WebhookEvent/headers> ;
    sh:name "headers" ;
    sh:description "HTTP headers as a map of header name to array of values" ;
    sh:node <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/ContainerShape> ;
    sh:class <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/Container> ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.WebhookEvent/body> ;
    sh:name "body" ;
    sh:description "Request body (may be null for GET/DELETE requests)" ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# Shape for Events Response
relay:EventsResponseShape a sh:NodeShape ;
  sh:targetClass relay:EventsResponse ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.EventsResponse/events> ;
    sh:name "events" ;
    sh:description "Array of events matching the query" ;
    sh:node relay:WebhookEventShape ;
    sh:class relay:WebhookEvent ;
    sh:nodeKind sh:BlankNodeOrIRI ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.EventsResponse/cursor> ;
    sh:name "cursor" ;
    sh:description "Cursor for pagination - use as 'cursor' param in next request" ;
    sh:datatype xsd:long ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.EventsResponse/hasMore> ;
    sh:name "has more" ;
    sh:description "Whether there are more events available" ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

# Shape for Endpoint Info
relay:EndpointShape a sh:NodeShape ;
  sh:targetClass relay:Endpoint ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.EndpointInfo/hostname> ;
    sh:name "hostname" ;
    sh:description "Full hostname of the endpoint" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.EndpointInfo/createdAt> ;
    sh:name "created at" ;
    sh:description "When the endpoint was created (ISO8601)" ;
    sh:datatype xsd:dateTime ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

# Shape for Provision Request
relay:ProvisionRequestShape a sh:NodeShape ;
  sh:targetClass relay:ProvisionRequest ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.ProvisionRequest/hostname> ;
    sh:name "hostname" ;
    sh:description "Full hostname for the endpoint (e.g., 'a.foo.tenant.wh.relay.inferal.com')" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.ProvisionRequest/readKey> ;
    sh:name "read key" ;
    sh:description "Read key for accessing events from this endpoint" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

# Shape for Provision Response
relay:ProvisionResponseShape a sh:NodeShape ;
  sh:targetClass relay:ProvisionResponse ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.ProvisionResponse/hostname> ;
    sh:name "hostname" ;
    sh:description "The provisioned hostname" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.ProvisionResponse/created> ;
    sh:name "created" ;
    sh:description "Whether a new endpoint was created (vs updated)" ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

# Shape for Endpoints List Response
relay:EndpointsListResponseShape a sh:NodeShape ;
  sh:targetClass relay:EndpointsListResponse ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.EndpointsListResponse/endpoints> ;
    sh:name "endpoints" ;
    sh:description "Array of endpoint information" ;
    sh:node relay:EndpointShape ;
    sh:class relay:Endpoint ;
    sh:nodeKind sh:BlankNodeOrIRI ;
  ] .

# Shape for Archive Response
relay:ArchiveResponseShape a sh:NodeShape ;
  sh:targetClass relay:ArchiveResponse ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.ArchiveResponse/status> ;
    sh:name "status" ;
    sh:description "Status of the operation" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue "archived" ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.ArchiveResponse/hostname> ;
    sh:name "hostname" ;
    sh:description "Hostname that was archived" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.ArchiveResponse/eventsArchived> ;
    sh:name "events archived" ;
    sh:description "Number of events archived" ;
    sh:datatype xsd:long ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

# Shape for Format Config
relay:FormatConfigShape a sh:NodeShape ;
  sh:targetClass relay:FormatConfig ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.FormatConfig/contentType> ;
    sh:name "content type" ;
    sh:description "MIME content type for this format" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.FormatConfig/extension> ;
    sh:name "extension" ;
    sh:description "File extension for this format (e.g., '.jsonld', '.ttl')" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

# Shape for Stream Info
relay:StreamShape a sh:NodeShape ;
  sh:targetClass relay:Stream ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.StreamInfo/id> ;
    sh:name "id" ;
    sh:description "Stream identifier" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.StreamInfo/formats> ;
    sh:name "formats" ;
    sh:description "Available format names (e.g., 'jsonld', 'turtle', 'ntriples', 'json')" ;
    sh:datatype xsd:string ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.StreamInfo/shardCount> ;
    sh:name "shard count" ;
    sh:description "Shard count for WebSocket connections" ;
    sh:datatype xsd:int ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.StreamInfo/latestTimestamp> ;
    sh:name "latest timestamp" ;
    sh:description "Latest timestamp of data in the stream (epoch ms)" ;
    sh:datatype xsd:long ;
    sh:maxCount 1 ;
  ] .

# Shape for Stream List Response
relay:StreamListResponseShape a sh:NodeShape ;
  sh:targetClass relay:StreamListResponse ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.StreamListResponse/streams> ;
    sh:name "streams" ;
    sh:description "Array of accessible streams" ;
    sh:node relay:StreamShape ;
    sh:class relay:Stream ;
    sh:nodeKind sh:BlankNodeOrIRI ;
  ] .

# Shape for Ingest Request
relay:IngestRequestShape a sh:NodeShape ;
  sh:targetClass relay:IngestRequest ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.IngestRequest/member> ;
    sh:name "member" ;
    sh:description "LDES member to ingest (arbitrary JSON-LD object)" ;
    sh:node <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/ContainerShape> ;
    sh:class <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/Container> ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

# Shape for Ingest Response
relay:IngestResponseShape a sh:NodeShape ;
  sh:targetClass relay:IngestResponse ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.IngestResponse/accepted> ;
    sh:name "accepted" ;
    sh:description "Whether the event was accepted" ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:hasValue true ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.IngestResponse/eventId> ;
    sh:name "event id" ;
    sh:description "UUIDv7 event ID assigned to the ingested member" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.IngestResponse/windowEpoch> ;
    sh:name "window epoch" ;
    sh:description "Window epoch (seconds) the event was assigned to" ;
    sh:datatype xsd:long ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

# Shape for LDESMember
ldes:MemberShape a sh:NodeShape ;
  sh:targetClass ldes:Member ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.LDESMember/generatedAtTime> ;
    sh:name "generated at time" ;
    sh:description "When this member was generated" ;
    sh:datatype xsd:dateTime ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path relay:eventId ;
    sh:name "event id" ;
    sh:description "UUIDv7 event ID for pagination (lexicographic order = temporal order)" ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path relay:path ;
    sh:name "path" ;
    sh:description "Hierarchical path for this member (e.g., 'us-east/prod'). Omit for root level." ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# Shape for Tree Relation
tree:RelationShape a sh:NodeShape ;
  sh:targetClass tree:Relation ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.TreeRelation/node> ;
    sh:name "node" ;
    sh:description "URL to the related node" ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.TreeRelation/path> ;
    sh:name "path" ;
    sh:description "Property path this relation applies to" ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/property/InferalRelay.TreeRelation/value> ;
    sh:name "value" ;
    sh:description "Value for comparison" ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# Shape for LDESNode Response
tree:NodeShape a sh:NodeShape ;
  sh:targetClass tree:Node ;
  sh:property [
    sh:path tree:member ;
    sh:name "members" ;
    sh:description "Members in this node/page" ;
    sh:node ldes:MemberShape ;
    sh:class ldes:Member ;
    sh:nodeKind sh:BlankNodeOrIRI ;
  ] ;
  sh:property [
    sh:path tree:relation ;
    sh:name "relations" ;
    sh:description "Relations to other nodes for pagination" ;
    sh:node tree:RelationShape ;
    sh:class tree:Relation ;
    sh:nodeKind sh:BlankNodeOrIRI ;
  ] ;
  sh:property [
    sh:path ldes:timestampPath ;
    sh:name "timestamp path" ;
    sh:description "Timestamp path for LDES members (always {\"@id\": \"prov:generatedAtTime\"})" ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path relay:streamHealth ;
    sh:name "stream health" ;
    sh:description "Stream health info (when includeHealth=true)" ;
    sh:node relay:StreamHealthShape ;
    sh:class relay:StreamHealth ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:maxCount 1 ;
  ] .

# Generated Record container shape for InferalRelay.IngestRequest.member
<https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/ContainerShape> a sh:NodeShape ;
  sh:targetClass <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/Container> ;
  sh:property [
    sh:path [ sh:inversePath <https://relay.inferal.com/ontology/property/InferalRelay.IngestRequest/member> ] ;
    sh:class relay:IngestRequest ;
    sh:minCount 1 ;
    sh:maxCount 1
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/entry> ;
    sh:node <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/EntryShape> ;
    sh:class <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/Entry> ;
    sh:nodeKind sh:BlankNodeOrIRI
  ] ;
  sh:sparql [
    a sh:SPARQLConstraint ;
    sh:message "Record entry keys must be unique" ;
    sh:select """SELECT $this ?key WHERE { $this <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/entry> ?left, ?right . FILTER(?left != ?right) ?left <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/key> ?key . ?right <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/key> ?key . }"""
  ] .

# Generated Record entry shape for InferalRelay.IngestRequest.member
<https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/EntryShape> a sh:NodeShape ;
  sh:targetClass <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/Entry> ;
  sh:property [
    sh:path [ sh:inversePath <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/entry> ] ;
    sh:class <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/Container> ;
    sh:minCount 1 ;
    sh:maxCount 1
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/key> ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/record/InferalRelay.IngestRequest/member/value> ;
    sh:datatype rdf:JSON ;
    sh:minCount 1 ;
    sh:maxCount 1
  ] .

# Generated Record container shape for InferalRelay.WebhookEvent.headers
<https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/ContainerShape> a sh:NodeShape ;
  sh:targetClass <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/Container> ;
  sh:property [
    sh:path [ sh:inversePath <https://relay.inferal.com/ontology/property/InferalRelay.WebhookEvent/headers> ] ;
    sh:class relay:WebhookEvent ;
    sh:minCount 1 ;
    sh:maxCount 1
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/entry> ;
    sh:node <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/EntryShape> ;
    sh:class <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/Entry> ;
    sh:nodeKind sh:BlankNodeOrIRI
  ] ;
  sh:sparql [
    a sh:SPARQLConstraint ;
    sh:message "Record entry keys must be unique" ;
    sh:select """SELECT $this ?key WHERE { $this <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/entry> ?left, ?right . FILTER(?left != ?right) ?left <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/key> ?key . ?right <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/key> ?key . }"""
  ] .

# Generated Record entry shape for InferalRelay.WebhookEvent.headers
<https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/EntryShape> a sh:NodeShape ;
  sh:targetClass <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/Entry> ;
  sh:property [
    sh:path [ sh:inversePath <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/entry> ] ;
    sh:class <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/Container> ;
    sh:minCount 1 ;
    sh:maxCount 1
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/key> ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1
  ] ;
  sh:property [
    sh:path <https://relay.inferal.com/ontology/record/InferalRelay.WebhookEvent/headers/value> ;
    sh:datatype xsd:string
  ] .
