Skip to content

Commit 35ebfc9

Browse files
imsduSimon Dumas
andauthored
Index schemas again for forge compatibility (#5350)
* Index schemas again for forge compatibility --------- Co-authored-by: Simon Dumas <simon.dumas@senscience.ai>
1 parent e6da5fd commit 35ebfc9

File tree

7 files changed

+130
-195
lines changed

7 files changed

+130
-195
lines changed

delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/SchemasModule.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resources.{FetchResource, Resources, Va
2222
import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas.{SchemaDefinition, SchemaLog}
2323
import ch.epfl.bluebrain.nexus.delta.sdk.schemas.*
2424
import ch.epfl.bluebrain.nexus.delta.sdk.schemas.job.{SchemaValidationCoordinator, SchemaValidationStream}
25-
import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaEvent
25+
import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.{Schema, SchemaEvent}
2626
import ch.epfl.bluebrain.nexus.delta.sdk.sse.SseEncoder
2727
import ch.epfl.bluebrain.nexus.delta.sourcing.projections.{ProjectionErrors, Projections}
2828
import ch.epfl.bluebrain.nexus.delta.sourcing.stream.Supervisor
@@ -164,4 +164,10 @@ object SchemasModule extends ModuleDef {
164164
many[PriorityRoute].add { (route: SchemaJobRoutes) =>
165165
PriorityRoute(pluginsMaxPriority + 8, route.routes, requiresStrictEntity = true)
166166
}
167+
168+
make[Schema.Shift].from { (schemas: Schemas, base: BaseUri) =>
169+
Schema.shift(schemas)(base)
170+
}
171+
172+
many[ResourceShift[?, ?]].ref[Schema.Shift]
167173
}

delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/Schema.scala

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdOptions, T
1212
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteContextResolution}
1313
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.encoder.JsonLdEncoder
1414
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.{CompactedJsonLd, ExpandedJsonLd}
15+
import ch.epfl.bluebrain.nexus.delta.sdk.ResourceShift
1516
import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdContent
16-
import ch.epfl.bluebrain.nexus.delta.sdk.model.ResourceF
17+
import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, IdSegmentRef, ResourceF}
18+
import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas
1719
import ch.epfl.bluebrain.nexus.delta.sdk.syntax.*
1820
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, Tags}
1921
import io.circe.Json
@@ -90,4 +92,14 @@ object Schema {
9092
override def context(value: Schema): ContextValue =
9193
value.source.topContextValueOrEmpty.merge(ContextValue(contexts.shacl))
9294
}
95+
96+
type Shift = ResourceShift[SchemaState, Schema]
97+
98+
def shift(schemas: Schemas)(implicit baseUri: BaseUri): Shift =
99+
ResourceShift[SchemaState, Schema](
100+
Schemas.entityType,
101+
(ref, project) => schemas.fetch(IdSegmentRef(ref), project),
102+
state => state.toResource,
103+
value => JsonLdContent(value, value.value.source, value.value.tags)
104+
)
93105
}

tests/src/test/scala/ch/epfl/bluebrain/nexus/tests/HttpClient.scala

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,6 @@ class HttpClient private (baseUrl: Uri, httpExt: HttpExt)(implicit
6161
)(implicit um: FromEntityUnmarshaller[A]): IO[Assertion] =
6262
requestAssert(POST, url, Some(body), identity, extraHeaders)(assertResponse)
6363

64-
def postIO[A](url: String, body: IO[Json], identity: Identity, extraHeaders: Seq[HttpHeader] = jsonHeaders)(
65-
assertResponse: (A, HttpResponse) => Assertion
66-
)(implicit um: FromEntityUnmarshaller[A]): IO[Assertion] = {
67-
body.flatMap(body => requestAssert(POST, url, Some(body), identity, extraHeaders)(assertResponse))
68-
}
69-
7064
def putJsonAndStatus(url: String, body: Json, identity: Identity): IO[(Json, StatusCode)] = {
7165
requestJsonAndStatus(PUT, url, Some(body), identity, jsonHeaders)
7266
}
@@ -92,12 +86,6 @@ class HttpClient private (baseUrl: Uri, httpExt: HttpExt)(implicit
9286
)(implicit um: FromEntityUnmarshaller[A]): IO[Assertion] =
9387
requestAssert(PUT, url, None, identity, extraHeaders)(assertResponse)
9488

