{
    "type": "object",
    "properties": {
        "name": {
            "type": "string", "pattern": "^[\\w]+\\.[\\w]+\\.[\\w]+\\.[\\w]+$",
            "description": "Component name in the format 'vendor.connectorName.core.componentName'"
        },
        "label": {
            "type": "string",
            "description": "The label of your component. If not label is specified, then last part of name will be used when component is dropped into Designer. If your component name is appmixer.twitter.statuses.CreateTweet then CreateTweet will be name of the component unless you specify label property. This allows you to use spaces as opposed to the name property. "
        },
        "description": {
            "type": "string",
            "description": "Description of your component. The description is displayed in the Designer UI inspector panel. "
        },
        "author": { "type": "string", "description": "Appmixer <info@appmixer.com>" },
        "trigger": { "type": "boolean", "description": "Whether the component is a trigger component." },
        "inPorts": { "$ref": "#/definitions/inPorts" },
        "outPorts": { "$ref": "#/definitions/ports" },
        "auth": { "$ref": "#/definitions/auth" },
        "tick": {
            "type": "boolean",
            "description": "When set to true, the component will receive signals in regular intervals from the engine. The tick() Component Virtual method will be called in those intervals (see Component Behaviour). This is especially useful for trigger-type of components that need to poll a certain API for changes. The polling interval can be set by the COMPONENT_POLLING_INTERVAL environment variable (for custom on-prem installations only). The default is 60000 (ms), i.e. 1 minute."
        },
        "webhook": {
            "type": "boolean",
            "description": "Set webhook property to true if you want your component to be a \"webhook\" type. That means that context.getWebhookUrl() method becomes available to you inside your component virtual methods (such as receive()). You can use this URL to send HTTP requests to. See the Behaviour section, especially the context.getWebhookUrl() for details and example."
        },
        "icon": { "type": "string", "description": "Link to svg icon. The icon representing the component in the UI." },
        "quota": {
            "type": "object",
            "description": "Configuration of the quota manager used for this component. Quotas allow you to throttle the firing of your component. This is especially useful and many times even necessary to make sure you don't go over limits of the usage of the API that you call in your components. Quota managers are defined in the quota.js file of your service/module.",
            "properties": {
                "manager": {
                    "type": "string", "description": "The name of the quota module where usage limit rules are defined."
                },
                "maxWait": { "type": "integer" },
                "concurrency": { "type": "integer" },
                "resources": {
                    "description": "One or more resources that identify rules from the quota module that apply to this component. Each rule in the quota module can have the resource property. quota.resources allow you to cherry-pick rules from the list of rules in the quota module that apply to this component. quota.resources can either be a string or an array of strings.",
                    "oneOf": [
                        { "type": "array", "items": { "type": "string" } },
                        { "type": "string" }
                    ]
                },
                "scope": {
                    "type": "object",
                    "description": "This scope instructs the quota manager to count calls either for the whole application (service) or per-user. Currently, it can either be omitted in which case the quota limits for this component apply for the whole application or it can be { \"userId\": \"{{userId}}\" } in which case the quota limits are counted per Appmixer user."
                }
            }
        },
        "properties": {
            "type": "object",
            "description": "The configuration properties of the component. Note that unlike properties specified on input ports, these properties cannot be configured by the user to use data coming from the components back in the chain of connected components. In other words, these properties can only use data that is known before the flow runs. This makes them suitable mainly for trigger type of components.",
            "properties": {
                "schema": { "$ref": "#/definitions/jsonSchema" },
                "inspector": { "$ref": "#/definitions/inspector" }
            }
        },
        "version": { "type": "string", "description": "The version of the component, e.g. '1.0.0'" }
    },
    "additionalProperties": false,
    "required": ["name"],
    "definitions": {
        "jsonSchema": {
            "type": "object",
            "description": "schema is a JSON Schema definition (http://json-schema.org) of the properties, their types and whether they are required or not."
        },
        "auth": {
            "type": "object",
            "description": "The authentication service and parameters. For example:\n\nCopy\n{\n    \"auth\": {\n        \"service\": \"appmixer:google\",\n        \"scope\": [\n            \"https://mail.google.com/\",\n            \"https://www.googleapis.com/auth/gmail.compose\",\n            \"https://www.googleapis.com/auth/gmail.send\"\n        ]\n    }\n}\nThe auth.service identifies the authentication module that will be used to authenticate the user to the service that the component uses. It must have the following format: [vendor]:[service]. The Appmixer engine looks up the auth.js file under that vendor and service category. auth.scope provides additional parameters to the authentication module. See the Authentication section for more details.\n\nWhen auth is defined, the component will have a section in the Designer UI inspector requiring the user to select from existing accounts or connect a new account. Only after an account is selected the user can continue configuring other properties of the component.",
            "properties": {
                "service": {
                    "type": "string"
                },
                "scope": {
                    "type": "array"
                }
            },
            "required": [
                "service"
            ]
        },
        "source": {
            "type": "object",
            "properties": {
                "url": {
                    "type": "string",
                    "description": "The URL of the component to call. The URL is relative to the Appmixer API base URL, e.g. '/component/appmixer/google/spreadsheets/ListWorksheets?outPort=out'."
                },
                "data": {
                    "type": "object",
                    "properties": {
                        "messages": {
                            "description": "Messages that will be sent to the input port of the component referenced by the properties.source.url. Keys in the object represent input port names and values are any objects that will be passed to the input port as messages."
                        },
                        "properties": {
                            "type": "object",
                            "description": "Properties that will be used in the target component referenced by the properties.source.url. The target component must have these properties defined in its manifest file. The values in the object are references to the properties of the component that calls the target component in the static mode. For example:\n\nCopy\n{\n    \"properties\": {\n        \"targetComponentProperty\": \"properties/myProperty\"\n    }\n}"
                        }
                    }
                },
                "transform": {
                    "type": "string",
                    "description": "The transformation function used to transform the output of the target component. It should return an inspector-like object, i.e.:\n\nCopy\n{\n    inputs: { ... },\n    groups: { ... }\n}\nExample:\n\nCopy\n{\n    \"transform\": \"./transformers#columnsToInspector\"\n}\nThe transform function is pointed to be a special format [module_path]#[function], where the transformation module path is relative to the target component directory."
                }
            },
            "required": ["url"]
        },
        "port": {
            "type": "object",
            "properties": {
                "name": { "type": "string" },
                "maxConnections": { "type": "integer" },
                "schema": { "$ref": "#/definitions/jsonSchema" },
                "source": {
                    "$ref": "#/definitions/source",
                    "description": "The definition is similar to the `source` of properties. When used for the output port definition, it allows defining the output port schema dynamically.\n\nThere is one difference though. When defined in the output port, the source definition can reference both component properties and input fields, while the properties source definition can only hold references to other properties' values. \n\nAn example is a Google Spreadsheet component UpdatedRow. The output port options of this component consist of the column names in the spreadsheet. But that is specific to the selected Spreadsheet/Worksheet combination. Therefore it has to be defined dynamically. "
                },
                "options": {
                    "type": "array",
                    "description": "We support full schema definition for each option, so you can specify the structure of the data that is coming out from your component. You can add a schema property to each option, which contains a JSON Schema definition. For example:\n\nCopy\n{\n    \"outPorts\": [\n        {\n            \"name\": \"weather\",\n            \"options\": [\n                { \"label\": \"Temperature\", \"value\": \"main.temp\" },\n                { \"label\": \"Pressure\", \"value\": \"main.pressure\" },\n                { \"label\": \"Humidity\", \"value\": \"main.humidity\" },\n                { \"label\": \"Sunrise time (unix, UTC)\", \"value\": \"sys.sunrise\" },\n                { \"label\": \"Sunset time (unix, UTC)\", \"value\": \"sys.sunset\" },\n                { \"label\": \"City name\", \"value\": \"name\" },\n                { \n                    \"label\": \"Weather data\", \n                    \"value\": \"weather\", \n                    \"schema\": {\n                        \"type\": \"array\",\n                        \"items\": {\n                            \"type\": \"object\",\n                            \"properties\": {\n                                \"description\": { \"type\": \"string\", \"title\": \"Weather description\" },\n                                \"icon\": { \"type\": \"string\", \"title\": \"Weather icon code\" },\n                                \"iconUrl\": { \"type\": \"string\", \"title\": \"Weather icon URL\" }\n                            }    \n                        }\n                    }\n                }\n            ]\n        }\n    ]\n}",
                    "items": {
                        "type": "object",
                        "properties": {
                            "content": { "type": "string" },
                            "label": { "type": "string" },
                            "value": { "type": "string" }
                        },
                        "required": ["value"]
                    }
                }
            },
            "required": ["name"]
        },
        "state": {
            "type": "object",
            "properties": {
                "persistent": {
                    "type": "boolean"
                }
            }
        },
        "options": {
            "type": "array",
            "minItems": 0,
            "items": {
                "oneOf": [
                    { "type": "object" },
                    { "type": "string" }
                ]
            },
            "uniqueItems": true
        },
        "inspectorInput": {
            "oneOf": [
                {
                    "type": "object",
                    "properties": {
                        "type": { "type": "string" },
                        "tooltip": { "type": "string" },
                        "index": { "type": "number" },
                        "placeholder": { "type": "string" },
                        "options": { "$ref": "#/definitions/options" },
                        "source": { "$ref": "#/definitions/source" }
                    },
                    "additionalProperties": true,
                    "required": ["type", "index"]
                },
                {
                    "type": "object",
                    "properties": {
                        "source": { "$ref": "#/definitions/source" }
                    },
                    "additionalProperties": false,
                    "required": ["source"]
                }
            ]
        },
        "inspector": {
            "description": "Inspector tells the Designer UI how the input fields should be rendered. The format of this definition uses the Rappid Inspector definition format.",
            "oneOf": [
                {
                    "type": "object",
                    "properties": {
                        "inputs": {
                            "patternProperties": {
                                "^.*$": { "$ref": "#/definitions/inspectorInput" }
                            }
                        }
                    },
                    "required": ["inputs"]
                },
                {
                    "type": "object",
                    "description": "Sometimes the structure of the inspector is not known in advance and it cannot be hardcoded in the manifest. Instead, the inspector fields are composed dynamically based on the data received from an API. A good example is the google.spreadsheets.CreateRow component where the inspector renders fields representing columns fetched from the actual worksheet. For this to work, we can define the source property in the manifest that calls a component of our choosing in a so called \"static\" mode. Example: {\n       \"source\": {\n           \"url\": \"/component/appmixer/google/spreadsheets/ListColumns?outPort=out\",\n           \"data\": {\n               \"messages\": {\n                   \"in\": 1\n               },\n               \"properties\": {\n                   \"sheetId\": \"properties/sheetId\",\n                   \"worksheet\": \"properties/worksheet\"\n               },\n               \"transform\": \"./transformers#columnsToInspector\"\n           }\n       }\n}\n",
                    "properties": {
                        "source": { "$ref": "#/definitions/source" }
                    },
                    "required": ["source"]
                }
            ]
        },
        "inPort": {
            "allOf": [
                { "$ref": "#/definitions/port" },
                {
                    "type": "object",
                    "properties": {
                        "inspector": { "$ref": "#/definitions/inspector" }
                    }
                }
            ]
        },
        "inPorts": {
            "description": "The definition of the input ports of the component. It's an array of objects.\n\nEach component can have zero or more input ports. If a component does not have any input ports, we call it a trigger. Input ports allow a component to be connected to other components. Input ports receive data from output ports of other connected components when the flow is running and the data is available. Each input port has a name and configuration that has the exact same structure as the configuration of properties, i.e. it has schema , inspector or source objects. The difference is that the user can use placeholders (variables) in the data fields that will be eventually replaced once the actual data is available. The placeholders (variables) can be entered by the user using the \"variables picker\" in the Designer UI inspector (see below)",
            "type": "array",
            "minItems": 0,
            "items": {
                "oneOf": [
                    { "$ref": "#/definitions/inPort" },
                    { "type": "string" }
                ]
            },
            "uniqueItems": true
        },
        "ports": {
            "description": "The definition of the output ports of the component. It's an array of objects.\n\nComponents can have zero or more output ports. Each output port has a name and optionally an array options that defines the structure of the message that this output port emits. Without the options object, the user won't be able to see the possible variables they can use in the other connected components. For example, a component connected to the weather output port of our GetCurrentWeather component can see the following variables in the variables picker",
            "type": "array",
            "minItems": 0,
            "items": {
                "oneOf": [
                    { "$ref": "#/definitions/port" },
                    { "type": "string" }
                ]
            },
            "uniqueItems": true
        }
    }
}
