Skip to content

Commit

Permalink
Address feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon Dumas committed Sep 20, 2023
1 parent e00938f commit 7393abc
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ final class ResourcesPracticeRoutes(
private def validateRoute: Route =
pathPrefix("resources") {
extractCaller { implicit caller =>
resolveProjectRef.apply { ref =>
authorizeFor(ref, Write).apply {
resolveProjectRef.apply { project =>
authorizeFor(project, Write).apply {
(get & idSegment & idSegmentRef & pathPrefix("validate") & pathEndOrSingleSlash) { (schema, id) =>
val schemaOpt = underscoreToOption(schema)
emit(
resourcesPractice.validate(id, ref, schemaOpt).leftWiden[ResourceRejection]
resourcesPractice.validate(id, project, schemaOpt).leftWiden[ResourceRejection]
)
}
}
Expand All @@ -78,10 +78,10 @@ final class ResourcesPracticeRoutes(
private def practiceRoute: Route =
(get & pathPrefix("practice") & pathPrefix("resources")) {
extractCaller { implicit caller =>
(resolveProjectRef & pathEndOrSingleSlash) { ref =>
authorizeFor(ref, Write).apply {
(resolveProjectRef & pathEndOrSingleSlash) { project =>
authorizeFor(project, Write).apply {
(entity(as[GenerationInput])) { input =>
generate(ref, input)
generate(project, input)
}
}
}
Expand Down
24 changes: 24 additions & 0 deletions delta/app/src/test/resources/practice/generated-resource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"@context": [
"https://bluebrain.github.io/nexus/contexts/metadata.json",
{
"@vocab": "https://bluebrain.github.io/nexus/vocabulary/"
},
"https://neuroshapes.org"
],
"@id": "https://bluebrain.github.io/nexus/vocabulary/myId",
"@type": "Morphology",
"name": "Morphology 001",
"_constrainedBy": "https://bluebrain.github.io/nexus/vocabulary/myschema",
"_createdAt": "1970-01-01T00:00:00Z",
"_createdBy": "http://localhost/v1/realms/wonderland/users/alice",
"_deprecated": false,
"_incoming": "http://localhost/v1/resources/myorg/myproj/_/https:%2F%2Fbluebrain.github.io%2Fnexus%2Fvocabulary%2FmyId/incoming",
"_outgoing": "http://localhost/v1/resources/myorg/myproj/_/https:%2F%2Fbluebrain.github.io%2Fnexus%2Fvocabulary%2FmyId/outgoing",
"_project": "http://localhost/v1/projects/myorg/myproj",
"_rev": 1,
"_schemaProject": "http://localhost/v1/projects/myorg/myproj",
"_self": "http://localhost/v1/resources/myorg/myproj/_/https:%2F%2Fbluebrain.github.io%2Fnexus%2Fvocabulary%2FmyId",
"_updatedAt": "1970-01-01T00:00:00Z",
"_updatedBy": "http://localhost/v1/realms/wonderland/users/alice"
}
26 changes: 0 additions & 26 deletions delta/app/src/test/resources/practice/resource-without-schema.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ class ResourcesPracticeRoutesSpec extends BaseRouteSpec with ResourceInstanceFix
val payload = json"""{ "resource": $validSource }"""
Get(s"/v1/practice/resources/$projectRef/", payload.toEntity) ~> asAlice ~> routes ~> check {
response.status shouldEqual StatusCodes.OK
response.asJson shouldEqual jsonContentOf("practice/resource-without-schema.json")
val jsonResponse = response.asJsonObject
jsonResponse("schema") shouldBe empty
jsonResponse("result") shouldEqual Some(jsonContentOf("practice/generated-resource.json"))
jsonResponse("error") shouldBe empty
}
}

Expand Down Expand Up @@ -204,8 +207,8 @@ class ResourcesPracticeRoutesSpec extends BaseRouteSpec with ResourceInstanceFix
}

s"successfully validate $myId for a user with access against the unconstrained schema" in {
val unconstrainedEncoded = UrlUtils.encode(schemas.resources.toString)
Get(s"/v1/resources/$projectRef/$unconstrainedEncoded/myId/validate") ~> asAlice ~> routes ~> check {
val unconstrained = UrlUtils.encode(schemas.resources.toString)
Get(s"/v1/resources/$projectRef/$unconstrained/myId/validate") ~> asAlice ~> routes ~> check {
response.status shouldEqual StatusCodes.OK
response.asJson shouldEqual
json"""{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class ResourcesRoutesSpec extends BaseRouteSpec with IOFromMap {
private val myIdEncoded = UrlUtils.encode(myId.toString)
private val myId2Encoded = UrlUtils.encode(myId2.toString)
private val payload = jsonContentOf("resources/resource.json", "id" -> myId)
private val payloadWithoutId = payload.removeKeys(keywords.id)
private val payloadWithBlankId = jsonContentOf("resources/resource.json", "id" -> "")
private val payloadWithUnderscoreFields =
jsonContentOf("resources/resource-with-underscore-fields.json", "id" -> myId5)
Expand Down Expand Up @@ -177,18 +178,15 @@ class ResourcesRoutesSpec extends BaseRouteSpec with IOFromMap {
"fail to create a resource that does not validate against a schema" in {
Put(
"/v1/resources/myorg/myproject/nxv:myschema/wrong",
payload.removeKeys(keywords.id).replaceKeyWithValue("number", "wrong").toEntity
payloadWithoutId.replaceKeyWithValue("number", "wrong").toEntity
) ~> routes ~> check {
response.status shouldEqual StatusCodes.BadRequest
response.asJson shouldEqual jsonContentOf("/resources/errors/invalid-resource.json")
}
}

"fail to create a resource against a schema that does not exist" in {
Put(
"/v1/resources/myorg/myproject/pretendschema/wrong",
payload.removeKeys(keywords.id).replaceKeyWithValue("number", "wrong").toEntity
) ~> routes ~> check {
Put("/v1/resources/myorg/myproject/pretendschema/", payloadWithoutId.toEntity) ~> routes ~> check {
status shouldEqual StatusCodes.NotFound
response.asJson shouldEqual jsonContentOf("/schemas/errors/invalid-schema-2.json")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@ import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{contexts, nxv, schema}
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdJavaApi}
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.JsonLdContext.keywords
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteContextResolution}
import ch.epfl.bluebrain.nexus.delta.sdk.SchemaResource
import ch.epfl.bluebrain.nexus.delta.sdk.generators.{ProjectGen, ResourceGen, SchemaGen}
import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller
import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContextDummy
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ApiMappings
import ch.epfl.bluebrain.nexus.delta.sdk.syntax._
import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverContextResolution
import ch.epfl.bluebrain.nexus.delta.sdk.resources.ValidationResult._
import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.{Resource, ResourceGenerationResult, ResourceRejection}
import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{InvalidResource, ProjectContextRejection, ReservedResourceId}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.ResourceRef.Revision
import ch.epfl.bluebrain.nexus.testkit.bio.BioSuite
import ch.epfl.bluebrain.nexus.testkit.{IOFixedClock, TestHelpers}
import monix.bio.IO
import monix.bio.{IO, UIO}
import munit.Location

import java.util.UUID

Expand Down Expand Up @@ -58,6 +61,22 @@ class ResourcesPracticeSuite extends BioSuite with ValidateResourceFixture with
private val source = NexusSource(jsonContentOf("resources/resource.json", "id" -> id))
private val resourceSchema = nxv + "schema"

private def assertSuccess(
io: UIO[ResourceGenerationResult]
)(schema: Option[SchemaResource], result: Resource)(implicit loc: Location) =
io.map { generated =>
assertEquals(generated.schema, schema)
assertEquals(generated.attempt.map(_.value), Right(result))
}

private def assertError(
io: UIO[ResourceGenerationResult]
)(schema: Option[SchemaResource], error: ResourceRejection)(implicit loc: Location) =
io.map { generated =>
assertEquals(generated.schema, schema)
assertEquals(generated.attempt.map(_.value), Left(error))
}

test("Successfully generates a resource") {
val practice = ResourcesPractice(
(_, _) => fetchResourceFail,
Expand All @@ -68,12 +87,7 @@ class ResourcesPracticeSuite extends BioSuite with ValidateResourceFixture with

val expectedData =
ResourceGen.resource(id, projectRef, source.value, Revision(resourceSchema, defaultSchemaRevision))

for {
generated <- practice.generate(projectRef, resourceSchema, source)
_ = assertEquals(generated.schema, None)
_ = assertEquals(generated.attempt.map(_.value), Right(expectedData))
} yield ()
assertSuccess(practice.generate(projectRef, resourceSchema, source))(None, expectedData)
}

test("Successfully generates a resource with a new schema") {
Expand All @@ -92,12 +106,7 @@ class ResourcesPracticeSuite extends BioSuite with ValidateResourceFixture with

val expectedData =
ResourceGen.resource(id, projectRef, source.value, Revision(anotherSchema, defaultSchemaRevision))

for {
generated <- practice.generate(projectRef, schema, source)
_ = assertEquals(generated.schema, Some(schema))
_ = assertEquals(generated.attempt.map(_.value), Right(expectedData))
} yield ()
assertSuccess(practice.generate(projectRef, schema, source))(Some(schema), expectedData)
}

test("Fail when validation raises an error") {
Expand All @@ -109,11 +118,7 @@ class ResourcesPracticeSuite extends BioSuite with ValidateResourceFixture with
resolverContextResolution
)

for {
generated <- practice.generate(projectRef, resourceSchema, source)
_ = assertEquals(generated.schema, None)
_ = assertEquals(generated.attempt, Left(expectedError))
} yield ()
assertError(practice.generate(projectRef, resourceSchema, source))(None, expectedError)
}

test("Validate a resource against a new schema reference") {
Expand Down
24 changes: 21 additions & 3 deletions docs/src/main/paradox/docs/delta/api/practice.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Practice
Practice operations contains read-only operations designed to help users compose and validate their
resources before effectively save them in Nexus.
Practice operations contain read-only operations designed to help users compose and validate their
resources before effectively saving them in Nexus.

@@@ note { .tip title="Authorization notes" }

Expand Down Expand Up @@ -35,6 +35,7 @@ This field is optional and defaults to no SHACL validation.
* `{resource}`: Json: The resource payload to test and validate

The Json response will contain:

* The generated resource in the compacted JSON-LD format if the generation and the validation was successful
* The generated schema if a new schema payload was provided
* The error if the one of the steps fails (invalid resource/invalid new schema/existing schema not found/...)
Expand All @@ -48,4 +49,21 @@ Payload
: @@snip [payload.json](assets/practice/resources/payload.json)

Response
: @@snip [created.json](assets/practice/resources/generated.json)
: @@snip [created.json](assets/practice/resources/generated.json)

## Validate

This operation runs validation of a resource against a schema. This would be useful to test whether resources would
match the shape of a new schema.

```
GET /v1/resources/{org_label}/{project_label}/{schema_id}/{resource_id}/validate
```

**Example**

Request
: @@snip [validate.sh](assets/resources/validate.sh)

Response
: @@snip [validated.json](assets/resources/validated.json)
18 changes: 0 additions & 18 deletions docs/src/main/paradox/docs/delta/api/resources-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,24 +148,6 @@ Request
Response
: @@snip [refreshed.json](assets/resources/updated.json)


## Validate

This operation runs validation of a resource against a schema. This would be useful to test whether resources would
match the shape of a new schema.

```
GET /v1/resources/{org_label}/{project_label}/{schema_id}/{resource_id}/validate
```

**Example**

Request
: @@snip [validate.sh](assets/resources/validate.sh)

Response
: @@snip [validated.json](assets/resources/validated.json)

## Tag

Links a resource revision to a specific name.
Expand Down

0 comments on commit 7393abc

Please sign in to comment.