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

Expose the schema of a resource while being compiled by an extension compiler #151

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

vassilvk
Copy link

Sometimes an extension compiler needs to collect metadata about the instance of the keyword being compiled - most notably, its location.
With the proposed method, one can collect the location of the keyword in the compiler method like so:

func (c myCompiler) Compile(ctx jsonschema.CompilerContext, m map[string]interface{}) (jsonschema.ExtSchema, error) {
	if v, ok := m["myKeyword"]; ok {
		location := ctx.GetResourceSchema().Location
		...
	}
	...
}

@santhosh-tekuri
Copy link
Owner

Can you explain the use case why you need schema location in your case.

Ideally only keywords like $ref need such case. To handle such use cases we already have CompilerContext CompileRef

@vassilvk
Copy link
Author

Here's an example: One of the schema extensions I am building, uses external meta information stored in a map provided to the extension compiler. When the compiler compiles a given instance of the extension keyword, it needs to lookup this map based on the location of the keyword in the schema. The provided metadata is attached to the compiled schema and it is used by the schema's Validate method.

@santhosh-tekuri
Copy link
Owner

Can you give specific example.

I see none of the keywords in specification require this info

@vassilvk
Copy link
Author

vassilvk commented Mar 1, 2024

I see none of the keywords in specification require this info

I'm not sure what you mean by this. The example I gave above is specific to the validation I am building. That validation revolves around testing the validity of data based on an external piece of information which is stored in a dictionary whose keys are the paths to the schema elements that use the extension keyword.
I am afraid I am not in a position to disclose more than this. I was hoping to make it clear that there are use cases where extension developers might need more from the compilation context than just the action methods it provides.

@debuggerpk
Copy link

@santhosh-tekuri can you please provide an example of how to use CompilerContext

@santhosh-tekuri
Copy link
Owner

santhosh-tekuri commented Apr 21, 2024

@debuggerpk

for simple keywords like shown in example, you will not need CompilerContext

but if you want to implement keywords that have:

  • subschemas. for example properties keyword; CompilerContext.Compile method used in this case
  • refere to other subschemas. for example $ref keyword; CompileContext.CompileRef method used in this case

consider an example of implementing discriminator keyword; let us say you have array of vehicles as shown below:

[
    {
        "kind": "fuelled-vehicle",
        "fuel": "petrol"
    },
    {
        "kind": "electric-vehicle",
        "battery-capacity": "4000mah"
    }
]

we support two types of vehicles, fuelled-vehicle and electric-vehicle each has its own properties
based on the value of kind property you want to validate with specific vehicle type.

we can implement discriminator keyword for this:

{
     "type": "array",
     "items": {
        "discriminator": {
            "kind": {
                "fuelled-vehicle": {
                    "type": "object",
                    "required": ["fuel"]
                },
                "electric-vehicle": {
                    "type": "object",
                    "required": ["battery-capacity"]
                }
            }
        }
     }
}

below is the implementation:

type discriminator struct {
   property string
   valueSchemas map[string]*Schema
}

func (d * discriminator) Compile(ctx jsonschema.CompilerContext, m map[string]interface{}) (jsonschema.ExtSchema, error) {
    v, ok := m["discriminator"]
    If !ok {
        return nil, nil
    }
   vmap, ok := v.(map[string]interface{})
   if !ok {
       return nil, nil
   }

   for pname, pvalue := range map {
     d.property  = name
     obj := value.(map[string]interface{})
     for value, _ := range obj {
           sch, err = ctx.Compile("discriminator/"+value, true)
           if err!=nil {
                return nil, err
          }
         d.valueSchemas[value] = such
     }
      break
   }
}

suppose you want discriminator always refer to other schemas as shown below:

{
     "$defs": {
                "fuelled-vehicle": {
                    "type": "object",
                    "required": ["fuel"]
                },
                "electric-vehicle": {
                    "type": "object",
                    "required": ["battery-capacity"]
                }
     }
     "type": "array",
     "items": {
        "discriminator": {
            "kind": {
                "fuelled-vehicle": "#/$defs/fuelled-vehicle",
                "electric-vehicle": "#/$defs/electric-vehicle",
            }
        }
     }
}

to implement this you use CompilerContext.CompileRef instead of CompilerContext.Compile in above implementation as below:

func (d * discriminator) Compile(ctx jsonschema.CompilerContext, m map[string]interface{}) (jsonschema.ExtSchema, error) {
    v, ok := m["discriminator"]
    If !ok {
        return nil, nil
    }
   vmap, ok := v.(map[string]interface{})
   if !ok {
       return nil, nil
   }

   for pname, pvalue := range map {
     d.property  = name
     obj := value.(map[string]interface{})
     for value, ref := range obj {
           sch, err = ctx.CompileRef(ref, "discriminator/"+value, true)
           if err!=nil {
                return nil, err
          }
         d.valueSchemas[value] = such
     }
      break
   }
}

note that, I have not tested this functionality myself. but this is how it is supposed to be used.

@debuggerpk
Copy link

I am looking to bump cdevents https://github.com/cdevents/sdk-go to version 4. The error comes because this line https://github.com/cdevents/spec/blob/v0.4.0/schemas/artifactdeleted.json#L41

the "$ref": "/schema/links/embeddedlinksarray.json" is a reference to the respected file in the relative links forlder, but the loader cannot find the file. I guess the schema is not published.

I am looking for a way to create a map at compile time. i can use the map aswell, right?

// ysf

@santhosh-tekuri
Copy link
Owner

@debuggerpk
please ask the question in Discussions

That will help others with similar question. I will answer it there.

@debuggerpk
Copy link

Sorry for late reply. Link to discussion.

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.

3 participants