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

Provide JSON Schema for draft.yaml to SchemaStore for IDE autocompletion #708

Open
edjw opened this issue Nov 18, 2024 · 2 comments
Open
Labels
enhancement New feature or request

Comments

@edjw
Copy link

edjw commented Nov 18, 2024

Just discovering Blueprint in the last couple of days and noted a couple of things that might make it easier to pick up.

I'm really used to autocomplete, inline validation etc in my editor and missed these things while making my draft.yaml.

The JSON Schema Store project might offer a way to provide these nice things for developers for Blueprint.

Synopsis:

Create a JSON Schema for Blueprint to provide autocompletion, validation, and tooltips to the draft.yaml

PhpStorm seems to include the schema validation by default.
Red Hat's YAML extension provides it for VS Code.

You then get some nice autocomplete, some tooltip docs, some validation.

Here's a couple of examples of what that looks like in VS Code with a Github Workflow yaml.
CleanShot 2024-11-18 at 14 33 01@2x
CleanShot 2024-11-18 at 14 34 07@2x

Proposed steps:

  1. Create a JSON Schema.
  2. Link to it in the JSON Schema Store.

Docs here on creating a schema:
https://json-schema.org/learn/getting-started-step-by-step

There is a list of all other JSON Schemas here:
https://www.schemastore.org/json/

It seems you can add the file in Blueprint's own repo or add to the SchemaStore repo directly:
https://github.com/SchemaStore/schemastore/blob/master/src/api/json/catalog.json

Expected Behavior:

In supported JSON editors like Visual Studio and Visual Studio Code, developers get auto-completion and validation to make sure their JSON document is correct.

When a JSON editor supports schemas, tooltips can help inform the user about the various properties and values.

It is supported in VS Code, PhpStorm, NeoVim and lots of other editors.

@edjw edjw added enhancement New feature or request pending This issue is pending review labels Nov 18, 2024
@jasonmccreary
Copy link
Collaborator

This would be great! I'm not that familiar with writing one of these. So I'd have to learn. If you know and could provide even a basic example for one of the model statements, that would be awesome.

@jasonmccreary jasonmccreary removed the pending This issue is pending review label Nov 18, 2024
@edjw
Copy link
Author

edjw commented Nov 19, 2024

Hi, here's an example of what that might look like. Hopefully this is a helpful step forward for you :-)

I should say upfront that Claude helped a lot with this – I don't know how you'll feel about that. I gave it the Laravel Blueprint docs and several other schema.json files to help it work out what to do.

The schema seems to work with a few draft.yaml files I can find around blogposts and Github. But it could definitely use some more testing. Do you have lots of valid draft.yaml files that could be tested against it? I expect that you might see some things to change as someone who obviously knows a lot about Blueprint!

The regex patterns are a bit much in places. There might be a cleaner way to do this

The error messages could be more helpful instead of saying it doesn't match a long regex.

Ideally, this would error but currently it doesn't name: stringasdf

Obviously there'd be a maintenance burden here to update the schema when the project changed.

You can test it in vscode by putting this in .vscode/settings.json where schema.json and draft.yaml are in the root of your project

