Partilhar via


Gerenciamento de recursos Python

A biblioteca de gerenciamento de recursos Python fornece uma maneira de desenvolver e expor a funcionalidade do aplicativo com base em sinalizadores de recursos. Uma vez que um novo recurso é desenvolvido, muitos aplicativos têm requisitos especiais, como quando o recurso deve ser ativado e em que condições. Essa biblioteca fornece uma maneira de definir essas relações e também se integra a padrões de código Python comuns para tornar possível a exposição desses recursos.

Os sinalizadores de recursos fornecem uma maneira para os aplicativos Python ativarem ou desativarem recursos dinamicamente. Os desenvolvedores podem usar sinalizadores de recursos em casos de uso simples, como instruções condicionais.

Aqui estão alguns dos benefícios de usar a biblioteca de gerenciamento de recursos Python:

  • Uma convenção comum para o gerenciamento de recursos

  • Baixa barreira à entrada

    • Suporta configuração de sinalizador de recurso JSON
  • Gerenciamento do tempo de vida do sinalizador de recurso

    • Os valores de configuração podem mudar em tempo real; Os sinalizadores de recursos podem ser consistentes em toda a solicitação
  • Cenários simples a complexos cobertos

    • Ativar e desativar recursos por meio do arquivo de configuração declarativa
    • Avalie dinamicamente o estado do recurso com base na chamada para o servidor

    A biblioteca de gerenciamento de recursos Python é de código aberto. Para obter mais informações, visite o repositório GitHub.

Marcas de funcionalidades

Os sinalizadores de recursos são compostos por duas partes, um nome e uma lista de filtros de recursos que são usados para ativar o recurso.

Filtros de recursos

Os filtros de recursos definem um cenário para quando um recurso deve ser habilitado. Quando um recurso é avaliado se está ativado ou desativado, sua lista de filtros de recursos é percorrida até que um dos filtros decida que o recurso deve ser habilitado. Neste ponto, o recurso é considerado habilitado e atravessa os filtros de recurso para. Se nenhum filtro de recurso indicar que o recurso deve ser habilitado, ele será considerado desativado.

Como exemplo, um filtro de recurso do navegador Microsoft Edge pode ser projetado. Esse filtro de recursos ativaria todos os recursos anexados a ele, desde que uma solicitação HTTP seja proveniente do Microsoft Edge.

Configuração do sinalizador de recursos

Um dicionário Python é usado para definir sinalizadores de recursos. O dicionário é composto por nomes de recursos como chaves e objetos de sinalizador de recursos como valores. O objeto de sinalizador de recurso é um dicionário que contém uma conditions chave, que por sua vez contém a client_filters chave. A client_filters chave é uma lista de filtros de recursos que são usados para determinar se o recurso deve ser habilitado.

Declaração de sinalizador de recurso

A biblioteca de gerenciamento de recursos suporta json como uma fonte de sinalizador de recurso. Abaixo temos um exemplo do formato usado para configurar sinalizadores de recursos em um arquivo JSON.

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "FeatureT",
                "enabled": "true"
            },
            {
                "id": "FeatureU",
                "enabled": "false"
            },
            {
                "id": "FeatureV",
                "enabled": "true",
                "conditions": {
                    "client_filters": [
                        {
                            "name": "Microsoft.TimeWindow",
                            "parameters": {
                                "Start": "Wed, 01 May 2019 13:59:59 GMT",
                                "End": "Mon, 01 Jul 2019 00:00:00 GMT"
                            }
                        }
                    ]
                }
            }
        ]
    }
}

A feature_management seção do documento json é usada por convenção para carregar as configurações do sinalizador de recurso. A feature_flags seção é uma lista dos sinalizadores de recursos que são carregados na biblioteca. Na seção acima, vemos três características diferentes. Os recursos definem seus filtros de recursos usando a client_filters propriedade, dentro do conditions. Nos filtros de recursos para FeatureT, vemos enabled que está ativado sem filtros definidos, resultando em FeatureT sempre retornando true . FeatureU é o mesmo que FeatureT mas com enabled está false resultando no recurso sempre retornando false. FeatureV Especifica um filtro de recurso chamado Microsoft.TimeWindow. FeatureV é um exemplo de um filtro de recurso configurável. Podemos ver no exemplo que o filtro tem uma parameters propriedade. A parameters propriedade é usada para configurar o filtro. Nesse caso, os horários de início e término para que o recurso esteja ativo são configurados.

