Skip to content

Commit 8ba8c93

Browse files
One more fix
Signed-off-by: Lukasz Gryglicki <lgryglicki@cncf.io> Assisted by [OpenAI](https://platform.openai.com/) Assisted by [GitHub Copilot](https://github.com/features/copilot)
1 parent e618fad commit 8ba8c93

2 files changed

Lines changed: 61 additions & 3 deletions

File tree

cla-backend-legacy/internal/api/handlers.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"context"
99
"encoding/base64"
1010
"encoding/json"
11+
"encoding/xml"
1112
"errors"
1213
"fmt"
1314
"html"
@@ -9151,6 +9152,45 @@ func (h *Handlers) CheckAndPrepareEmployeeSignatureV2(w http.ResponseWriter, r *
91519152
respond.JSON(w, http.StatusOK, map[string]any{"success": []string{"the employee is ready to sign the CCLA"}})
91529153
}
91539154

9155+
type docusignCallbackEnvelope struct {
9156+
EnvelopeStatus struct {
9157+
RecipientStatuses []struct {
9158+
ClientUserID string `xml:"ClientUserId"`
9159+
Status string `xml:"Status"`
9160+
} `xml:"RecipientStatuses>RecipientStatus"`
9161+
} `xml:"EnvelopeStatus"`
9162+
}
9163+
9164+
func extractDocuSignSignatureCompletion(payload []byte) (string, bool) {
9165+
var envelope docusignCallbackEnvelope
9166+
if err := xml.Unmarshal(payload, &envelope); err != nil {
9167+
return "", false
9168+
}
9169+
if len(envelope.EnvelopeStatus.RecipientStatuses) == 0 {
9170+
return "", false
9171+
}
9172+
recipient := envelope.EnvelopeStatus.RecipientStatuses[0]
9173+
signatureID := strings.TrimSpace(recipient.ClientUserID)
9174+
completed := strings.EqualFold(strings.TrimSpace(recipient.Status), "Completed")
9175+
return signatureID, completed
9176+
}
9177+
9178+
func (h *Handlers) waitForSignedSignature(ctx context.Context, signatureID string, attempts int, delay time.Duration) bool {
9179+
if h == nil || h.signatures == nil || strings.TrimSpace(signatureID) == "" {
9180+
return false
9181+
}
9182+
for i := 0; i < attempts; i++ {
9183+
sig, found, err := h.signatures.GetByID(ctx, signatureID)
9184+
if err == nil && found && getAttrBool(sig, "signature_signed") {
9185+
return true
9186+
}
9187+
if i+1 < attempts {
9188+
time.Sleep(delay)
9189+
}
9190+
}
9191+
return false
9192+
}
9193+
91549194
// POST /v2/signed/individual/{installation_id}/{github_repository_id}/{change_request_id}
91559195
// Python: cla/routes.py:1884 post_individual_signed()
91569196
// Calls: cla.controllers.signing.post_individual_signed
@@ -9189,6 +9229,21 @@ func (h *Handlers) PostIndividualSignedV2(w http.ResponseWriter, r *http.Request
91899229
if status >= 400 {
91909230
logging.Warnf("v4 signed/individual returned %d: %s", status, string(respBody))
91919231
}
9232+
if signatureID, completed := extractDocuSignSignatureCompletion(body); completed && signatureID != "" {
9233+
if h.waitForSignedSignature(r.Context(), signatureID, 10, 500*time.Millisecond) {
9234+
if err := h.triggerGitHubChangeRequestUpdateV4(
9235+
r.Context(),
9236+
strings.TrimSpace(chi.URLParam(r, "installation_id")),
9237+
strings.TrimSpace(chi.URLParam(r, "github_repository_id")),
9238+
strings.TrimSpace(chi.URLParam(r, "change_request_id")),
9239+
); err != nil {
9240+
logging.Warnf("post_individual_signed - best-effort GitHub change request refresh failed: %v", err)
9241+
}
9242+
} else {
9243+
logging.Warnf("post_individual_signed - signed signature did not become visible in time: %s", signatureID)
9244+
}
9245+
}
9246+
91929247
copyV4ResponseHeaders(w, hdr)
91939248
w.WriteHeader(http.StatusOK)
91949249
if len(respBody) == 0 {

tests/functional/cypress/e2e/v2/events.cy.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright The Linux Foundation and each contributor to LFX.
22
// SPDX-License-Identifier: MIT
33

4-
import { validate_200_Status, validate_expected_status, getAPIBaseURL, getTokenForV2, getXACLHeader } from '../../support/commands';
4+
import { validate_200_Status, validate_expected_status, getAPIBaseURL, getTokenForV2 } from '../../support/commands';
55

66
describe('To Validate & test Events APIs via API call (V2)', function () {
77
const claEndpoint = getAPIBaseURL('v2');
@@ -57,14 +57,17 @@ describe('To Validate & test Events APIs via API call (V2)', function () {
5757
});
5858

5959
it('POST /clear-cache - Clear cache (Requires authentication)', function () {
60+
const envToken = Cypress.env('TOKEN');
61+
const tokenForClearCache = envToken && envToken !== '-' ? envToken : bearerToken;
62+
6063
cy.request({
6164
method: 'POST',
6265
url: `${claEndpoint}clear-cache`,
6366
timeout: timeout,
6467
failOnStatusCode: allowFail,
6568
headers: {
66-
...getXACLHeader(),
67-
Authorization: `Bearer ${bearerToken}`,
69+
'Content-Type': 'application/json',
70+
Authorization: `Bearer ${tokenForClearCache}`,
6871
},
6972
}).then((response) => {
7073
return cy.logJson('POST /clear-cache response', response).then(() => {

0 commit comments

Comments
 (0)