private async Task <JsonElement?> RecordDeploymentAsync(string appId, DeploymentDto deployment)
        {
            using var client = m_httpClientFactory.CreateClient("newrelic");
            var response = await client.PostAsJsonAsync($"applications/{appId}/deployments.json", new
            {
                deployment,
            });

            var responseContent = await response.Content.ReadFromJsonAsync <JsonElement>();

            m_logger.LogInformation("Recorded deployment\n{StatusCode} {Reason}: {Content}", (int)response.StatusCode, response.ReasonPhrase, responseContent);
            return(response.IsSuccessStatusCode ? responseContent.GetProperty("deployment") : default(JsonElement?));
        }
        private async Task <IReadOnlyList <JsonPatchOperation>?> MutateDeployment(AdmissionRequestDto request)
        {
            if (!(request.Object is JsonElement objectEl))
            {
                m_logger.LogWarning("Invalid object");
                return(null);
            }

            var metadataEl = objectEl.GetProperty("metadata");

            if (!metadataEl.TryGetProperty("annotations", out var annotationsEl) || !annotationsEl.TryGetProperty("newRelic", out var newRelicEl))
            {
                m_logger.LogInformation("No newRelic metadata");
                return(null);
            }

            var newRelicMetadata = JsonSerializer.Deserialize <JsonElement>(newRelicEl.GetString());

            var appId = newRelicMetadata.TryGetStringProperty("appId");

            if (appId is null)
            {
                m_logger.LogInformation("No appId");
                return(null);
            }

            var revision = newRelicMetadata.TryGetStringProperty("revision");

            if (revision is null)
            {
                m_logger.LogInformation("No revision");
                return(null);
            }

            var deployment = new DeploymentDto
            {
                Revision = revision,
                User     = (request.UserInfo?.Username),
                // Omit timestamp. New Relic will respond with BadRequest if the timestamp is in the future by even 1 second.
            };

            var deploymentJson = JsonSerializer.Serialize(deployment, s_camelCaseOptions);

            m_logger.LogInformation("New Relic Deployment: {Deployment}", deploymentJson);

            var lastAppliedDeploymentJson = annotationsEl.TryGetStringProperty("lastAppliedNewRelicDeployment");
            var lastAppliedDeployment     = lastAppliedDeploymentJson is null ? default(JsonElement?) : JsonSerializer.Deserialize <JsonElement>(lastAppliedDeploymentJson);
            var lastAppliedRevision       = lastAppliedDeployment?.TryGetStringProperty("revision");

            if (lastAppliedRevision == revision)
            {
                m_logger.LogInformation("No change from last deployment");
                return(null);
            }

            var deploymentResponse = await RecordDeploymentAsync(appId, deployment);

            var patch = new[]
            {
                new JsonPatchOperation("add", "/metadata/annotations/lastAppliedNewRelicDeployment", JsonSerializer.Serialize(deploymentResponse)),
            };

            return(patch);
        }