O esquema detalhado da feature_management seção pode ser encontrado aqui.

Avançado: O uso de dois pontos ':' é proibido em nomes de sinalizadores de recursos.

Declaração de ativação/desativação

O trecho a seguir demonstra uma maneira alternativa de definir um recurso que pode ser usado para recursos ativados/desativados.

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "FeatureT",
                "enabled": "true"
            },
            {
                "id": "FeatureX",
                "enabled": "false"
            }
        ]
    }
}

Requirement_type

A requirement_type propriedade de um sinalizador de recurso é usada para determinar se os filtros devem usar Any ou All lógica ao avaliar o estado de um recurso. Se requirement_type não for especificado, o valor padrão será Any.

  • Any significa que apenas um filtro precisa ser avaliado como true para que o recurso seja habilitado.
  • All significa que cada filtro precisa ser avaliado como true para que o recurso seja habilitado.

A requirement_type de All muda a travessia. Primeiro, se não houver filtros, o recurso será desativado. Em seguida, os filtros de recurso são percorridos até que um dos filtros decida que o recurso deve ser desativado. Se nenhum filtro indicar que o recurso deve ser desativado, ele será considerado habilitado.

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "FeatureW",
                "enabled": "true",
                "conditions": {
                    "requirement_type": "All",
                    "client_filters": [
                        {
                            "name": "Microsoft.TimeWindow",
                            "parameters": {
                                "Start": "Wed, 01 May 2019 13:59:59 GMT",
                                "End": "Mon, 01 Jul 2019 00:00:00 GMT"
                            }
                        },
                        {
                            "name": "Percentage",
                            "parameters": {
                                "Value": "50"
                            }
                        }
                    ]
                }
            },
        ]
    }
}

No exemplo acima, FeatureW especifica a requirement_type de All, o que significa que todos os seus filtros devem ser avaliados como true para que o recurso seja habilitado. Nesse caso, o recurso é habilitado para 50% dos usuários durante a janela de tempo especificada.

Consumo

A forma básica de gerenciamento de recursos é verificar se um sinalizador de recurso está habilitado e, em seguida, executar ações com base no resultado. A verificação do estado de um sinalizador de recurso é feita através FeatureManagerdo is_enabled método .

…
feature_manager = FeatureManager(feature_flags)
…
if feature_manager.is_enabled("FeatureX"):
    # Do something

O feature_flags fornecido para FeatureManager pode ser o AzureAppConfigurationProvider ou um dicionário de bandeiras de recurso.

Implementando um filtro de recursos

A criação de um filtro de recursos fornece uma maneira de habilitar recursos com base em critérios definidos por você. Para implementar um filtro de recursos, a FeatureFilter interface deve ser implementada. FeatureFilter tem um único método chamado evaluate. Quando um recurso especifica que pode ser habilitado para um filtro de recurso, o evaluate método é chamado. Se evaluate retornar true, significa que o recurso deve ser habilitado.

O trecho a seguir demonstra como adicionar um filtro MyCustomFilterde recurso personalizado.

feature_manager = FeatureManager(feature_flags, feature_filters=[MyCustomFilter()])

Os filtros de recursos são registrados fornecendo-os à propriedade feature_filters ao criar FeatureManagero . Se um filtro de recurso personalizado precisar de qualquer contexto, ele poderá ser passado ao chamar is_enabled usando kwargso .

Atributo de alias de filtro

Quando um filtro de feição é registrado para um sinalizador de recurso, o nome do filtro é usado como o alias por padrão.

O identificador do filtro de recursos pode ser substituído usando o @FeatureFilter.alias("MyFilter"). Um filtro de recurso pode ser decorado com esse atributo para declarar o nome que deve ser usado na configuração para fazer referência a esse filtro de recurso dentro de um sinalizador de recurso.

Filtros de recursos ausentes

Se um recurso estiver configurado para ser habilitado para um filtro de recurso específico e esse filtro de recurso não estiver registrado, uma ValueError exceção será gerada quando o recurso for avaliado.

Filtros de recursos integrados

