Partilhar via


Criar uma assinatura de gancho de serviço programaticamente

Serviços de DevOps do Azure | Azure DevOps Server | Azure DevOps Server 2022

Você pode usar uma assinatura para executar uma ação em um serviço externo ou de consumidor quando um evento específico ocorre em um projeto do Azure DevOps. Por exemplo, uma assinatura pode notificar seu serviço quando uma compilação falhar.

Para criar uma assinatura programaticamente, você pode usar as APIs REST de assinaturas. Este artigo fornece uma solicitação de exemplo e um código de exemplo para criar uma assinatura.

Pré-requisitos

Categoria Requerimentos
Acesso ao projeto Membro do projeto.
de dados - ID do projeto. Use o da API REST do Project para obter a ID do projeto.
- ID do evento e configurações. Consulte Eventos de Service Hook.
- IDs e configurações de utilizadores e ações. Consulte Utilizadores de hooks de serviço.

Eventos suportados

O Azure DevOps fornece suporte para vários eventos de gatilho. Os exemplos incluem os seguintes eventos:

  • Construção concluída
  • Código enviado (para projetos Git)
  • Pull request criado ou atualizado (para projetos Git)
  • Código submetido (para projetos do Controle de Versão do Team Foundation)
  • Item de trabalho criado, atualizado, excluído, restaurado ou comentado

Para controlar quais eventos acionam uma ação, você pode configurar filtros em suas assinaturas. Por exemplo, você pode filtrar o evento build completed com base no status da compilação.

Criar um pedido

Ao criar uma assinatura, você usa o corpo de uma solicitação HTTP POST para especificar a ID do projeto, evento, consumidor, ação e configurações relacionadas.

Você pode usar a solicitação a seguir para criar uma assinatura para um evento de compilação concluída. Neste exemplo, quando a WebSite.CI compilação falha, a assinatura envia uma solicitação POST para https://myservice/event.

Pedido

{
    "publisherId": "tfs",
    "eventType": "build.complete",
    "resourceVersion": "1.0",
    "consumerId": "webHooks",
    "consumerActionId": "httpRequest",
    "publisherInputs": {
        "buildStatus": "failed",
        "definitionName": "WebSite.CI",
        "projectId": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff",
    },
    "consumerInputs": {
        "url": " https://myservice/event"
    },
}

É altamente recomendável usar URLs HTTPS seguras para a segurança dos dados privados no objeto JSON.

Resposta

A solicitação para criar a assinatura gera uma resposta semelhante à seguinte:

{
    "id": "aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e",
    "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/hooks/subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e",
    "publisherId": "tfs",
    "eventType": "build.complete",
    "resourceVersion": "1.0",
    "consumerId": "webHooks",
    "consumerActionId": "httpRequest",
    "createdBy": {
        "id": "22cc22cc-dd33-ee44-ff55-66aa66aa66aa"
    },
    "createdDate": "2014-03-28T16:10:06.523Z",
    "modifiedBy": {
        "id": "22cc22cc-dd33-ee44-ff55-66aa66aa66aa"
    },
    "modifiedDate": "2014-04-25T18:15:26.053Z",
    "publisherInputs": {
        "buildStatus": "failed",
        "definitionName": "WebSite.CI",
        "hostId": "d3d3d3d3-eeee-ffff-aaaa-b4b4b4b4b4b4",
        "projectId": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff",
        "tfsSubscriptionId": "ffff5f5f-aa6a-bb7b-cc8c-dddddd9d9d9d"
    },
    "consumerInputs": {
        "url": "http://myservice/event"
    }
}

Se a solicitação de assinatura falhar, você receberá um código de resposta HTTP de 400 com uma mensagem com mais detalhes.

O que acontece quando o evento ocorre?

Quando ocorre um evento, todas as assinaturas habilitadas no projeto são avaliadas. Em seguida, a ação do consumidor é executada para todas as assinaturas correspondentes.

Versões de recursos (avançadas)

O controle de versão de recursos é aplicável quando uma API está em prévia. Para a maioria dos cenários, especificar 1.0 como a versão do recurso é a rota mais segura.

A carga útil do evento enviada a determinados consumidores inclui uma representação JSON de um recurso de assunto. Por exemplo, a carga enviada para webhooks, Barramento de Serviço do Azure e Armazenamento do Azure inclui informações sobre uma compilação ou item de trabalho. A representação deste recurso pode ter várias formas ou versões.