95-
def putIO[A](url: String, body: IO[Json], identity: Identity, extraHeaders: Seq[HttpHeader] = jsonHeaders)(
96-
assertResponse: (A, HttpResponse) => Assertion
97-
)(implicit um: FromEntityUnmarshaller[A]): IO[Assertion] = {
98-
body.flatMap(body => requestAssert(PUT, url, Some(body), identity, extraHeaders)(assertResponse))
99-
}
100-
10189
def putAttachmentFromPath[A](
10290
url: String,
10391
path: Path,

tests/src/test/scala/ch/epfl/bluebrain/nexus/tests/Optics.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,12 @@ object Optics {
178178
)
179179
}
180180

181+
object sparql {
182+
183+
val countResult: Json => Option[Int] = root.results.bindings(0).count.value.string.getOption(_).map(_.toInt)
184+
185+
}
186+
181187
object supervision {
182188

183189
/**

tests/src/test/scala/ch/epfl/bluebrain/nexus/tests/kg/SchemasSpec.scala

Lines changed: 77 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package ch.epfl.bluebrain.nexus.tests.kg
22

33
import akka.http.scaladsl.model.StatusCodes
44
import ch.epfl.bluebrain.nexus.delta.kernel.utils.UrlUtils.encodeUriPath
5-
import ch.epfl.bluebrain.nexus.tests.BaseIntegrationSpec
65
import ch.epfl.bluebrain.nexus.tests.Identity.Anonymous
76
import ch.epfl.bluebrain.nexus.tests.Identity.resources.Rick
7+
import ch.epfl.bluebrain.nexus.tests.Optics.sparql
88
import ch.epfl.bluebrain.nexus.tests.builders.SchemaPayloads
99
import ch.epfl.bluebrain.nexus.tests.builders.SchemaPayloads.*
10+
import ch.epfl.bluebrain.nexus.tests.{BaseIntegrationSpec, Optics}
1011
import io.circe.Json
1112
import io.circe.optics.JsonPath.root
1213

@@ -23,37 +24,27 @@ class SchemasSpec extends BaseIntegrationSpec {
2324

2425
"Schemas" should {
2526
"update a schema" in {
26-
val schemaId = "updatable-schema"
27+
val schemaId = "updatable-schema"
28+
val encodedId = encodeUriPath(schemaId)
2729

2830
def expectMinCount(json: Json, value: Int) =
2931
root.shapes.index(0).minCount.int.getOption(json) shouldEqual Some(value)
3032

3133
for {
32-
_ <- deltaClient.postIO[Json](s"/schemas/$project", withMinCount(schemaId, minCount = 1), Rick) {
33-
expectCreated
34-
}
35-
_ <-
36-
deltaClient
37-
.putIO[Json](
38-
s"/schemas/$project/${encodeUriPath(schemaId)}?rev=1",
39-
withMinCount(schemaId, minCount = 2),
40-
Rick
41-
) {
42-
expectOk
43-
}
44-
_ <- deltaClient.get[Json](s"/schemas/$project/${encodeUriPath(schemaId)}", Rick) { (json, response) =>
45-
response.status shouldEqual StatusCodes.OK
46-
expectMinCount(json, 2)
47-
}
34+
withMin1 <- withMinCount(schemaId, minCount = 1)
35+
_ <- deltaClient.post[Json](s"/schemas/$project", withMin1, Rick) { expectCreated }
36+
withMin2 <- withMinCount(schemaId, minCount = 2)
37+
_ <- deltaClient.put[Json](s"/schemas/$project/$encodedId?rev=1", withMin2, Rick) { expectOk }
38+
_ <- deltaClient.get[Json](s"/schemas/$project/$encodedId", Rick) { (json, response) =>
39+
response.status shouldEqual StatusCodes.OK
40+
expectMinCount(json, 2)
41+
}
4842
} yield succeed
4943
}
5044

5145
"fail creating a schema with a deprecated import" in {
52-
val baseSchemaId = "https://localhost/base-schema"
53-
val baseSchemaPayload = withPowerLevelShape(id = baseSchemaId, maxPowerLevel = 10000).accepted
54-
val importingSchemaId = "https://localhost/importing-schema"
55-
val importingSchemaPayload =
56-
withImportOfPowerLevelShape(id = importingSchemaId, importedSchemaId = baseSchemaId).accepted
46+
val baseSchemaId = "https://localhost/base-schema"
47+
val importingSchemaId = "https://localhost/importing-schema"
5748

5849
def checkDeprecationError(json: Json) = {
5950
val rejectionsRoot = root.schemaImports.report.history.index(0).rejections
@@ -62,84 +53,77 @@ class SchemasSpec extends BaseIntegrationSpec {
6253
}
6354

6455
for {
65-
_ <- deltaClient.post[Json](s"/schemas/$project", baseSchemaPayload, Rick) { expectCreated }
66-
_ <- deltaClient.delete[Json](s"/schemas/$project/${encodeUriPath(baseSchemaId)}?rev=1", Rick) { expectOk }
67-
_ <- deltaClient.post[Json](s"/schemas/$project", importingSchemaPayload, Rick) { (json, response) =>
68-
response.status shouldEqual StatusCodes.BadRequest
69-
json should have(`@type`("InvalidSchemaResolution"))
70-
checkDeprecationError(json)
71-
}
56+
baseSchemaPayload <- withPowerLevelShape(id = baseSchemaId, maxPowerLevel = 10000)
57+
_ <- deltaClient.post[Json](s"/schemas/$project", baseSchemaPayload, Rick) { expectCreated }
58+
_ <- deltaClient.delete[Json](s"/schemas/$project/${encodeUriPath(baseSchemaId)}?rev=1", Rick) { expectOk }
59+
importingSchemaPayload <- withImportOfPowerLevelShape(id = importingSchemaId, importedSchemaId = baseSchemaId)
60+
_ <- deltaClient.post[Json](s"/schemas/$project", importingSchemaPayload, Rick) { (json, response) =>
61+
response.status shouldEqual StatusCodes.BadRequest
62+
json should have(`@type`("InvalidSchemaResolution"))
63+
checkDeprecationError(json)
64+
}
7265
} yield succeed
7366
}
7467

68+
"have indexed the schema in the default sparql namespace" in eventually {
69+
val query =
70+
s"""
71+
|prefix nxv: <https://bluebrain.github.io/nexus/vocabulary/>
72+
|SELECT (COUNT(*) as ?count)
73+
|WHERE { ?s a nxv:Schema }
74+
""".stripMargin
75+
deltaClient.sparqlQuery[Json](s"/views/$project/graph/sparql", query, Rick) { (json, response) =>
76+
response.status shouldEqual StatusCodes.OK
77+
sparql.countResult(json).value should be > 0
78+
}
79+
}
80+
7581
"refresh a schema" in {
7682
val powerLevelSchemaId = "https://dev.nexus.test.com/schema-with-power-level"
7783
val schemaId = "https://dev.nexus.test.com/refreshable-schema"
7884

79-
def resourceWithPowerLevel(id: String, powerLevel: Int) =
85+
val encodedPowerLevelSchemaId = encodeUriPath(powerLevelSchemaId)
86+
val encodedSchemaId = encodeUriPath(schemaId)
87+
88+
def genResource =
8089
jsonContentOf(
8190
"kg/resources/resource-with-power-level.json",
82-
"id" -> id,
83-
"powerLevel" -> powerLevel
91+
"id" -> genId(),
92+
"powerLevel" -> 9001
8493
)
8594

8695
for {
87-
_ <- deltaClient.postIO[Json](
88-
s"/schemas/$project",
89-
withPowerLevelShape(id = powerLevelSchemaId, maxPowerLevel = 10000),
90-
Rick
91-
) { expectCreated }
92-
_ <- deltaClient
93-
.postIO[Json](
94-
s"/schemas/$project",
95-
withImportOfPowerLevelShape(id = schemaId, importedSchemaId = powerLevelSchemaId),
96-
Rick
97-
) { expectCreated }
98-
_ <- deltaClient
99-
.post[Json](
100-
s"/resources/$project/${encodeUriPath(schemaId)}",
101-
resourceWithPowerLevel(genId(), 9001),
102-
Rick
103-
) { expectCreated }
104-
_ <- deltaClient
105-
.putIO[Json](
106-
s"/schemas/$project/${encodeUriPath(powerLevelSchemaId)}?rev=1",
107-
withPowerLevelShape(id = powerLevelSchemaId, maxPowerLevel = 9000),
108-
Rick
109-
) { expectOk }
110-
_ <- deltaClient
111-
.put[Json](s"/schemas/$project/${encodeUriPath(schemaId)}/refresh", Json.Null, Rick) { expectOk }
112-
_ <- deltaClient
113-
.post[Json](
114-
s"/resources/$project/${encodeUriPath(schemaId)}",
115-
resourceWithPowerLevel(genId(), 9001),
116-
Rick
117-
) { expectBadRequest }
96+
powerLevelPayload <- withPowerLevelShape(id = powerLevelSchemaId, maxPowerLevel = 10000)
97+
_ <- deltaClient.post[Json](s"/schemas/$project", powerLevelPayload, Rick) { expectCreated }
98+
withImportsPayload <- withImportOfPowerLevelShape(id = schemaId, importedSchemaId = powerLevelSchemaId)
99+
_ <- deltaClient.post[Json](s"/schemas/$project", withImportsPayload, Rick) { expectCreated }
100+
_ <- deltaClient.post[Json](s"/resources/$project/$encodedSchemaId", genResource, Rick) { expectCreated }
101+
powerLevelUpdated <- withPowerLevelShape(id = powerLevelSchemaId, maxPowerLevel = 9000)
102+
_ <- deltaClient.put[Json](s"/schemas/$project/$encodedPowerLevelSchemaId?rev=1", powerLevelUpdated, Rick) {
103+
expectOk
104+
}
105+
_ <- deltaClient.put[Json](s"/schemas/$project/$encodedSchemaId/refresh", Json.Null, Rick) { expectOk }
106+
_ <- deltaClient.post[Json](s"/resources/$project/$encodedSchemaId", genResource, Rick) { expectBadRequest }
118107
} yield succeed
119108
}
120109

121110
"when a resource is created, validate it against a schema" in {
122-
val schemaId = "bicycle-validation-schema"
111+
val schemaId = "bicycle-validation-schema"
112+
val encodedSchemaId = encodeUriPath(schemaId)
113+
114+
def bicycleSchema = jsonContentOf("kg/schemas/bicycle-schema.json", "id" -> schemaId, "maxNumberOfGears" -> 13)
115+
116+
def generateBicycleResource(gears: Int) =
117+
jsonContentOf("kg/resources/bicycle.json", "id" -> genId(), "gears" -> gears)
123118

124119
for {
125-
_ <- deltaClient
126-
.post[Json](
127-
s"/schemas/$project",
128-
jsonContentOf("kg/schemas/bicycle-schema.json", "id" -> schemaId, "maxNumberOfGears" -> 13),
129-
Rick
130-
) { expectCreated }
131-
_ <- deltaClient
132-
.post[Json](
133-
s"/resources/$project/${encodeUriPath(schemaId)}",
134-
jsonContentOf("kg/resources/bicycle.json", "id" -> genId(), "gears" -> 13),
135-
Rick
136-
) { expectCreated }
137-
_ <- deltaClient
138-
.post[Json](
139-
s"/resources/$project/${encodeUriPath(schemaId)}",
140-
jsonContentOf("kg/resources/bicycle.json", "id" -> genId(), "gears" -> 14),
141-
Rick
142-
) { expectBadRequest }
120+
_ <- deltaClient.post[Json](s"/schemas/$project", bicycleSchema, Rick) { expectCreated }
121+
_ <- deltaClient.post[Json](s"/resources/$project/$encodedSchemaId", generateBicycleResource(13), Rick) {
122+
expectCreated
123+
}
124+
_ <- deltaClient.post[Json](s"/resources/$project/$encodedSchemaId", generateBicycleResource(14), Rick) {
125+
expectBadRequest
126+
}
143127
} yield succeed
144128
}
145129

@@ -148,37 +132,24 @@ class SchemasSpec extends BaseIntegrationSpec {
148132
val schemaId12Gears = "bicycle-with-12-gears"
149133
val resourceId = "my-bike"
150134

135+
def bicycleSchema(schemaId: String, gears: Int) =
136+
jsonContentOf("kg/schemas/bicycle-schema.json", "id" -> schemaId, "maxNumberOfGears" -> gears)
137+
def bicycleResource = jsonContentOf("kg/resources/bicycle.json", "id" -> resourceId, "gears" -> 13)
138+
151139
for {
152-
_ <- deltaClient
153-
.post[Json](
154-
s"/schemas/$project",
155-
jsonContentOf("kg/schemas/bicycle-schema.json", "id" -> schemaId13Gears, "maxNumberOfGears" -> 13),
156-
Rick
157-
) { expectCreated }
158-
_ <- deltaClient
159-
.post[Json](
160-
s"/schemas/$project",
161-
jsonContentOf("kg/schemas/bicycle-schema.json", "id" -> schemaId12Gears, "maxNumberOfGears" -> 12),
162-
Rick
163-
) { expectCreated }
164-
_ <- deltaClient
165-
.post[Json](
166-
s"/resources/$project/$schemaId13Gears",
167-
jsonContentOf("kg/resources/bicycle.json", "id" -> resourceId, "gears" -> 13),
168-
Rick
169-
) { expectCreated }
140+
_ <- deltaClient.post[Json](s"/schemas/$project", bicycleSchema(schemaId13Gears, 13), Rick) { expectCreated }
141+
_ <- deltaClient.post[Json](s"/schemas/$project", bicycleSchema(schemaId12Gears, 12), Rick) { expectCreated }
142+
_ <- deltaClient.post[Json](s"/resources/$project/$schemaId13Gears", bicycleResource, Rick) { expectCreated }
170143
_ <- deltaClient.get[Json](s"/resources/$project/$schemaId13Gears/$resourceId/validate", Rick) {
171144
(json, response) =>
172145
response.status shouldEqual StatusCodes.OK
173-
json.hcursor.downField("@type").as[String].toOption shouldEqual Some("Validated")
146+
Optics.`@type`.getOption(json).value shouldEqual "Validated"
174147
}
175148
_ <- deltaClient.get[Json](s"/resources/$project/$schemaId12Gears/$resourceId/validate", Rick) {
176149
(json, response) =>
177150
response.status shouldEqual StatusCodes.BadRequest
178-
json should have(`@type`("InvalidResource"))
179-
json.hcursor.downField("details").downField("@type").as[String].toOption shouldEqual Some(
180-
"sh:ValidationReport"
181-
)
151+
Optics.`@type`.getOption(json).value shouldEqual "InvalidResource"
152+
root.details.`@type`.string.getOption(json).value shouldEqual "sh:ValidationReport"
182153
}
183154
} yield succeed
184155
}

0 commit comments

Comments
 (0)