Há dois filtros de recursos que acompanham o FeatureManagement pacote: TimeWindowFilter, e TargetingFilter.

Cada um dos filtros de recursos internos tem seus próprios parâmetros. Aqui está a lista de filtros de recursos, juntamente com exemplos.

Microsoft.TimeWindow

Esse filtro fornece a capacidade de habilitar um recurso com base em uma janela de tempo. Se apenas End for especificado, o recurso será considerado ativado até esse momento. Se apenas Start for especificado, o recurso será considerado ativado em todos os pontos após esse período.

"client_filters": [
    {
        "name": "Microsoft.TimeWindow",
        "parameters": {
            "Start": "Wed, 01 May 2019 13:59:59 GMT",
            "End": "Mon, 01 Jul 2019 00:00:00 GMT"
        }
    }
]     

Microsoft.Targeting

Esse filtro fornece a capacidade de habilitar um recurso para um público-alvo. Uma explicação detalhada da segmentação é explicada na seção de segmentação abaixo. Os parâmetros de filtro incluem um Audience objeto que descreve usuários, grupos, usuários/grupos excluídos e uma porcentagem padrão da base de usuários que deve ter acesso ao recurso. Cada objeto de grupo listado Groups na seção também deve especificar qual porcentagem dos membros do grupo deve ter acesso. Se um usuário for especificado na Exclusion seção, diretamente ou se o usuário estiver em um grupo excluído, o recurso será desabilitado. Caso contrário, se um usuário for especificado na Users seção diretamente, ou se o usuário estiver na porcentagem incluída de qualquer uma das distribuições do grupo, ou se o usuário cair na porcentagem de distribuição padrão, esse usuário terá o recurso habilitado.

"client_filters": [
    {
        "name": "Microsoft.Targeting",
        "parameters": {
            "Audience": {
                "Users": [
                    "Jeff",
                    "Alicia"
                ],
                "Groups": [
                    {
                        "Name": "Ring0",
                        "RolloutPercentage": 100
                    },
                    {
                        "Name": "Ring1",
                        "RolloutPercentage": 50
                    }
                ],
                "DefaultRolloutPercentage": 20,
                "Exclusion": {
                    "Users": [
                        "Ross"
                    ],
                    "Groups": [
                        "Ring2"
                    ]
                }
            }
        }
    }
]

Seleção do destino

A segmentação é uma estratégia de gerenciamento de recursos que permite que os desenvolvedores implementem progressivamente novos recursos em sua base de usuários. A estratégia baseia-se no conceito de segmentação de um conjunto de utilizadores conhecido como público-alvo. Um público é composto por usuários específicos, grupos, usuários/grupos excluídos e uma porcentagem designada de toda a base de usuários. Os grupos que estão incluídos na audiência podem ser divididos em porcentagens de seus membros totais.

As etapas a seguir demonstram um exemplo de uma distribuição progressiva para um novo recurso 'Beta':

  1. Os usuários individuais Jeff e Alicia recebem acesso ao Beta
  2. Outro usuário, Mark, pede para aceitar e é incluído.
  3. Vinte por cento de um grupo conhecido como "Ring1" usuários estão incluídos no Beta.
  4. O número de usuários "Ring1" incluídos na versão beta é aumentado para 100%.
  5. Cinco por cento da base de utilizadores está incluída na versão beta.
  6. A porcentagem de lançamento é aumentada para 100% e o recurso é completamente implementado.

Essa estratégia para implantar um recurso é incorporada à biblioteca por meio do filtro de recursos Microsoft.Targeting incluído.

Segmentar um utilizador

Um usuário pode ser especificado diretamente na is_enabled chamada ou um TargetingContext pode ser usado para especificar o usuário e o grupo opcional.

# Directly specifying the user
result = is_enabled(feature_flags, "test_user")

# Using a TargetingContext
result = is_enabled(feature_flags, TargetingContext(user_id="test_user", groups=["Ring1"]))

Segmentação da exclusão

Ao definir uma audiência, os usuários e grupos podem ser excluídos da audiência. As exclusões são úteis para quando um recurso está sendo implementado para um grupo de usuários, mas alguns usuários ou grupos precisam ser excluídos da implantação. A exclusão é definida pela adição de uma lista de usuários e grupos à Exclusion propriedade da audiência.