Você pode especificar a versão do recurso que deseja enviar ao serviço ao consumidor por meio do resourceVersion campo na assinatura.

A versão do recurso é a mesma que a versão da API . Se você não especificar uma versão do recurso, a versão mais recente, latest released, será usada. Para ajudar a garantir uma carga útil de evento consistente ao longo do tempo, especifique sempre uma versão do recurso.

Perguntas frequentes

P: Existem serviços que posso subscrever manualmente?

R: Sim. Para obter mais informações sobre os serviços que o/a pode subscrever numa página de administração de projeto, consulte Integrar com Ganchos de Serviço.

P: Existem bibliotecas C# que eu possa usar para criar assinaturas?

R: Não, mas aqui está um exemplo para ajudá-lo a começar. Para autenticação no Azure DevOps, o código a seguir usa um token de acesso pessoal (PAT) armazenado no Cofre da Chave do Azure. Em um ambiente de produção, use um método de autenticação mais seguro. Para obter mais informações, consulte Escolher o mecanismo de autenticação correto.

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.ServiceHooks.WebApi;
using Microsoft.VisualStudio.Services.WebApi;

namespace CreateServiceHookSubscription
{
    internal class Program
    {
        // Create a service hook subscription to send a message to an Azure Service Bus queue when code is pushed to a Git repository.

        static async Task Main(string[] args)
        {
            // Get the secrets from the key vault.
            string keyVaultURI = "https://<key-vault-name>.vault.azure.net/";
            var secretClient = new SecretClient(new Uri(keyVaultURI), new DefaultAzureCredential());
            string personalAccessTokenSecretName = "<personal-access-token-secret-name>";
            string serviceBusConnectionStringSecretName = "<Service-Bus-connection-string-secret-name>";
            KeyVaultSecret personalAccessTokenSecret = await secretClient.GetSecretAsync(personalAccessTokenSecretName);
            KeyVaultSecret serviceBusConnectionStringSecret = await secretClient.GetSecretAsync(serviceBusConnectionStringSecretName);

            // Set up the connection parameters for Azure DevOps.
            var azureDevOpsOrganizationURL = new Uri("https://dev.azure.com/<Azure-DevOps-organization-name>/");
            string azureDevOpsTeamProjectID = "<Azure-DevOps-team-project-ID>";
            string azureDevOpsPersonalAccessToken = personalAccessTokenSecret.Value;

            // Set up the event parameters.
            string eventPublisherID = "tfs";
            string eventID = "git.push";
            string eventDescription = "Any stage in any release";
            string resourceVersion = "1.0";

            // Set up the consumer parameters.
            string consumerID = "azureServiceBus";
            string consumerActionID = "serviceBusQueueSend";
            string serviceBusNamespace = "<Service-Bus-namespace>";
            string serviceBusQueueName = "<Service-Bus-queue-name>";
            string consumerActionDescription = $"Send a message to the Service Bus {serviceBusQueueName} queue in the {serviceBusNamespace} namespace.";
            string serviceBusConnectionString = serviceBusConnectionStringSecret.Value;

            // Configure the subscription.
            var subscription = new Subscription()
            {
                PublisherId = eventPublisherID,
                PublisherInputs = new Dictionary<string, string>
                {
                    ["projectId"] = azureDevOpsTeamProjectID
                },
                EventType = eventID,
                EventDescription = eventDescription,
                ResourceVersion = resourceVersion,
                ActionDescription = consumerActionDescription,
                ConsumerActionId = consumerActionID,
                ConsumerId = consumerID,
                ConsumerInputs = new Dictionary<string, string>
                {
                    ["connectionString"] = serviceBusConnectionString,
                    ["queueName"] = serviceBusQueueName
                }
            };

            // Connect to the Azure DevOps organization and get a service hook client.
            var azureDevOpsCredentials = new VssBasicCredential(azureDevOpsPersonalAccessToken, string.Empty);
            var azureDevOpsConnection = new VssConnection(azureDevOpsOrganizationURL, azureDevOpsCredentials);
            var serviceHookClient = azureDevOpsConnection.GetClient<ServiceHooksPublisherHttpClient>();

            // Create the subscription.
            var createdSubscription = await serviceHookClient.CreateSubscriptionAsync(subscription);
            Console.WriteLine($"A subscription was created that has ID {createdSubscription.Id}.");
        }
    }
}