Skip to content

[Bug]: Swagger does not create all the elements in a component #5226

Description

@jsjames

Swagger is not creating one of the elements in a data structure

In this component, Swagger does not generate the "slots" element. I've tried numerous @Schema annotations to try and force it to create 'slots', but none have been successful. I presume it has something to do with the recursive nature of the element?

package org.openhab.core.ui.components;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue;

/**
 * A UIComponent represents a piece of UI element for a client frontend to render; it is kept very simple and delegates
 * the actual rendering and behavior to the frontend.
 *
 * It has"s reference to a component's name as defined by the frontend, a map of configuration parameters, and several
 * named "slots", or placeholders, which may contain other sub-components, thus defining a tree.
 *
 * No checks are performed on the actual validity of configuration parameters and their values, the validity of a
 * particular slot for a certain component or the validity of certain types of sub-components within a particular slot:
 * that is the frontend's responsibility.
 *
 * @author Yannick Schaus - Initial contribution
 */
@NonNullByDefault
public class UIComponent {
    String component;

    @Schema(additionalProperties = AdditionalPropertiesValue.TRUE)
    @Nullable
    Map<String, Object> config;

    @ArraySchema(schema = @Schema(implementation = UIComponent.class, description = "The nested sub-components inside this slot"))
    @Nullable
    Map<String, List<UIComponent>> slots;

    /**
     * Empty constructor for deserialization.
     */
    public UIComponent() {
        this.component = "";
        this.config = new HashMap<>();
    }

    /**
     * Constructs a component by its type name - component names are not arbitrary, they are defined by the target
     * frontend.
     *
     * @param componentType type of the component as known to the frontend
     */
    public UIComponent(String componentType) {
        this.component = componentType;
        this.config = new HashMap<>();
    }

    /**
     * Retrieves the type of the component.
     *
     * @return the component type
     */
    @Schema(hidden = true)
    public String getType() {
        return component;
    }

    /**
     * Retrieves the type of the component.
     *
     * @return the component type
     */
    public String getComponent() {
        return component;
    }

    /**
     * Sets the type of the component.
     *
     * @param component the component type
     */
    public void setComponent(String component) {
        this.component = component;
    }

    /**
     * Gets all the configuration parameters of the component
     *
     * @return the map of configuration parameters
     */
    public Map<String, Object> getConfig() {
        return config;
    }

    /**
     * Sets all the configuration parameters of the component
     *
     * @param config the map of configuration parameters
     */
    public void setConfig(Map<String, Object> config) {
        this.config = config;
    }

    /**
     * Adds a new configuration parameter to the component
     *
     * @param key the parameter key
     * @param value the parameter value
     */
    public void addConfig(String key, Object value) {
        this.config.put(key, value);
    }

    /**
     * Returns all the slots of the component including their sub-components
     *
     * @return the slots and their sub-components
     *         Sets all the slots of the component
     *
     * @param slots the slots and their sub-components
     */
    public void setSlots(Map<String, List<UIComponent>> slots) {
        this.slots = slots;
    }

    /**
     * Adds a new empty slot to the component
     *
     * @param slotName the name of the slot
     * @return the empty list of components in the newly created slot
     */
    @Nullable
    public List<UIComponent> addSlot(String slotName) {
        if (slots == null) {
            slots = new HashMap<>();
        }
        List<UIComponent> newSlot = new ArrayList<>();

        if (slots == null) {
            return null;
        }

        this.slots.put(slotName, newSlot);

        return newSlot;
    }

    /**
     * Gets the list of sub-components in a slot
     *
     * @param slotName the name of the slot
     * @return the list of sub-components in the slot
     */
    @Nullable
    public List<UIComponent> getSlot(String slotName) {
        if (slots == null) {
            return null;
        }
        return this.slots.get(slotName);
    }

    /**
     * Add a new sub-component to the specified slot. Creates the slot if necessary.
     *
     * @param slotName the slot to add the component to
     * @param subComponent the sub-component to add
     */
    public void addComponent(String slotName, UIComponent subComponent) {
        List<UIComponent> slot;
        if (slots == null || !slots.containsKey(slotName)) {
            slot = addSlot(slotName);
        } else {
            slot = getSlot(slotName);
        }

        if (slot == null) {
            return;
        }

        slot.add(subComponent);
    }
}

The element generated in the openapi spec is the following:

            "UIComponent": {
                "type": "object",
                "properties": {
                    "component": {
                        "type": "string"
                    },
                    "config": {
                        "type": "object",
                        "additionalProperties": true
                    }
                },
                "description": "The nested sub-components inside this slot"
            },

Affected Version

2.2.49

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions