コード例 #1
0
        public async Task <Dictionary <string, string> > ProcessAsync()
        {
            JObject record = JObject.Parse(this.requestBody);

            // Some events e.g., "member_added", does not have "repository" as part of the Webhooks payload since they are only associated with the
            // organization and not with a specific repository. Use defaults (0 and empty string) for repository id and repository name since they are optional.
            long   repositoryId   = Repository.NoRepositoryId;
            string repositoryName = Repository.NoRepositoryName;

            JToken repositoryIdToken = record.SelectToken("$.repository.id");

            if (repositoryIdToken != null)
            {
                repositoryId = repositoryIdToken.Value <long>();
            }
            JToken repositoryNameToken = record.SelectToken("$.repository.name");

            if (repositoryNameToken != null)
            {
                repositoryName = repositoryNameToken.Value <string>();
            }

            // There have been GitHub payloads that are impossible to process because they are missing required attributes (e.g., organization, repository.id, etc.).
            // Instead of failing with a cryptic Newtonsoft JSON parsing error, handle these cases more graciously and log them in telemetry for future debugging and validation.

            if (!repositoryName.Equals(Repository.NoRepositoryName) && repositoryId == Repository.NoRepositoryId)
            {
                // Case 1: there is a repository name but no repository id.
                Dictionary <string, string> properties = new Dictionary <string, string>()
                {
                    { "RequestBody", this.requestBody },
                    { "Reason", "repository.id is missing." },
                };
                this.telemetryClient.TrackEvent("UnexpectedWebhookPayload", properties);
                return(new Dictionary <string, string>());
            }

            JToken organizationIdToken = record.SelectToken("$.organization.id");

            if (organizationIdToken == null)
            {
                // Case 2: organization login is missing.
                Dictionary <string, string> properties = new Dictionary <string, string>()
                {
                    { "RequestBody", this.requestBody },
                    { "Reason", "organization.id is missing." },
                };
                this.telemetryClient.TrackEvent("UnexpectedWebhookPayload", properties);
                return(new Dictionary <string, string>());
            }
            long organizationId = organizationIdToken.Value <long>();

            JToken organizationLoginToken = record.SelectToken("$.organization.login");

            if (organizationLoginToken == null)
            {
                // Case 3: organization id is missing.
                Dictionary <string, string> properties = new Dictionary <string, string>()
                {
                    { "RequestBody", this.requestBody },
                    { "Reason", "organization.login is missing." },
                };
                this.telemetryClient.TrackEvent("UnexpectedWebhookPayload", properties);
                return(new Dictionary <string, string>());
            }
            string organizationLogin = organizationLoginToken.Value <string>();

            Repository repository = new Repository(organizationId, repositoryId, organizationLogin, repositoryName);

            foreach (IRecordWriter recordWriter in this.recordWriters)
            {
                recordWriter.SetOutputPathPrefix($"{repository.OrganizationId}/{repository.RepositoryId}");
            }

            if (repository.IsValid())
            {
                int eventCount = await this.eventsBookkeeper.IncrementCountAsync(repository).ConfigureAwait(false);

                if (eventCount >= EventCountLimit)
                {
                    await this.eventsBookkeeper.SignalCountAsync(repository).ConfigureAwait(false);

                    await this.eventsBookkeeper.ResetCountAsync(repository).ConfigureAwait(false);
                }
            }

            string eventType = this.context.EventType;

            await this.CacheRecord(record, repository, eventType).ConfigureAwait(false);

            RecordContext recordContext = new RecordContext()
            {
                RecordType = eventType
            };

            foreach (IRecordWriter recordWriter in this.recordWriters)
            {
                await recordWriter.WriteRecordAsync(record, recordContext).ConfigureAwait(false);
            }

            ICollector collector = CollectorFactory.Instance.GetCollector(eventType, this.context, this.authentication, this.httpClient, this.recordWriters, this.collectorCache, this.telemetryClient, this.apiDomain);
            await collector.ProcessWebhookPayloadAsync(record, repository).ConfigureAwait(false);

            Dictionary <string, string> additionalSessionEndProperties = new Dictionary <string, string>(this.RetrieveAdditionalPrimaryKeys(record))
            {
                { "OrganizationLogin", repository.OrganizationLogin },
                { "OrganizationId", repository.OrganizationId.ToString() },
                { "RepositoryName", repository.RepositoryName },
                { "RepositoryId", repository.RepositoryId.ToString() },
            };

            return(additionalSessionEndProperties);
        }