{
    "yaml.schemas": {
        "./schema.json": [
            "draft.yaml"
        ],
    }
}
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "title": "Laravel Blueprint Schema",
    "description": "JSON Schema for Laravel Blueprint YAML definitions",
    "definitions": {
        "columnType": {
            "type": "string",
            "enum": [
                "bigIncrements",
                "bigInteger",
                "binary",
                "boolean",
                "char",
                "dateTimeTz",
                "dateTime",
                "date",
                "decimal",
                "double",
                "enum",
                "float",
                "foreignId",
                "foreignIdFor",
                "foreignUlid",
                "foreignUuid",
                "geography",
                "geometry",
                "id",
                "increments",
                "integer",
                "ipAddress",
                "json",
                "jsonb",
                "longText",
                "macAddress",
                "mediumInteger",
                "mediumText",
                "morphs",
                "nullableMorphs",
                "nullableTimestamps",
                "nullableUlidMorphs",
                "nullableUuidMorphs",
                "rememberToken",
                "set",
                "smallInteger",
                "softDeletesTz",
                "softDeletes",
                "string",
                "text",
                "timeTz",
                "time",
                "timestampTz",
                "timestamp",
                "timestampsTz",
                "timestamps",
                "tinyInteger",
                "tinyText",
                "unsignedBigInteger",
                "unsignedInteger",
                "unsignedSmallInteger",
                "unsignedTinyInteger",
                "ulidMorphs",
                "uuidMorphs",
                "ulid",
                "uuid",
                "year"
            ]
        },
        "columnModifier": {
            "type": "string",
            "enum": [
                "autoIncrement",
                "charset",
                "collation",
                "default",
                "foreign",
                "index",
                "nullable",
                "primary",
                "unique",
                "unsigned",
                "useCurrent",
                "useCurrentOnUpdate"
            ]
        },
        "relationship": {
            "type": "string",
            "pattern": "^[A-Z][a-zA-Z0-9]*(:[a-zA-Z0-9_]+)?(,\\s*[A-Z][a-zA-Z0-9]*(:[a-zA-Z0-9_]+)?)*$",
            "examples": [
                "Project",
                "User",
                "Project, User",
                "Project:client, User:owner"
            ]
        },
        "controllerStatement": {
            "type": "object",
            "patternProperties": {
                "^(delete|dispatch|find|fire|flash|notify|query|redirect|render|resource|respond|save|send|store|update|validate)$": {
                    "type": "string"
                }
            },
            "additionalProperties": false
        }
    },
    "properties": {
        "models": {
            "type": "object",
            "description": "Define Eloquent models with their columns, relationships, and properties",
            "patternProperties": {
                "^[A-Z][a-zA-Z0-9]*(\\\\[A-Z][a-zA-Z0-9]*)*$": {
                    "type": "object",
                    "description": "Model definition",
                    "properties": {
                        "id": {
                            "oneOf": [
                                {
                                    "type": "boolean"
                                },
                                {
                                    "type": "string",
                                    "enum": [
                                        "id",
                                        "uuid"
                                    ]
                                }
                            ]
                        },
                        "timestamps": {
                            "type": "boolean"
                        },
                        "timestampsTz": {
                            "type": "boolean"
                        },
                        "softDeletes": {
                            "type": "boolean"
                        },
                        "softDeletesTz": {
                            "type": "boolean"
                        },
                        "relationships": {
                            "type": "object",
                            "properties": {
                                "hasMany": {
                                    "$ref": "#/definitions/relationship"
                                },
                                "belongsTo": {
                                    "$ref": "#/definitions/relationship"
                                },
                                "hasOne": {
                                    "$ref": "#/definitions/relationship"
                                },
                                "belongsToMany": {
                                    "$ref": "#/definitions/relationship"
                                }
                            },
                            "additionalProperties": false
                        }
                    },
                    "patternProperties": {
                        "^[a-z_][a-zA-Z0-9_]*$": {
                            "type": "string",
                            "description": "Column definition with valid type, optional parameters and modifiers"
                        }
                    },
                    "additionalProperties": false
                }
            }
        },
        "controllers": {
            "type": "object",
            "description": "Define controllers with their actions and statements",
            "patternProperties": {
                "^[A-Z][a-zA-Z0-9]*(Controller)?(\\\\[A-Z][a-zA-Z0-9]*)*$": {
                    "type": "object",
                    "properties": {
                        "resource": {
                            "oneOf": [
                                {
                                    "type": "boolean"
                                },
                                {
                                    "type": "string",
                                    "pattern": "^(web|api|api\\.[a-z]+|[a-z]+(,\\s*[a-z]+)*)?$"
                                }
                            ]
                        },
                        "invokable": {
                            "oneOf": [
                                {
                                    "type": "boolean"
                                },
                                {
                                    "$ref": "#/definitions/controllerStatement"
                                }
                            ]
                        }
                    },
                    "patternProperties": {
                        "^[a-z_][a-zA-Z0-9_]*$": {
                            "$ref": "#/definitions/controllerStatement"
                        }
                    },
                    "additionalProperties": false
                }
            }
        },
        "seeders": {
            "type": "string",
            "description": "Comma-separated list of models to generate seeders for",
            "pattern": "^[A-Z][a-zA-Z0-9]*(,\\s*[A-Z][a-zA-Z0-9]*)*$",
            "examples": [
                "Post",
                "Post, Comment, User"
            ]
        }
    },
    "required": [
        "models"
    ],
    "additionalProperties": false
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants