Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not allow deprecated schemas/ontologies in imports #4391

Merged
merged 9 commits into from
Oct 25, 2023

Conversation

imsdu
Copy link
Contributor

@imsdu imsdu commented Oct 20, 2023

Fixes #1855

It is implemented at the resource resolution level as it felt weird to do it at the schema import part.
It is also not excluded that we exclude deprecated resources for other operations.

Copy link
Contributor

@shinyhappydan shinyhappydan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My guess is there's a reason why it makes more sense to do the exclusion in the resolution part, but I think it's a less straightforward system that doing it when importing from the schema. Is the resolution system another one of these features that isn't really used but adding complexity, or is this a more necessary one?

Comment on lines 147 to 156
for {
resourceOpt <- fetch(ref, projectRef)
result <- resourceOpt.traverse(checkLatestDeprecated(ref, projectRef, _))
} yield {
result match {
case None => ResolverReport.failed(resolver.id, projectRef -> ResolutionFetchRejection(ref, projectRef)) -> None
case Some(true) =>
ResolverReport.failed(resolver.id, projectRef -> ResourceIsDeprecated(ref.original, projectRef)) -> None
case Some(false) => ResolverReport.success(resolver.id, projectRef) -> resourceOpt
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite sure how to unpick this, but feels like it could be improved? If nothing else, result should be called isDeprecated or similar, checkLatestDeprecated could be more like isLatestRevisionDeprecated

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I forgot to get back to this one, I will see what I can do

Comment on lines 203 to 207
private def checkLatestDeprecated(resourceRef: ResourceRef, project: ProjectRef, resource: R) =
resourceRef match {
case _: ResourceRef.Latest => IO.pure(deprecationCheck(resource))
case _ => fetch(ResourceRef.Latest(resourceRef.original), project).map(_.exists(deprecationCheck(_)))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok so my previous comment was slightly wrong. I would argue here that this should be called something like shouldBeDisallowed, because the current method name suggests the result is whether or not the resource is deprecated. I can see that by putting check you're trying to communicate this, but I think maybe it needs to be taken a step further

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldDisallowDueToDeprecation?

.downField("minCount")
.as[Int]
.toOption shouldEqual Some(2)
expectMinCount(json, 2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice change

_ <- deltaClient.delete[Json](s"/schemas/$project/${UrlUtils.encode(baseSchemaId)}?rev=1", Rick) { expectOk }
_ <- deltaClient.post[Json](s"/schemas/$project", importingSchemaPayload, Rick) { (json, response) =>
response.status shouldEqual StatusCodes.BadRequest
`@type`.getOption(json) shouldEqual Some("InvalidSchemaResolution")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the error not be specifically that the import was deprecated?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still feels like the wrong error to receive

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because we accumulate errors here, we can have an error because an import is deprecated and another because another one is not found at all.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't the user need the information in order to fix their problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The information is there, you can see it in the diff mentioning the report that is included in the payload.
I have also added a check to look for this specific error in the report

@imsdu
Copy link
Contributor Author

imsdu commented Oct 20, 2023

My guess is there's a reason why it makes more sense to do the exclusion in the resolution part, but I think it's a less straightforward system that doing it when importing from the schema. Is the resolution system another one of these features that isn't really used but adding complexity, or is this a more necessary one?

Resolution is necessary and is widely used, we rely on it to resolve remote contexts in resources, imports in schemas, there are a couple of endpoints depending on it to help people find resources, ...

@shinyhappydan
Copy link
Contributor

My guess is there's a reason why it makes more sense to do the exclusion in the resolution part, but I think it's a less straightforward system that doing it when importing from the schema. Is the resolution system another one of these features that isn't really used but adding complexity, or is this a more necessary one?

Resolution is necessary and is widely used, we rely on it to resolve remote contexts in resources, imports in schemas, there are a couple of endpoints depending on it to help people find resources, ...

If that is the case then isn't it better to have this functionality localised to where it's used, rather than in this more general system

Comment on lines +147 to +159
for {
resourceOpt <- fetch(ref, projectRef)
result <- resourceOpt.traverse(runDeprecationCheck(ref, projectRef, _))
} yield {
result match {
// The resource has not been found in the project
case None => ResolverReport.failed(resolver.id, projectRef -> ResolutionFetchRejection(ref, projectRef)) -> None
// The resource exists but the deprecation check is positive so we reject it
case Some(true) =>
ResolverReport.failed(resolver.id, projectRef -> ResourceIsDeprecated(ref.original, projectRef)) -> None
// The resource has been successfully resolved
case Some(false) => ResolverReport.success(resolver.id, projectRef) -> resourceOpt
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think by using meaningful variable and method names you don't really need the comments here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried and it either had a negative impact on the cross-project resolver part or it was not as clear as adding comments

Copy link
Contributor

@shinyhappydan shinyhappydan Oct 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this?

    fetch(ref, projectRef).flatMap {
      case None => IO.pure(ResolverReport.failed(resolver.id, projectRef -> ResolutionFetchRejection(ref, projectRef)) -> None)
      case Some(resource) => shouldExcludeDueToDeprecation(ref, projectRef, resource)).map {
        case Exclude => ResolverReport.failed(resolver.id, projectRef -> ResourceIsDeprecated(ref.original, projectRef)) -> None
        case Include => ResolverReport.success(resolver.id, projectRef) -> Some(resource)
      }
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not look really clearer and it will have an impact on the cross-project resolver side

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully understand the impact the change would have on cross-project resolver 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obviously this is all a matter of opinion, but the ways in which I would see it as objectively clearer:

  • No comments needed
  • Boolean matching removed for clearer case objects
  • resourceOpt is only used when it is Some, this is fixed
  • shouldExcludeDueToDeprecation is clearer than runDeprecationCheck as it is explaining the check
  • Traversal is maybe not a big deal but it is a complication

I think we can move on for this PR, but the things I have mentioned are things which in general make the codebase we have difficult to understand/navigate. They are all small things by themselves, but they add up.

I'm just posting this to attempt to express how I feel about it, I think it's fine to disagree

Comment on lines 192 to 193
resourceOpt <- fetch(ref, projectRef)
resource <- IO.fromOption(resourceOpt)(ResolutionFetchRejection(ref, projectRef))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I preferred this previously. An option would be to make a method to hide the boilerplate

Comment on lines 195 to 196
isDeprecated <- runDeprecationCheck(ref, projectRef, resource)
_ <- IO.raiseWhen(isDeprecated)(ResourceIsDeprecated(ref.original, projectRef))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another instance where a method can hide the boilerplate

_ <- deltaClient.delete[Json](s"/schemas/$project/${UrlUtils.encode(baseSchemaId)}?rev=1", Rick) { expectOk }
_ <- deltaClient.post[Json](s"/schemas/$project", importingSchemaPayload, Rick) { (json, response) =>
response.status shouldEqual StatusCodes.BadRequest
`@type`.getOption(json) shouldEqual Some("InvalidSchemaResolution")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still feels like the wrong error to receive

@imsdu
Copy link
Contributor Author

imsdu commented Oct 24, 2023

My guess is there's a reason why it makes more sense to do the exclusion in the resolution part, but I think it's a less straightforward system that doing it when importing from the schema. Is the resolution system another one of these features that isn't really used but adding complexity, or is this a more necessary one?

Resolution is necessary and is widely used, we rely on it to resolve remote contexts in resources, imports in schemas, there are a couple of endpoints depending on it to help people find resources, ...

If that is the case then isn't it better to have this functionality localised to where it's used, rather than in this more general system

  • This feature can be interesting in other cases such as context resolution when creating/updating resources
  • Schema imports does not know how to resolve and fetch schemas and ontologies, it would not be able to fetch the latest version to check the deprecation status
  • This way, it fits well in the error report returned whenever the resolution of imports fails

@shinyhappydan
Copy link
Contributor

shinyhappydan commented Oct 24, 2023

  • This feature can be interesting in other cases such as context resolution when creating/updating resources

If no other feature is using it right now I would say YAGNI

  • Schema imports does not know how to resolve and fetch schemas and ontologies, it would not be able to fetch

the latest version to check the deprecation status
We could give it the ability to check the deprecation status of the latest revision

It's just an option. Because this feels like quite a complex solution for what on the face of it seems like a simple problem, so I think it's worth making sure we want to pay that price

@imsdu
Copy link
Contributor Author

imsdu commented Oct 24, 2023

  • This feature can be interesting in other cases such as context resolution when creating/updating resources

If no other feature is using it right now I would say YAGNI

I don't see why we would exclude deprecated schemas from imports and not deprecated contexts in resources.

  • Schema imports does not know how to resolve and fetch schemas and ontologies, it would not be able to fetch

the latest version to check the deprecation status We could give it the ability to check the deprecation status of the latest revision

It's just an option. Because this feels like quite a complex solution for what on the face of it seems like a simple problem, so I think it's worth making sure we want to pay that price

It is not so easy because resolvers are involved and schemas imports role is limited to accumulate imports or errors

@imsdu imsdu merged commit 2f08298 into BlueBrain:master Oct 25, 2023
5 checks passed
@imsdu imsdu deleted the 1855-do-not-import-deprecated-schemas branch October 25, 2023 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deprecation status of imports in schemas
3 participants