"Audience": {
    "Users": [
        "Jeff",
        "Alicia"
    ],
    "Groups": [
        {
            "Name": "Ring0",
            "RolloutPercentage": 100
        }
    ],
    "DefaultRolloutPercentage": 0
    "Exclusion": {
        "Users": [
            "Mark"
        ]
    }
}

No exemplo acima, o recurso está habilitado para usuários chamados Jeff e Alicia. Ele também está habilitado para usuários no grupo chamado Ring0. No entanto, se o usuário for nomeado Mark, o recurso será desativado, independentemente de ele estar no grupo Ring0 ou não. As exclusões têm prioridade sobre o restante do filtro de segmentação.

Variantes

Quando novos recursos são adicionados a um aplicativo, pode chegar um momento em que um recurso tem várias opções de design propostas diferentes. Uma solução comum para decidir sobre um projeto é alguma forma de teste A/B. O teste A/B envolve fornecer uma versão diferente do recurso para diferentes segmentos da base de usuários e escolher uma versão com base na interação do usuário. Nesta biblioteca, essa funcionalidade é habilitada representando diferentes configurações de um recurso com variantes.

As variantes permitem que um sinalizador de recurso se torne mais do que um simples sinalizador de ligar/desligar. Uma variante representa um valor de um sinalizador de recurso que pode ser uma cadeia de caracteres, um número, um booleano ou até mesmo um objeto de configuração. Um sinalizador de recurso que declara variantes deve definir em que circunstâncias cada variante deve ser usada, o que é abordado com mais detalhes na seção Alocação de variantes.

class Variant:
    def __init__(self, name: str, configuration: Any):
        self._name = name
        self._configuration = configuration

    @property
    def name(self) -> str:
        """
        The name of the variant.
        :rtype: str
        """
        return self._name

    @property
    def configuration(self) -> Any:
        """
        The configuration of the variant.
        :rtype: Any
        """
        return self._configuration

Obter variantes

Para cada recurso, uma variante pode ser recuperada usando o FeatureManagermétodo do get_variant .

…
variant = print(feature_manager.get_variant("TestVariants", TargetingContext(user_id="Adam"))

variantConfiguration = variant.configuration;

// Do something with the resulting variant and its configuration

A variante retornada depende do usuário que está sendo avaliado no momento, e essa informação é obtida de uma instância do TargetingContext.

Declaração de sinalizador de recurso variante

Em comparação com os sinalizadores de recursos normais, os sinalizadores de recursos variantes têm mais duas propriedades: variants e allocation. A variants propriedade é uma matriz que contém as variantes definidas para esse recurso. A allocation propriedade define como essas variantes devem ser alocadas para o recurso. Assim como declarar sinalizadores de recursos normais, você pode configurar sinalizadores de recursos variantes em um arquivo JSON. Aqui está um exemplo de um sinalizador de recurso variante.

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "MyVariantFeatureFlag",
                "enabled": true,
                "allocation": {
                    "default_when_enabled": "Small",
                    "group": [
                        {
                            "variant": "Big",
                            "groups": [
                                "Ring1"
                            ]
                        }
                    ]
                },
                "variants": [
                    { 
                        "name": "Big"
                    },  
                    { 
                        "name": "Small"
                    } 
                ]
            }
        ]
    }
}

Definição de variantes

Cada variante tem duas propriedades: um nome e uma configuração. O nome é usado para se referir a uma variante específica, e a configuração é o valor dessa variante. A configuração pode ser definida usando configuration_value a propriedade. configuration_value é uma configuração embutida que pode ser uma cadeia de caracteres, número, booleano ou objeto de configuração. Se configuration_value não for especificado, a propriedade da variante Configuration retornada será None.

Uma lista de todas as variantes possíveis é definida para cada recurso sob a variants propriedade.

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "MyVariantFeatureFlag",
                "variants": [
                    { 
                        "name": "Big", 
                        "configuration_value": {
                            "Size": 500
                        }
                    },  
                    { 
                        "name": "Small", 
                        "configuration_value": {
                            "Size": 300
                        }
                    } 
                ]
            }
        ]
    }
}

Atribuição de variantes

