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

Where are you using it? #89

Open
elbakerino opened this issue Mar 7, 2021 · 4 comments
Open

Where are you using it? #89

elbakerino opened this issue Mar 7, 2021 · 4 comments

Comments

@elbakerino
Copy link
Member

Help me to better shape the future of UI-Schema!

Show what you've built - or explain what you would like to built with it.

Just respond here with:

  • a screenshot, video or gif
  • a link to your tool, codesandbox or similiar
  • a higher level concept what you want to achieve

From my side UI-Schema is currently developed for use as the central part of a schema driven headless information management and content management system, some features like UIStorePro are made open-source after the initial development there. Can't show some screenshots atm.

@elbakerino elbakerino pinned this issue Mar 7, 2021
@elbakerino
Copy link
Member Author

elbakerino commented Sep 14, 2022

Collaborative Schema-Driven Data Editor

Automatic UI generation and backend routing, the client and backend decide automatically what and how everything must be processed and shown.

An visual example and technical overview with small code examples:

ui-schema-data-editor.mp4

Dev Focus

write a schema, define an entity - all else will work automatically.

  • Open-Api spec interoperable
  • any DB
  • any client-side implementation
  • support for long running / realtime / standard (CRUD) querying and executions
  • good DX to customize any part which a DDD (domain-driven-design) approach, purely event driven

Technical Description

Backend and model<>schema definition basics:

  • TS/NodeJS backend which implements a universal DB model-definition
  • backend definition is build for easy conversion to Open-Api-Spec
  • backend routes are build automatically using that generated Open-Api-Spec
  • backend routes are handled by one controller, which handles all necessary validation and dispatches the final backend execution
  • the backend can attach event handlers for pre- and post-processing in a data-driven manner
  • the backend definition is grouped around entities which implement operations
    • implemented operations: create, update, delete, details, collect
  • each entity-operation can be mapped to one specific API endpoint
  • one generic API endpoint describes the available operations for the current client (RBAC) for one specific entity

Client side:

  • the example app "knows" when to request which entity with an special "app-bootstrap" API call (not shown) and then further on by the entity keyword
  • the backend operation-definition is exposed in a public-usable format, e.g. not including "backend-internals" like grants/permission rules
  • each public-operation contains
    • href/method for the client to know how to execute it
    • use: string[] for the client to know which connector options are available, e.g. api and others like websocket etc.
    • in the schema for the request body, if not set is e.g. GET
    • out the schema for the expected response body
  • client implements per connector, running and resolving the operations, e.g. each entity could implement the api in a different way
  • the client can e.g. use realtime connections when available - or otherwise the default api to query data, but e.g. may must use api to create/delete items, even when realtime querying is available for some operations
  • UI Specials
    • the UI includes further UI-Schema extensions which implement a few custom keywords
    • the editing is realtime collaborative, using UI-Schema and a custom onChange binding for Google Firestore (not affiliated / not sponsored)
    • the UI-Schema component implements a read > draft > publish experience using the default UI-Schema features and one store, the store.values are replaced depending if read-only or edit-on with either the published data or the latest draft-data
    • custom widgets for Autocomplete enum
    • custom widgets for entities, e.g. EntitySelect
      • which selects the $key of the entity, using the collect operation for select options
      • uses an existing value to resolve the entity data, using the details operation
      • supports read-only mode with details-previs / card design and custom styleSchema

Examples

  1. Example JSON-Schema
  2. Example Backend Spec
  3. Example Public Spec
  4. Example Open-Api Docs
  5. Side Notes

Example JSON-Schema

This is the schema for an entity named project.

It contains a special EntitySelect at the customer property, this widget implements a "select or write" mode, but not editing the respective entity, but it shows an button to the actual entity UI.

The read schema is injected into the actual customer-entity schema when read mode is active, handled using the experimental InjectSplitSchemaPlugin.

{
    "type": "object",
    "properties": {
        "id": {
            "type": "string"
        },
        "name": {
            "type": "string"
        },
        "customer": {
            "type": "string",
            "widget": "EntitySelect",
            "entity": "customer",
            "read": {
                "/details/name": {
                    "view": {
                        "sizeMd": 5
                    }
                },
                "/details/company": {
                    "view": {
                        "sizeMd": 3
                    }
                },
                "/details/email": {
                    "view": {
                        "sizeMd": 4
                    }
                },
                "/details/phone": {
                    "hidden": true
                },
                "/details/billingAddress": {
                    "hidden": true
                },
                "/details/postalAddress": {
                    "hidden": true
                }
            }
        }
    },
    "required": [
        "id"
    ]
}

Example Backend Spec

Example backend spec for an entity named project:

export type Spec = {} // private code
export const schemaCollection = () => {} // private code
export const schemaDetails = () => {} // private code
export const schemaResult = () => {} // private code

