Templates Inputs
O poder criar formulários avançados é entender como funciona o react-jsonschema-form. Aqui você encontrará exemplo que estão em json, mas de json para yaml é um pulo! Também podemos conferir a documentação dessa biblioteca aqui.
Template Editor
Uma funcionalidade que podemos aproveitar no Backstage para nos ajudar a criar nosso templates, principalmente em um estágio inicial.
Quando vamos alterando o código é mostrado como ficará o formulário ao lado.
Vamos utilizar esse editor para fazer testes explicando os campos.
Parameters
o campo parameters em templates representa os dados de entrada que o usuário deve fornecer ao executar ou configurar o template. Ele é uma lista de definições de parâmetros que:
- Define quais informações o usuário precisa fornecer.
- Especifica os tipos, validações, descrições e comportamento esperado desses inputs.
- Serve como base para criar a interface do usuário (UI) de entrada de dados.
Um objeto da lista parameters pode conter:
title (obrigatório)
- Define o título da seção/grupo de parâmetros.
- Tipo: string.
description (opcional)
- Fornece uma descrição adicional da seção para ajudar o usuário.
- Tipo: string
properties (obrigatório)
- Define os campos (parâmetros) que o usuário deve preencher. Vamos falar mais abaixo.
- Tipo: object contendo pares chave: valor
required (opcional)
- Lista os nomes dos campos obrigatórios definidos em properties.
- Tipo: array de strings.
allOf, anyOf, oneOf (opcional)
- Define condições ou validações adicionais para os campos.
- Tipo: array de objetos com validações específicas.
- Exemplos:
- allOf: Todas as condições devem ser atendidas.
- anyOf: Pelo menos uma condição deve ser atendida.
- oneOf: Apenas uma condição deve ser atendida.
if, then, else (opcional)
- Permite aplicar lógica condicional aos campos com base nos valores de outros campos.
- Tipo: objetos que especificam:
- if: A condição a ser verificada.
- then: O que acontece se a condição for verdadeira.
- else: O que acontece se a condição for falsa.
errorMessage (opcional)
- Define mensagens de erro personalizadas para validações falhas.
- Tipo: object com pares chave: mensagem, onde a chave é o campo ou validação.
Titles
Os titles dividem o preenchimento em etapas. Basicamente isso.
Poderíamos fazer tudo em uma única etapa, é mais uma questão de organização. Cada vez que criamos um title o botão de next passa para o próximo title (sessão) até chegar em review que irá aplicar os steps.
Properties (Inputs)
Dentro de properties cada objeto será o valor do campo. Podemos dizer que seria o valor da variável e dentro de bloco temos suas definições com as seguintes chaves:
type (obrigatório)
Especifica o tipo de dado do campo.- Tipos válidos: string, integer, boolean, array, object. Quando temos um tipo object declaramos outro properties dentro dele, vamos ver no último exemplo.
title (opcional)
O título do campo, exibido na interface.- Tipo: string.
description (opcional)
Fornece um texto explicativo sobre o campo.- Tipo: string.
enum (opcional)
Restringe os valores possíveis do campo a uma lista fixa.- Tipo: array com valores.
default (opcional)
Valor padrão para o campo.- Tipo: Depende do type.
minimum, maximum (opcional)
Define valores mínimos e máximos para campos do tipo integer.Tipo: number
.minLength, maxLength (opcional)
Define o número mínimo/máximo de caracteres para campos do tipo string.- Tipo: integer.
format (opcional)
Define formatos específicos para o campo (ex.: email, uri, date-time).pattern
Define o regex se o tipo for uma string.- Tipo: string.
ui:*
(opcional) Configurações específicas para a interface do usuário.- Exemplos:
- ui:widget: Tipo de widget usado (checkbox, textarea).
- ui:field: Campo customizado (OwnerPicker).
- ui:options: Opções adicionais para personalização.
- Exemplos:
dependencies (opcional)
Define campos dependentes que só aparecem se o campo principal tiver um valor específico.- Tipo: object.
Existem mais campos se for explicar tudo teremos que documentar a biblioteca react-jsonschema-formd aqui e o propósito não é esse.
Alguns exemplos:
parameters:
- title: Fill in some steps
properties:
name:
title: Simple text input # Se não for definido irá aparecer o nome da variável, nesse caso name.
type: string # Obrigatório. O que esperar do campo de empreenchimento.
description: Description about input # É opcional
maxLength: 8 # Tamanho máximo é opcional
pattern: '^([a-zA-Z][a-zA-Z0-9]*)(-[a-zA-Z0-9]+)*$' # Regra do tempo para entrada esperada baseada em regex, é opcional
ui:autofocus: true
ui:help: 'Hint: additional description...' # Aqui é uma segunda descrição
errorMessage: # Opcional
properties:
name: '1-8 alphanumeric tokens (first starts with letter) delimited by -' # Mensagem de erro para name
Vamos explicar o detalhes iniciais primeiro.
Gerando um erro.
O erro específico para cada um dos campos.
Podemos ter uma entrada com varias linhas caso necessário. Muitas vezes é necessário passar todo um conteúdo para criar um arquivo. Um kubeconfig seria um ótimo exemplo!
parameters:
- title: Fill in some steps
properties:
examples:
title: Place Holde Example
type: string
description: Insert your multi line string
ui:widget: textarea
ui:options:
rows: 10
ui:placeholder: |
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage
spec:
type: library
owner: CNCF
lifecycle: experimental
Um exemplo de seleção entre possíveis valores.
parameters:
- title: Fill in some steps
properties:
volume_type:
title: Volume Type
type: string
description: The volume type to be used
default: gp2 # Esse será o valor padrão caso não tenha mudança
enum:
- gp2
- gp3
- io1
- io2
- sc1
- st1
- standard
enumNames:
- 'General Purpose SSD (gp2)'
- 'General Purpose SSD (gp3)'
- 'Provisioned IOPS (io1)'
- 'Provisioned IOPS (io2)'
- 'Cold HDD (sc1)'
- 'Throughput Optimized HDD (st1)'
- 'Magnetic (standard)'
As vezes é necessário escolher mais de um.
parameters:
- title: Fill in some steps
properties:
name:
title: Select environments
type: array
items:
type: string
enum:
- production
- staging
- development
uniqueItems: true
ui:widget: checkboxes # observe que aqui usamos o checkbox
As vezes é necessário misturar.
Vários exemplos usando um boleano.
o uniqueItems nesse exemplo não faz muito sentido pois cada entrada é única. Já nesse sentido abaixo já faz um pouco mais de sentido.
# Edit the template parameters below to see how they will render in the scaffolder form UI
parameters:
- title: Configure servers
properties:
serverNames:
title: Enter server names
type: array
items:
type: string
uniqueItems: true
description: "List of unique server names. Duplicate entries are not allowed."
required:
- serverNames
Alguns inputs podem ou não aparece dependendo de outra entrada.
parameters:
- title: Configure your cluster
properties:
clusterType: # Estamos dando duas opções
title: Select cluster type
type: string
enum:
- Development
- Production
nodeCount:
title: Number of nodes
type: integer
minimum: 1 # Aqui ele já inicial com 1 e não deixar de zero
enableAutoScaling:
title: Enable Auto Scaling
type: boolean
required:
- clusterType
dependencies:
# Dependendo de qual clusterType tivermos precisamos de mais entradas.
clusterType:
oneOf:
- properties:
clusterType:
const: Production
# Se production então esses dois campos irão aparecer
autoScalingMinNodes:
title: Minimum nodes for auto-scaling
type: integer
minimum: 2
autoScalingMaxNodes:
title: Maximum nodes for auto-scaling
type: integer
minimum: 3
required:
- autoScalingMinNodes
- autoScalingMaxNodes
- properties:
clusterType:
const: Development
# Se desenvolvimento esse campo irá aparecer
debugMode:
title: Enable debug mode
type: boolean
Podemos fazer campos aparecerem se uma funcionalidade esta ou não ativada (feature flags). Não é a mesma idéia proposta acima em um fluxo padrão, mas também funciona como uma condicional.
Secrets
Aqui podemos ver um exemplo escondendo o campo de password
parameters:
- title: Authenticaion
description: Provide authentication for the resource
required:
- username
- password
properties:
username:
title: username
type: string
password:
title: password
type: string
ui:field: Secret
Um outro caso é que um objeto pode conter outro, depetindo toda a cadeia de properties.
Campos Customizados
Você ainda pode criar campos customizados.
Configuramos pattern para forçar um regex em um determinado campo dizendo que só podería ser letra minuscula e traço. É perfeitamente possível rederizar um campo que isso já esteja implementado e nem precisar especificar.
Poderíamos usar isso para password criando um campo que force um tamanho mínimo e use letras maiucuslas, minusculas, caracteres especiais e números.
ui:field: MeuCampoCustomizado
Claro que irá exigir um conhecimento melhor de node, mas não é muito complicado não.
Campos Customizados BuiltIn
Nessa mesma pegada já existe alguns campos customizados que vão além do que a biblioteca oferece e foi implementado pelo próprio Backstage.
Se estamos entregando algo em um repositório já devemos mapeá-lo para ser descoberto pelo backstage. Provavelmente ele terá um catalog-info.yaml que deve ser preenchido. Com certeza será necessário buscar entidades, definir owner, etc.
Aqui temos o conjunto de ui:fiels customizados para pegar objetos dentro do backstage.
- EntityPicker
- MultiEntityPicker
- OwnerPicker (Importante)
- RepoUrlPicker (Importante)
- RepoBranchPicker
É importante entender bem nossa opções pois a grande maioria dos templates irá utilizar em algum momento isso.
ui:field: RepoUrlPicker
Facilita a seleção de um provedor de repositório e a inserção de um projeto ou proprietário e nome do repositório.
parameters:
- title: Choose a location
required:
- repoUrl
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker # Campo personlizado do Backstage
ui:options:
allowedHosts:
- github.com
- gitlab.com
A allowedHosts
deve ser definida para onde você deseja habilitar este modelo para publicação. E pode ser qualquer host que esteja listado na sua integrations configuração em app-config.yaml.
Podemos restringir quem será o dono do repositorio e quais repositório são permitidos.
Também podemos existir autenticação durante o processo, mas é necessário que toda a parte de autenticação do backstage esteja configurada.
Ainda é possível selecionar uma branch específica usando o RepoBranchPicker se necessário.
ui:field: OwnerPicker
Quase sempre precisaremos definir o owner de um component e provavelmente será algum Group. Não é boa prática definir um user como owner de alguma alguma coisa pois se ele sair do sistema perderemos as relações. Para restringir o owner baseado no que temos no catalog podemos utilizar o OwnerPicker dizendo que somente queremos os groups e com o tipo especificado.
ui:field: EntityPicker e ui:field: MultiEntityPicker
Filtrar uma entidade seria o mesmo processo. Se vamos criar um component que faz parte de um system e queremos definir qual sistema, poderíamos fazer uma filtragem e somente permitir que seja escolhido alguns.