O processo de alocação das variantes de um recurso é determinado pela allocation propriedade do recurso.

"allocation": { 
    "default_when_enabled": "Small", 
    "default_when_disabled": "Small",  
    "user": [ 
        { 
            "variant": "Big", 
            "users": [ 
                "Marsha" 
            ] 
        } 
    ], 
    "group": [ 
        { 
            "variant": "Big", 
            "groups": [ 
                "Ring1" 
            ] 
        } 
    ],
    "percentile": [ 
        { 
            "variant": "Big", 
            "from": 0, 
            "to": 10 
        } 
    ], 
    "seed": "13973240" 
},
"variants": [
    { 
        "name": "Big", 
        "configuration_value": "500px"
    },  
    { 
        "name": "Small", 
        "configuration_value": "300px"
    } 
]

A allocation configuração de um recurso tem as seguintes propriedades:

Property Description
default_when_disabled Especifica qual variante deve ser usada quando uma variante é solicitada enquanto o recurso é considerado desativado.
default_when_enabled Especifica qual variante deve ser usada quando uma variante é solicitada enquanto o recurso é considerado habilitado e nenhuma outra variante foi atribuída ao usuário.
user Especifica uma variante e uma lista de usuários aos quais essa variante deve ser atribuída.
group Especifica uma variante e uma lista de grupos. A variante é atribuída se o usuário estiver em pelo menos um dos grupos.
percentile Especifica uma variante e um intervalo de porcentagem no qual a porcentagem calculada do usuário deve se encaixar para que essa variante seja atribuída.
seed O valor em percentile que se baseiam os cálculos percentuais. O cálculo de porcentagem para um usuário específico será o mesmo em todos os recursos se o mesmo seed valor for usado. Se não seed for especificado, uma semente padrão será criada com base no nome do recurso.

Se o recurso não estiver habilitado, o gerenciador de recursos atribuirá a variante marcada como default_when_disabled ao usuário atual, que é Small neste caso.

Se o recurso estiver habilitado, o gerenciador de recursos verificará o user, groupe as alocações nessa ordem para atribuir uma variante percentile . Para este exemplo em particular, se o usuário que está sendo avaliado for nomeado Marsha, no grupo chamado Ring1, ou se o usuário cair entre o percentil 0 e 10, a variante especificada será atribuída ao usuário. Nesse caso, todos os usuários atribuídos retornariam a Big variante. Se nenhuma dessas alocações corresponder, o usuário receberá a default_when_enabled variante, que é Small.

A lógica de alocação é semelhante ao filtro de recursos Microsoft.Targeting , mas há alguns parâmetros presentes na segmentação que não estão na alocação e vice-versa. Os resultados da segmentação e da alocação não estão relacionados.

Substituindo o estado habilitado por uma variante

Você pode usar variantes para substituir o estado ativado de um sinalizador de recurso. A substituição dá às variantes a oportunidade de estender a avaliação de um sinalizador de recurso. Ao chamar is_enabled um sinalizador com variantes, o gerenciador de recursos verificará se a variante atribuída ao usuário atual está configurada para substituir o resultado. A substituição é feita usando a propriedade status_overridevariant opcional. Por padrão, essa propriedade é definida como None, o que significa que a variante não afeta se o sinalizador é considerado habilitado ou desabilitado. Configuração status_override para Enabled permitir que a variante, quando escolhida, substitua um sinalizador a ser habilitado. Configuração status_override para Disabled fornecer a funcionalidade oposta, portanto, desativando o sinalizador quando a variante é escolhida. Um recurso com um enabled estado de não pode ser substituído false .

Se você estiver usando um sinalizador de recurso com variantes binárias, a status_override propriedade pode ser útil. Ele permite que você continue usando APIs como is_enabled em seu aplicativo, enquanto se beneficia dos novos recursos que vêm com variantes, como alocação de percentis e semente.

{
    "id": "MyVariantFeatureFlag",
    "enabled": true,
    "allocation": {
        "percentile": [
            {
                "variant": "On",
                "from": 10,
                "to": 20
            }
        ],
        "default_when_enabled":  "Off",
        "seed": "Enhanced-Feature-Group"
    },
    "variants": [
        {
            "name": "On"
        },
        {
            "name": "Off",
            "status_override": "Disabled"
        }
    ]
}