export const specProject: Spec<'project'> = {
    name: 'project',
    grants: [
        {
            perm: 'read',
            public: true,
        },
        {
            perm: 'write',
            roles: ['member'],
        },
        /*{
            perm: 'delete',
            roles: ['admin'],
        },*/
    ],
    ops: [
        {
            type: 'details',
            // special `$entity` placeholder replaced with the entity `name`
            out: schemaDetails('entity/$entity'),
            perms: ['read', 'details'],
            // special `$key` placeholder, replaced with the `key` prop when existing
            params: ['$key'],
            use: ['firestore', 'api'],
            // `method` defaults to `get` when `in` is missing
        },
        {
            type: 'collect',
            out: schemaCollection('entity/$entity'),
            use: ['firestore', 'api'],
            perms: ['read', 'collect'],
        },
        {
            type: 'create',
            in: 'entity/$entity',
            out: 'entity/$entity',
            perms: ['write', 'create'],
            method: 'put',
            use: ['api'],
        },
        {
            type: 'update',
            in: 'entity/$entity_update',
            out: schemaResult({type: 'boolean'}),
            perms: ['write', 'update'],
            params: ['$key'],
            // `method` defaults to `post` when `in` is existing
        },
        {
            type: 'delete',
            out: schemaResult({type: 'boolean'}),
            perms: ['write', 'delete'],
            params: ['$key'],
            method: 'delete',
        },
    ],
    // used for the `List` labels / can be overwritten from within schema
    listLabelKeys: ['name'],
    // the unique `id` of an entity / compound-keys are possible but not shown here
    key: 'id',
    // e.g. `auto` for auto-increment/auto-created ids
    keyGen: 'input',
    // which DB-binding the backend implementation must use, e.g. can be an custom repository per entity for e.g. PostgreSQL
    binding: 'firestore',
}

export interface IProject {
    id: string
    name?: string
    customer?: string
}

Example Public Spec

This public spec is generated automatically by one endpoint, exposing only the public execution info and only the operations the user is allowed to execute.

With this schema the UI can automatically run any further operations necessary to provide the full editor experience.

{
    "ops": {
        "details": {
            "params": [
                "id"
            ],
            "out": {
                "type": "object",
                "properties": {
                    "details": {
                        "$ref": "http://localhost:3030/schemas/entity/project",
                        "type": "object"
                    }
                }
            },
            "href": "http://localhost:3030/e/project/details/{id}",
            "templated": true,
            "method": "get",
            "use": [
                "firestore",
                "api"
            ]
        },
        "collect": {
            "params": [],
            "out": {
                "type": "object",
                "properties": {
                    "collection": {
                        "type": "array",
                        "items": {
                            "$ref": "http://localhost:3030/schemas/entity/project",
                            "type": "object"
                        }
                    }
                }
            },
            "href": "http://localhost:3030/e/project/collect",
            "method": "get",
            "use": [
                "firestore",
                "api"
            ]
        },
        "create": {
            "params": [],
            "in": {
                "$ref": "http://localhost:3030/schemas/entity/project",
                "type": "object"
            },
            "out": {
                "$ref": "http://localhost:3030/schemas/entity/project",
                "type": "object"
            },
            "href": "http://localhost:3030/e/project/create",
            "method": "put",
            "use": [
                "api"
            ]
        },
        "update": {
            "params": [
                "id"
            ],
            "in": {
                "$ref": "http://localhost:3030/schemas/entity/project_update",
                "type": "object"
            },
            "out": {
                "type": "object",
                "properties": {
                    "result": {
                        "type": "boolean"
                    }
                }
            },
            "href": "http://localhost:3030/e/project/update/{id}",
            "templated": true,
            "method": "post"
        },
        "delete": {
            "params": [
                "id"
            ],
            "out": {
                "type": "object",
                "properties": {
                    "result": {
                        "type": "boolean"
                    }
                }
            },
            "href": "http://localhost:3030/e/project/delete/{id}",
            "templated": true,
            "method": "delete"
        }
    },
    "model": {
        "name": "project",
        "key": "id",
        "keyGen": "input",
        "listLabelKeys": [
            "name"
        ]
    }
}

Example Open-Api Docs

Generated Open-Api docs, using the backend schema, this shows the create operation of the project entity:

uis-example-oas-docs

Side Notes

  • operations and maybe even their keywords are an experiment to see is if that is a good fit for UIApi / Loading Data / Data Ops #198
  • there are no plans to release the full code in the near future, feel free to contact me about it (e.g. UI-Schema slack channel), these internal packages are providing all features in the video
    • NodeJS backend spec & model definition
    • NodeJS backend Firestore binding
    • NodeJS backend open-api generator / express factory
    • React Google Firebase/Firestore connection provider
    • React Firebase User Presence (e.g. on storeKeys level for some widgets using onFocus/onBlur)
    • React Firestore data-connector
    • React Firestore & UI-Schema based Read-Draft-Publish - does not rely on the entity spec.
    • React entity-spec provider, fetch based connector for any operation and firestore for details and collect operations
    • React entity-spec Widgets for UI-Schema
    • React Firestore UI-Schema StoreActions integration
    • React Progress State & MUI Progress / Click-n-Confirm Buttons (this will be public soon)

@SaadBazaz

This comment was marked as off-topic.

@hennessyevan

This comment was marked as off-topic.

@elbakerino

This comment was marked as off-topic.

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

No branches or pull requests

3 participants