Skip to content

(GH-538) Set URI and docs keywords for DscRepoSchema types - #1699

Draft
Mikey Lombardi (He/Him) (michaeltlombardi) wants to merge 8 commits into
PowerShell:mainfrom
michaeltlombardi:gh-538/main/set-ids
Draft

(GH-538) Set URI and docs keywords for DscRepoSchema types#1699
Mikey Lombardi (He/Him) (michaeltlombardi) wants to merge 8 commits into
PowerShell:mainfrom
michaeltlombardi:gh-538/main/set-ids

Conversation

@michaeltlombardi

Copy link
Copy Markdown
Collaborator

PR Summary

This change:

  1. Defines new extension methods for operating on schemars::Schema instances:

    • [get|set]_title - Retrieve and override the title keyword as a string slice.
    • [get|set]_description - Retrieve and override the description keyword as a string slice.
    • set_markdown_description - Override the markdownDescription keyword as a string slice.
    • [get|set]_meta_schema - Retrieve and override the $schema keyword as a string slice.
    • get_meta_schema_as_url - Retrieve the $schema keyword as a Url.
    • has_meta_schema_keyword - Indicate whether schema defines the $schema keyword.
  2. Defines new transformer functions for the DscRepoSchema trait:

    • transform_export_schema_uris - Insert the $id and $schema keywords with the default_export_* function outputs.
    • transform_schema_docs - Insert the title, description, and markdownDescription keywords if they have defined localization strings.
    • transform_schema_docs_strict - Insert the title, description, and markdownDescription keywords if they have defined localization strings. If any translation strings are missing, panic.
  3. Updates the type definitions in dsc-lib for every type that derives or implements DscRepoSchema to ensure that the $id and $schema keywords are always populated and the docs keywords are defined if the translation strings are provided. Where the type was already using translated docs this change uses the strict transform to catch regressions in translation string definitions.

PR Context

Prior to this change the generated schemas were not consistently defining the $id and $schema keywords, which we need to properly populate for every schema that we publish in its own file. Additionally, the documentation keywords were manually inserted for every schema (that has translated docs already defined).

Effectively, before this PR, every struct needed a definition like the following when deriving JsonSchema and DscRepoSchema:

#[derive(Debug, Clone, JsonSchema, DscRepoSchema)]
#[dsc_repo_schema(base_name = "struct", folder_path = "example")]
#[schemars(
    title = schema_i18n!("title"),
    description = schema_i18n("description"),
    extend(
        "$schema" = ExampleStruct::default_export_meta_schema_uri(),
        "$id"     = ExampleStruct::default_export_schema_id_uri(),
        "markdownDescription" = schema_i18n!("markdownDescription"),
    )
)]
pub struct ExampleStruct {
    // Elided for brevity
}

With these changes, the definition now looks like:

#[derive(Debug, Clone, JsonSchema, DscRepoSchema)]
#[dsc_repo_schema(base_name = "struct", folder_path = "example")]
#[schemars(
    transform = ExampleStruct::transform_export_schema_uris,
    transform = ExampleStruct::transform_schema_docs,
)]
pub struct ExampleStruct {
  // Elided for brevity
}

This corrects various problems in the schema generation and export pipeline, which relies on the $id keyword to correctly manage bundled schema resources, and ensures that we can update the docs keywords for types without needing to modify the type definition directly.

@michaeltlombardi

Copy link
Copy Markdown
Collaborator Author

