{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "connect/manifest.schema.json",
  "title": "Manifesto de conector do Zoppy Connect",
  "description": "O que um conector pode fazer, conferido antes de uma versão ser guardada: o gatilho do cron, os hosts e as rotas que ele chama, os agentes locais que ele alcança, os nomes dos segredos e se ele escreve em outro sistema. Nome repetido numa lista é recusado; partners.zoppy.com.br precisa de rotas e nunca aceita DELETE.",
  "type": "object",
  "properties": {
    "version": {
      "description": "Formato do manifesto: sempre 1",
      "type": "number",
      "minimum": 1,
      "maximum": 1
    },
    "triggers": {
      "description": "O que inicia uma execução: um cron, na V1",
      "type": "array",
      "minItems": 1,
      "maxItems": 1,
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "description": "Roda num agendamento, a cada everyMinutes minutos",
            "type": "string",
            "enum": [
              "cron"
            ]
          },
          "everyMinutes": {
            "description": "Intervalo entre execuções, em minutos",
            "type": "integer",
            "minimum": 5,
            "maximum": 1440
          }
        },
        "required": [
          "type",
          "everyMinutes"
        ]
      }
    },
    "hosts": {
      "description": "Domínios que o código chama com ctx.http e ctx.soap, por https; um novo espera uma pessoa aprovar",
      "type": "array",
      "maxItems": 20,
      "items": {
        "description": "Domínio que o conector pode chamar por https, sem esquema, porta nem caminho",
        "type": "string"
      }
    },
    "routes": {
      "description": "Método e caminho liberados por host; um host com rotas só aceita as dele, e partners.zoppy.com.br sempre precisa de rotas",
      "type": "array",
      "maxItems": 100,
      "items": {
        "type": "object",
        "properties": {
          "host": {
            "description": "Um dos hosts do manifesto",
            "type": "string"
          },
          "methods": {
            "description": "Métodos HTTP liberados nesse caminho; DELETE nunca é aceito em partners.zoppy.com.br",
            "type": "array",
            "minItems": 1,
            "maxItems": 6,
            "items": {
              "type": "string",
              "enum": [
                "GET",
                "HEAD",
                "POST",
                "PUT",
                "PATCH",
                "DELETE"
              ]
            }
          },
          "path": {
            "description": "Padrão de caminho sem query string: segmentos literais, * vale um segmento e ** no fim vale o resto",
            "type": "string"
          }
        },
        "required": [
          "host",
          "methods",
          "path"
        ]
      },
      "default": []
    },
    "readRoutes": {
      "description": "Rotas POST, PUT ou PATCH que só consultam (uma API que lê por POST): saem como leitura, no ensaio e de verdade, e uma pessoa as aprova junto com o host",
      "type": "array",
      "maxItems": 50,
      "items": {
        "type": "object",
        "properties": {
          "host": {
            "description": "Um dos hosts do manifesto, nunca partners.zoppy.com.br",
            "type": "string"
          },
          "methods": {
            "description": "Métodos que só consultam nesse caminho, mesmo não sendo GET",
            "type": "array",
            "minItems": 1,
            "maxItems": 3,
            "items": {
              "type": "string",
              "enum": [
                "POST",
                "PUT",
                "PATCH"
              ]
            }
          },
          "path": {
            "description": "Padrão de caminho sem query string: segmentos literais, * vale um segmento e ** no fim vale o resto",
            "type": "string"
          }
        },
        "required": [
          "host",
          "methods",
          "path"
        ]
      },
      "default": []
    },
    "agents": {
      "description": "Agentes locais dentro da rede da loja e o que o conector alcança por cada um: APIs internas e bancos de dados",
      "type": "array",
      "maxItems": 3,
      "items": {
        "type": "object",
        "properties": {
          "agentId": {
            "description": "Id do agente local; o gateway manda essas chamadas pelo túnel dele",
            "type": "string",
            "format": "uuid"
          },
          "http": {
            "description": "APIs dentro da rede da loja, chamadas com ctx.http",
            "type": "array",
            "maxItems": 20,
            "items": {
              "description": "Uma API dentro da rede da loja, chamada com ctx.http (só GET e HEAD)",
              "type": "object",
              "properties": {
                "host": {
                  "description": "Host na rede da loja, como a máquina do agente o resolve: um nome, um domínio ou um IPv4",
                  "type": "string"
                },
                "port": {
                  "description": "Porta da API (8080, 443...)",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 65535
                },
                "tls": {
                  "description": "true quando a API é https, false quando é http simples dentro da rede",
                  "type": "boolean"
                }
              },
              "required": [
                "host",
                "port",
                "tls"
              ]
            },
            "default": []
          },
          "databases": {
            "description": "Bancos lidos com ctx.db.query, só leitura",
            "type": "array",
            "maxItems": 10,
            "items": {
              "description": "Um banco que o agente lê com um usuário só leitura, por ctx.db.query",
              "type": "object",
              "properties": {
                "name": {
                  "description": "Como o código chama este banco em ctx.db.query({ database })",
                  "type": "string",
                  "pattern": "^[a-z][a-z0-9_]{0,31}$"
                },
                "engine": {
                  "description": "sqlserver, oracle, firebird, mysql ou postgres",
                  "type": "string",
                  "enum": [
                    "sqlserver",
                    "oracle",
                    "firebird",
                    "mysql",
                    "postgres"
                  ]
                },
                "host": {
                  "description": "Host na rede da loja, como a máquina do agente o resolve: um nome, um domínio ou um IPv4",
                  "type": "string"
                },
                "port": {
                  "description": "Porta do banco (1433 SQL Server, 1521 Oracle, 3050 Firebird, 3306 MySQL, 5432 Postgres)",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 65535
                },
                "database": {
                  "description": "O banco como o servidor o conhece: nome do banco, `service name` do Oracle, caminho ou alias do Firebird",
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 512
                },
                "username": {
                  "description": "Nome do segredo do conector com o usuário só leitura do banco",
                  "type": "string",
                  "pattern": "^[A-Z][A-Z0-9_]{0,63}$"
                },
                "password": {
                  "description": "Nome do segredo do conector com a senha desse usuário",
                  "type": "string",
                  "pattern": "^[A-Z][A-Z0-9_]{0,63}$"
                }
              },
              "required": [
                "name",
                "engine",
                "host",
                "port",
                "database",
                "username",
                "password"
              ]
            },
            "default": []
          }
        },
        "required": [
          "agentId"
        ]
      },
      "default": []
    },
    "secrets": {
      "description": "Segredos que o código lê com ctx.secret(nome): uma pessoa preenche o valor, que nunca fica no código",
      "type": "array",
      "maxItems": 20,
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "description": "Nome que o código usa para referenciar o segredo",
            "type": "string",
            "pattern": "^[A-Z][A-Z0-9_]{0,63}$"
          },
          "label": {
            "description": "O que a pessoa preenche, em português",
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "help": {
            "description": "Onde a pessoa encontra o valor no outro sistema",
            "type": "string",
            "maxLength": 500
          }
        },
        "required": [
          "name",
          "label"
        ]
      }
    },
    "externalWrites": {
      "description": "Se o conector escreve em outro sistema",
      "type": "boolean"
    }
  },
  "required": [
    "version",
    "triggers",
    "hosts",
    "secrets",
    "externalWrites"
  ],
  "additionalProperties": false
}