No exemplo acima, o recurso está sempre ativado. Se o usuário atual estiver no intervalo de percentis calculado de 10 a 20, a On variante será retornada. Caso contrário, a Off variante é retornada e, como status_override é igual a Disabled, o recurso agora será considerado desativado.

Telemetria

Quando uma alteração de sinalizador de recurso é implantada, geralmente é importante analisar seu efeito em um aplicativo. Por exemplo, aqui estão algumas perguntas que podem surgir:

  • Os meus sinalizadores estão ativados/desativados conforme esperado?
  • Os usuários-alvo estão tendo acesso a um determinado recurso conforme o esperado?
  • Qual variante um usuário específico está vendo?

Estes tipos de perguntas podem ser respondidas através da emissão e análise de eventos de avaliação de sinalizadores de funcionalidades. Opcionalmente, essa biblioteca permite a AzureMonitor telemetria de rastreamento de produção durante a avaliação do sinalizador de recursos via OpenTelemetry.

Habilitando a telemetria

Por padrão, os sinalizadores de recursos não têm telemetria emitida. Para publicar telemetria para um determinado sinalizador de recurso, o sinalizador DEVE declarar que está habilitado para emissão de telemetria.

Para sinalizadores de recursos definidos em json, a habilitação é feita usando a telemetry propriedade.

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "MyFeatureFlag",
                "enabled": true,
                "telemetry": {
                    "enabled": true
                }
            }
        ]
    }
}

O trecho acima define um sinalizador de recurso chamado MyFeatureFlag que está habilitado para telemetria. A telemetry propriedade do enabled objeto é definida como true. O valor da enabled propriedade deve ser true publicar telemetria para o sinalizador.

A telemetry seção de um sinalizador de recurso tem as seguintes propriedades:

Property Description
enabled Especifica se a telemetria deve ser publicada para o sinalizador de recurso.
metadata Uma coleção de pares chave-valor, modelada como um dicionário, que pode ser usada para anexar metadados personalizados sobre o sinalizador de recurso a eventos de avaliação.

Além disso, ao criar FeatureManagero , um retorno de chamada deve ser registrado para manipular eventos de telemetria. Esse retorno de chamada é chamado sempre que um sinalizador de recurso é avaliado e a telemetria é habilitada para esse sinalizador.

feature_manager = FeatureManager(feature_flags, on_feature_evaluated=publish_telemetry)

Telemetria do Application Insights

A biblioteca de gerenciamento de recursos fornece um editor de telemetria interno que envia dados de avaliação de sinalizadores de recursos para o Application Insights. Para habilitar o Application Insights, a biblioteca de gerenciamento de recursos pode ser instalada com o Azure Monitor via pip install FeatureManagement[AzureMonitor]. Este comando instala o azure-monitor-events-extension pacote, que é usado para estilizar telemetria para o Application Insights usando OpenTelemetry.

Nota

O azure-monitor-events-extension pacote adiciona apenas a telemetria ao pipeline de Telemetria Aberta. O registro do Application Insights ainda é necessário.

from azure.monitor.opentelemetry import configure_azure_monitor

configure_azure_monitor(
        connection_string="InstrumentationKey=00000000-0000-0000-0000-000000000000"
    )

Publicação de telemetria personalizada

Como o retorno de chamada de telemetria é uma função, ele pode ser personalizado para publicar a telemetria em qualquer destino desejado. Por exemplo, a telemetria pode ser publicada em um serviço de log, um banco de dados ou um serviço de telemetria personalizado.

Quando um sinalizador de recurso é avaliado e a telemetria é habilitada, o gerenciador de recursos chama o retorno de chamada de telemetria com um EvaluationEvent parâmetro. EvaluationEvent Contém as seguintes propriedades:

Etiqueta Description
feature O sinalizador de recurso usado.
user O ID de usuário usado para segmentação.
enabled Se o sinalizador de recurso é avaliado como habilitado.
Variant A variante atribuída.
VariantAssignmentReason A razão pela qual a variante é atribuída.

Próximos passos

Para saber como usar sinalizadores de recursos em seus aplicativos, continue para os seguintes inícios rápidos.

Para saber como usar filtros de recursos, continue para os tutoriais a seguir.