Investigating test failures led me down a rabbit hole. In short, reference lookups begin failing for bundled schema resources when they define the $id keyword. Contributing factors:

  1. Schemars emits references to other defined types as `"$ref": "#/$defs/".

  2. Schemars inserts definitions for other defined types in $defs/<TypeName>.

  3. When resolving a reference that begins with #/ the resolution is against the current schema resource. For non-bundled schemas, this works fine with schemars/jsonschema because the only schema resource is the root document. For bundled schemas (which schemars generates for any type with fields/variants that also implement JsonSchema), this breaks resolution because references in a bundled schema resource resolve against that resource.

    For example, the references in this snippet all resolve:

    $id: https://contoso.com/schemas/example # root schema URI
    type: object
    properties:
      foo: { $ref: '#/$defs/foo' } # effectively 'https://contoso.com/schemas/example#/$defs/foo'
      bar: { $ref: 'https://contoso.com/schemas/fields/bar' }
    $defs:
      foo:
        type: object
        properties:
          baz: { $ref: '#/$defs/baz' } # effectively 'https://contoso.com/schemas/examples#/$defs/baz'
      bar:
        $id: https://contoso.com/schemas/fields/bar
        type: array
        items: { $ref: '/schemas/fields/baz' } # effectively 'https://contoso.com/schemas/fields/baz'
      baz:
        $id: https://contoso.com/schemas/fields/baz
        type: string
        pattern: '^\w+$'

    However, when we define $id for the foo definition, the schema is no longer valid:

    $id: https://contoso.com/schemas/example # root schema URI
    type: object
    properties:
      foo: { $ref: '#/$defs/foo' } # effectively 'https://contoso.com/schemas/example#/$defs/foo'
      bar: { $ref: 'https://contoso.com/schemas/fields/bar' }
    $defs:
      foo:
        $id: https://contoso.com/schemas/fields/foo
        type: object
        properties:
          baz: { $ref: '#/$defs/baz' } # effectively 'https://contoso.com/schemas/fields/foo#/$defs/baz
      bar:
        $id: https://contoso.com/schemas/fields/bar
        type: array
        items: { $ref: '/schemas/fields/baz' } # effectively 'https://contoso.com/schemas/fields/baz'
      baz:
        $id: https://contoso.com/schemas/fields/baz
        type: string
        pattern: '^\w+$'

    At this point, validator compilation fails with a message like Error when resolving schema reference '#/$defs/baz'. Path '$defs.foo.properties.baz'

To work around these limitations, the latest commit:

  1. Sets all types that derive JsonSchema but not DscRepoSchema to use the inline schemars attribute (instead of inserting a reference/definition, schemars pushes the generated schema into wherever it's being used).
  2. Defines a new helper macro, dsc_repo_schema_for!() to retrieve the generated schema and canonicalize all references and definitions. This fixes references to any schema that defines the $id keyword.
  3. Updates the schema_for integration tests to use the new macro, validate that the schema is valid for its meta schema, and validate that the schema compiles.

Prior to this change, retrtieving the `title` and `description`
keywords from a schema required using the `get_keyword_as_str`
extension method. Setting `title`, `description`, and
`markdownDescription` required using the `insert` method on
the `Schema` and passing a `serde_json::Value`.

This change improves the ergonomics by defining the following
extension methods:

- `get_title` - retrieve the `title` keyword as a string
- `set_title` - override the `title` keyword, returning the
  previous value if it was defined.
- `get_description` - retrieve the `description` keyword
  as a string
- `set_description` - override the `description` keyword,
  returning the previous value if it was defined.
- `set_markdown_description` - override the `markdownDescription`
  keyword, returning the previous value if it was defined.
Prior to this change, working with the `$schema` field for a
schema required using the `get_keyword_as_str` method and
parsing into a `Url` or calling the `insert` method with a
`serde_json::Value`.

This change adds the following extension methods:

- `get_meta_schema` - Retrieve the `$schema` keyword as a
  string slice if defined.
- `get_meta_schema_as_url` - Retrieve the `$schema` keyword
  as a `Url` if defined and valid.
- `has_meta_schema_keyword` - Indicates if the schema defines
  the `$schema` keyword.
- `set_meta_schema` - Overrides the `$schema` keyword and
  returns the previous value if it was already defined.
Prior to this change, defining the `$schema`, `$id`, `title,
`description`, and `markdownDescription` keywords to the
JSON Schema for a `DscRepoSchema` type required the following
type definition pattern:

```rust
#[derive(Debug, Clone, JsonSchema, DscRepoSchema)]
#[dsc_repo_schema(base_name = "struct", folder_path = "example")]
#[schemars(
    title = schema_i18n!("title"),
    description = schema_i18n("description"),
    extend(
        "$schema" = ExampleStruct::default_export_meta_schema_uri(),
        "$id"     = ExampleStruct::default_export_schema_id_uri(),
        "markdownDescription" = schema_i18n!("markdownDescription"),
    )
)]
pub struct ExampleStruct {
    // Elided for brevity
}
```

With this change, you can insert the `$id` and `$schema` keywords with
the `transform_export_schema_uris` transform method and the localized
docs keywords with either the `transform_schema_docs` or
`transform_schema_docs_strict` methods.

```rust
#[derive(Debug, Clone, JsonSchema, DscRepoSchema)]
#[dsc_repo_schema(base_name = "struct", folder_path = "example")]
#[schemars(
    transform = ExampleStruct::transform_export_schema_uris,
    transform = ExampleStruct::transform_schema_docs,
)]
pub struct ExampleStruct {
  // Elided for brevity
}
```

This change adds the following transform methods to the `DscRepoSchema`
trait with default implementations for each transformer:

- `transform_export_schema_uris` - Insert the default export URIs for
  the `$schema` and `$id` keywords.
- `transform_schema_docs` - Insert the `title`, `description`, and
  `markdownDescription` keywords with localized text. If the translation
  is missing, silently skip overriding that keyword.
- `transform_schema_docs_strict` - As above, but collect missing
  translations and panic to indicate that the schema is missing docs.
Prior to this change, the schemas for `DscRepoSchema` types were
inconsistent about:

- Defining `$schema` and `$id` - some types deriving `JsonSchema`
  or implementing it manually supplied those keywords, most didn't.
  Some used `default_schema_id_uri` or `default_export_schema_id_uri`.
- Defining the documentation keywords. Most types didn't set them
  at all.

This change ensures every `DscRepoSchema` either defines the keywords
directly (for types manually implementing `JsonSchema`) or uses the
newly available `transform_*` associated trait functions (for types
that derive `JsonSchema`).

Most types _don't_ have localized documentation yet, so this PR
uses the non-strict transform. Eventually we should always use
the strict transforms and panic on missing documentation.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

