1EdTech/openbadges-specification

OB3: Conflict between Data Model and Json Schema!

Opened this issue · 6 comments

Data Model specifies [0..1] or [1] options, Json Schema says AnyOf, which translates to [1..*] (all of the actual instances would be [1..2])
Conflict occurs in five occasions, all five conflicts are of the same type.
To me, it seems that both the data model and the Json Schema are wrong.
I think the Json Schema should use the word "OneOf" in all instances.
Also, the data model should say [1] in all instances.

  1. AchievementType

Data Model:
achievementType [AchievementType Enumeration] The type of achievement. This is an extensible vocabulary. [0..1]

Json Schema:

"achievementType": {
      "description": "The type of achievement. This is an extensible vocabulary.",
      "$comment": "Origin: AchievementType (EnumExt); The type of achievement, for example 'Award' or 'Certification'. This is an extensible enumerated vocabulary. Extending the vocabulary makes use of a naming convention.",
      "anyOf": [  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<---------------------------------------------------------------------------------------------------
        {
          "type": "string",
          "enum": [
            "Achievement",
            "ApprenticeshipCertificate",
            "Assessment",
            "Assignment",
            "AssociateDegree",
            "Award",
            "Badge",
            "BachelorDegree",
            "Certificate",
            "CertificateOfCompletion",
            "Certification",
            "CommunityService",
            "Competency",
            "Course",
            "CoCurricular",
            "Degree",
            "Diploma",
            "DoctoralDegree",
            "Fieldwork",
            "GeneralEducationDevelopment",
            "JourneymanCertificate",
            "LearningProgram",
            "License",
            "Membership",
            "ProfessionalDoctorate",
            "QualityAssuranceCredential",
            "MasterCertificate",
            "MasterDegree",
            "MicroCredential",
            "ResearchDoctorate",
            "SecondarySchoolDiploma"
          ]
        },
        {
          "type": "string",
          "pattern": "(ext:)[a-z|A-Z|0-9|.|-|_]+"
        }
  1. AlignmentTargetType

Exact same situation

  1. IdentifierEntryType

Data Model:
identifierType [IdentifierTypeEnum Enumeration] The identifier type. [1]

Json Schema:

"identifierType": {
      "description": "The identifier type.",
      "$comment": "Origin: IdentifierTypeEnum (EnumExt)",
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "name",
            .
            .
            .
          ]
        },
        {
          "type": "string",
          "pattern": "(ext:)[a-z|A-Z|0-9|.|-|_]+"
        }
      ]
  1. IdentityObjectType

Same situation as 3

  1. ResultDescriptionType

Same as 3 and 4

If the proposed solution is agreed upon, I can open a pull request for it as well.

My understanding is that the cardinality of anyOf refers to the valid underlaying subschemas and not about the cardinality of the attribute in the entity, which is defined by the array type.

So, if my understanding is correct, the current json schema is valid.

I think @Oran-Dan is right. For example, IdentityObject.identityType can either be one of the define terms (e.g. name) or an extension term (e.g. "ext:fullname"). The two subschemas are: 1) an enum, and 2) a string pattern. The JSON schema should be:

        "identityType": {
          "description": "The identity type.",
          "$comment": "Origin: IdentifierTypeEnum (EnumExt)",
          "oneOf": [
            {
              "type": "string",
              "enum": [
                "name",
                "sourcedId",
                "systemId",
                "productId",
                "userName",
                "accountId",
                "emailAddress",
                "nationalIdentityNumber",
                "isbn",
                "issn",
                "lisSourcedId",
                "oneRosterSourcedId",
                "sisSourcedId",
                "ltiContextId",
                "ltiDeploymentId",
                "ltiToolId",
                "ltiPlatformId",
                "ltiUserId",
                "identifier"
              ]
            },
            {
              "type": "string",
              "pattern": "(ext:)[a-z|A-Z|0-9|.|-|_]+"
            }
          ]
        }

I think that applies to all extensible types such a AlignmentTargetType. Values can only match the underlying type or the extension pattern, not both.

Since the extension pattern is not ever part of the underlying type, anyOf and oneOf don't any practical difference.

Agree that oneOf is best suitable for enumerations, althought both are equals in this kind of enumerations, where the additional values have a mandatory prefix. So, the existing schema is also valid.

My point was that either anyOf or oneOf don't have any effect on the cardinality of the attribute in the entity.

I will update the schemas to have oneOf.

Thanks for both your quick replies!
I forgot to add some context, which might clear up why I think it is important.

I'm a back-end developer trying to translate the Json Schema into Rust. The aim is to develop software which enables users to issue there own OpenBadges (to for example their students). We used the Typify library (https://crates.io/crates/typify/0.0.14) to convert the Json Schema to Rust code and it does so pretty well. But due to the cardinality specified, it had translated these types not to enums but to structs, which complicates things a bit. With enums the logical implications (OneOf) are obvious and require no extra coding. With structs however, it's a hassle to implement the rules as structs support a much wider, less straightforward, use.

I'm fairly certain the same goes for many other coding languages to which this Json Schema will be translated if others want to develop software to support OpenBadges v3.

Finally, I understand that it makes no difference to human logic wether it is AnyOf or OneOf, since it can't be both at the same time anyway. But hopefully the context provided has clarified that, to my opinion, it does matter when translating this to code.

Although, I must admit I don't fully understand your explanations Xavi, hopefully you can clarify.
The idea is that we have one single string entered into the achievementType field which complies with OneOf the 2 types, whether it be a defined term like "accountId" or an "ext:teamName", with the option of leaving it blank, am I right?
If so, I must correct myself, the Data Model specifies the correct cardinality of [0..1] for achievementType. Instances 3, 4 and 5, which I raised in my initial comment, are also OneOf the 2 types then but without the option of leaving it blank ( [1] ).

Is this a correct understanding of the Data Model and the Json Schema?

Thanks for your time and explanations.

Thanks @Oran-Dan for the clarification.

Your understanding is correct, achievementType is an optional field (it can be blank or not be at all), but when it's informed it must be one of the values in the enumeration (like accountId) or an extended term starting with "ext:", like `ext:teamName". This also applies to instance 2 in your initial comment.

On the other hand, instances 3, 4 and 5 in your initial comment are mandatory fields (so their cardinality is [1]). Their values must be one of the values in the enumeration or an extended term starting with "ext:".

None of the instances in your comment are collections that can hold multiple values.

We do have a data modeling system that autogenerates the JSON schemas. I'll make the proper changes to that system and publish the updated schemas.

Thanks a lot!