A couple of enums currently rely on multiple #[schemars(...)] attributes where consolidating into one makes transform application/order explicit and avoids ambiguity.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR standardizes JSON Schema export metadata and documentation keywords for DscRepoSchema types by introducing reusable schema utility methods and schema transforms, then applying those transforms across dsc-lib types and schema-related models.

Changes:

  • Added schema utility/extension methods for $schema, title, description, and markdownDescription, plus DscRepoSchema transforms to populate $id/$schema and docs keywords.
  • Updated many dsc-lib types to use the new transforms (and added #[schemars(inline)] in several places to control schema shapes).
  • Added a dsc_repo_schema_for! macro and strengthened integration tests by validating schemas against the meta-schema and compiling them with jsonschema.
File summaries
File Description
lib/dsc-lib/tests/integration/schemas/schema_for.rs Switches schema generation to dsc_repo_schema_for! and adds meta-schema + compilation validation.
lib/dsc-lib/src/types/tag.rs Moves Tag schema keyword population to standardized transforms while preserving pattern keywords.
lib/dsc-lib/src/types/tag_list.rs Replaces manual docs keyword injection with strict docs/URI transforms.
lib/dsc-lib/src/types/semantic_version.rs Adjusts schema naming and explicitly sets $schema/$id in the manual schema.
lib/dsc-lib/src/types/semantic_version_req.rs Adjusts schema naming and explicitly sets $schema/$id in the manual schema.
lib/dsc-lib/src/types/resource_version.rs Replaces manual docs keyword injection with strict docs/URI transforms.
lib/dsc-lib/src/types/resource_version_req.rs Replaces manual docs keyword injection with strict docs/URI transforms.
lib/dsc-lib/src/types/fully_qualified_type_name.rs Uses strict docs/URI transforms and keeps pattern keywords.
lib/dsc-lib/src/types/exit_codes_map.rs Updates manual schema to use default export $schema/$id.
lib/dsc-lib/src/types/date_version.rs Updates manual schema to use default export $schema/$id.
lib/dsc-lib/src/schemas/mod.rs Introduces a schemas module that re-exports schema-related modules and macros.
lib/dsc-lib/src/schemas/macros.rs Adds dsc_repo_schema_for! macro to canonicalize refs/defs after schema_for!.
lib/dsc-lib/src/lib.rs Switches from dependency-crate re-export to an internal schemas module.
lib/dsc-lib/src/functions/mod.rs Applies docs/URI transforms to function schema types and uses string-enum idiomatic transform.
lib/dsc-lib/src/extensions/secret.rs Adds schema transforms and inline tweaks for extension secret schema models.
lib/dsc-lib/src/extensions/import.rs Adds schema transforms and inline tweaks for extension import schema models.
lib/dsc-lib/src/extensions/extension_manifest.rs Adds docs/URI transforms to the extension manifest schema type.
lib/dsc-lib/src/extensions/dscextension.rs Adds docs/URI transforms and keeps idiomatic enum schema generation.
lib/dsc-lib/src/extensions/discover.rs Adds docs/URI transforms and inline tweaks for discover models and args.
lib/dsc-lib/src/dscresources/resource_manifest.rs Applies docs/URI transforms broadly across resource manifest schema models and args.
lib/dsc-lib/src/dscresources/invoke_result.rs Adds docs/URI transforms to resource invocation result schema models.
lib/dsc-lib/src/dscresources/dscresource.rs Adds docs/URI transforms to resource listing/capabilities schema models.
lib/dsc-lib/src/dscresources/adapted_resource_manifest.rs Adds docs/URI transforms and inline tweaks for adapted manifest types.
lib/dsc-lib/src/discovery/command_discovery.rs Adds DscRepoSchema + transforms to discovered manifest list schema and tweaks enum inlining.
lib/dsc-lib/src/configure/parameters.rs Adds inline to configuration parameter/secure wrapper schema types.
lib/dsc-lib/src/configure/config_result.rs Adds docs/URI transforms to configuration output schema models and tweaks enum inlining.
lib/dsc-lib/src/configure/config_doc.rs Adds docs/URI transforms widely to config document schema models and adds some inline markers.
lib/dsc-lib-jsonschema/src/vscode/schema_extensions.rs Adds set_markdown_description for VS Code’s markdownDescription keyword.
lib/dsc-lib-jsonschema/src/schema_utility_extensions.rs Adds $schema, title, and description get/set helpers and related utilities.
lib/dsc-lib-jsonschema/src/dsc_repo/dsc_repo_schema.rs Adds standardized DscRepoSchema transforms for export URIs and localized docs keywords.
Review details
  • Files reviewed: 30/30 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 467 to +468
#[schemars(transform = idiomaticize_string_enum)]
#[schemars(inline)]
Comment on lines 74 to +79
#[schemars(transform = idiomaticize_string_enum)]
#[dsc_repo_schema(base_name = "resourceCapabilities", folder_path = "definitions")]
#[schemars(
transform = Capability::transform_export_schema_uris,
transform = Capability::transform_schema_docs
)]
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.

2 participants