Skip to content

fix: normalize DocuSeal completed_at queue contract - #78

Merged
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix
Mar 2, 2026
Merged

fix: normalize DocuSeal completed_at queue contract#78
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix

Conversation

@michaelmwu

@michaelmwumichaelmwu commented Mar 2, 2026

Copy link
Copy Markdown
Member

Description

Normalize DocuSeal webhook agreement timestamps into an explicit UTC queue contract (YYYY-MM-DD HH:mm:ss) before enqueuing jobs and preserve the same contract through worker processing to prevent CRM validation failures.
The webhook handler now normalizes completed_at from raw ISO input (including offsets) into UTC before queueing process_docuseal_agreement_job.
process_docuseal_agreement_job and DocusealAgreementProcessor are documented to treat completed_at as a UTC string for JSON-serializable queue payloads.
I added a worker-scoped docs file (apps/worker/README.md) and pointed the root README webhook list to this contract.
Regression coverage was added for webhook enqueue contract normalization and processor-side datetime formatting plus invalid timestamp handling.

Related Issue

None.

How Has This Been Tested?

Unit tests for backend/api.py and docuseal_processor.py were updated to assert UTC string conversion and persisted argument format.

Summary by CodeRabbit

  • Documentation

    • Added webhook endpoint documentation for DocuSeal contract processing and other service integrations.
  • Bug Fixes

    • Fixed timestamp handling in the DocuSeal webhook to consistently normalize completion times to UTC format (YYYY-MM-DD HH:MM:SS) for reliable CRM synchronization.
    • Added validation to gracefully handle invalid timestamp inputs with descriptive error responses.

@coderabbitai

coderabbitaiBot commented Mar 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Added webhook endpoint documentation and implemented UTC timestamp normalization for DocuSeal contract completion timestamps across the API layer, worker processor, and supporting tests. Includes validation logic to handle invalid timestamp inputs gracefully.

Changes

Cohort / File(s)Summary
Documentation
README.md, apps/worker/README.md
Added webhook endpoint documentation including POST /webhooks/docuseal, /webhooks/{source}, /webhooks/espocrm, and /webhooks/espocrm/people-sync with expected input contract details for completed_at UTC format.
Timestamp Normalization Logic
apps/worker/src/five08/backend/api.py, apps/worker/src/five08/worker/crm/docuseal_processor.py, apps/worker/src/five08/worker/jobs.py
Introduced UTC coercion and normalization helper functions for completed_at timestamps (format: YYYY-MM-DD HH:MM:SS), validation in processor with structured error responses, and public format constant definition.
Unit Tests
tests/unit/test_backend_api.py, tests/unit/test_docuseal_processor.py
Added test coverage for UTC timestamp normalization, validation of enqueue_job argument ordering with normalized timestamps, invalid completed_at handling, and timestamp format conversion from offset to UTC strings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A timestamp walks through the UTC door,
Normalized, formatted, no timezone more,
From webhook to processor, validation in sight,
DocuSeal agreements signed, formatted just right! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix: normalize DocuSeal completed_at queue contract' accurately summarizes the main change: normalizing timestamp handling for DocuSeal webhooks to enforce a consistent UTC string format across the queue contract.
Docstring Coverage✅ PassedDocstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch michaelmwu/crm-member-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/worker/src/five08/worker/jobs.py (1)

16-17: Use one shared source for the DocuSeal datetime format across modules.

Line 16 defines the contract format, but the same literal is still hardcoded in apps/worker/src/five08/backend/api.py (Line 133) and apps/worker/src/five08/worker/crm/docuseal_processor.py (Line 29). Centralizing this in a neutral shared module would reduce contract drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/jobs.py` around lines 16 - 17, Replace the
duplicated DocuSeal datetime literal with a single shared constant: move
DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral shared module (e.g., a new
constant in five08 package like docuseal_constants or constants) and update all
call sites to import that constant instead of hardcoding the string;
specifically, replace literal occurrences in backend/api.py (where the format is
used around the DocuSeal handling) and worker/crm/docuseal_processor.py to
import DOCUSEAL_COMPLETED_AT_UTC_FORMAT, remove the duplicate literals, and run
tests to ensure no import errors.
apps/worker/src/five08/worker/crm/docuseal_processor.py (1)

84-100: Validate completed_at before CRM lookup to fail fast.

Right now Line 85 runs after the CRM search request, so malformed timestamps still incur external I/O and can be masked by earlier contact_not_found paths. Moving normalization ahead of the first API call would enforce the worker boundary contract more consistently.

♻️ Suggested refactor sketch
 def process_agreement(...):
masked_email = mask_email(email)
+ try:+ crm_completed_at = self._normalize_completed_at(completed_at)+ except ValueError as exc:+ logger.error(+ "CRM update failed for invalid datetime=%s: %s",+ completed_at,+ exc,+ )+ return {+ "success": False,+ "masked_email": masked_email,+ "submission_id": submission_id,+ "error": f"invalid_completed_at: {exc}",+ }+
try:
result = self.api.request("GET", "Contact", {...})
except EspoAPIError as exc:
...
-- try:- crm_completed_at = self._normalize_completed_at(completed_at)- except ValueError as exc:- ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py` around lines 84 -
100, Move the completed_at normalization so it runs before any CRM lookup: call
self._normalize_completed_at(completed_at) at the start of the processing flow
(before the CRM search/lookup call that uses contact_id), catch ValueError and
return the same error payload (including masked_email, submission_id, contact_id
and error=f"invalid_completed_at: {exc}") and log via logger.error as already
done; update the method containing the CRM search to remove the current
try/except around _normalize_completed_at that occurs after the CRM call so
malformed timestamps fail fast and avoid performing external I/O.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py`:
- Around line 84-100: Move the completed_at normalization so it runs before any
CRM lookup: call self._normalize_completed_at(completed_at) at the start of the
processing flow (before the CRM search/lookup call that uses contact_id), catch
ValueError and return the same error payload (including masked_email,
submission_id, contact_id and error=f"invalid_completed_at: {exc}") and log via
logger.error as already done; update the method containing the CRM search to
remove the current try/except around _normalize_completed_at that occurs after
the CRM call so malformed timestamps fail fast and avoid performing external
I/O.
In `@apps/worker/src/five08/worker/jobs.py`:
- Around line 16-17: Replace the duplicated DocuSeal datetime literal with a
single shared constant: move DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral
shared module (e.g., a new constant in five08 package like docuseal_constants or
constants) and update all call sites to import that constant instead of
hardcoding the string; specifically, replace literal occurrences in
backend/api.py (where the format is used around the DocuSeal handling) and
worker/crm/docuseal_processor.py to import DOCUSEAL_COMPLETED_AT_UTC_FORMAT,
remove the duplicate literals, and run tests to ensure no import errors.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48e6f50 and 7d2c938.

📒 Files selected for processing (7)
  • README.md
  • apps/worker/README.md
  • apps/worker/src/five08/backend/api.py
  • apps/worker/src/five08/worker/crm/docuseal_processor.py
  • apps/worker/src/five08/worker/jobs.py
  • tests/unit/test_backend_api.py
  • tests/unit/test_docuseal_processor.py

@michaelmwu
michaelmwu merged commit 950f144 into mainMar 2, 2026
5 checks passed
@michaelmwu
michaelmwu deleted the michaelmwu/crm-member-fix branch March 2, 2026 10:39
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@michaelmwu
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix: normalize DocuSeal completed_at queue contract by michaelmwu · Pull Request #78 · 508-dev/508-workflows · GitHub
Skip to content

fix: normalize DocuSeal completed_at queue contract - #78

Merged
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix
Mar 2, 2026
Merged

fix: normalize DocuSeal completed_at queue contract#78
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix

Conversation

@michaelmwu

@michaelmwumichaelmwu commented Mar 2, 2026

Copy link
Copy Markdown
Member

Description

Normalize DocuSeal webhook agreement timestamps into an explicit UTC queue contract (YYYY-MM-DD HH:mm:ss) before enqueuing jobs and preserve the same contract through worker processing to prevent CRM validation failures.
The webhook handler now normalizes completed_at from raw ISO input (including offsets) into UTC before queueing process_docuseal_agreement_job.
process_docuseal_agreement_job and DocusealAgreementProcessor are documented to treat completed_at as a UTC string for JSON-serializable queue payloads.
I added a worker-scoped docs file (apps/worker/README.md) and pointed the root README webhook list to this contract.
Regression coverage was added for webhook enqueue contract normalization and processor-side datetime formatting plus invalid timestamp handling.

Related Issue

None.

How Has This Been Tested?

Unit tests for backend/api.py and docuseal_processor.py were updated to assert UTC string conversion and persisted argument format.

Summary by CodeRabbit

  • Documentation

    • Added webhook endpoint documentation for DocuSeal contract processing and other service integrations.
  • Bug Fixes

    • Fixed timestamp handling in the DocuSeal webhook to consistently normalize completion times to UTC format (YYYY-MM-DD HH:MM:SS) for reliable CRM synchronization.
    • Added validation to gracefully handle invalid timestamp inputs with descriptive error responses.

@coderabbitai

coderabbitaiBot commented Mar 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Added webhook endpoint documentation and implemented UTC timestamp normalization for DocuSeal contract completion timestamps across the API layer, worker processor, and supporting tests. Includes validation logic to handle invalid timestamp inputs gracefully.

Changes

Cohort / File(s)Summary
Documentation
README.md, apps/worker/README.md
Added webhook endpoint documentation including POST /webhooks/docuseal, /webhooks/{source}, /webhooks/espocrm, and /webhooks/espocrm/people-sync with expected input contract details for completed_at UTC format.
Timestamp Normalization Logic
apps/worker/src/five08/backend/api.py, apps/worker/src/five08/worker/crm/docuseal_processor.py, apps/worker/src/five08/worker/jobs.py
Introduced UTC coercion and normalization helper functions for completed_at timestamps (format: YYYY-MM-DD HH:MM:SS), validation in processor with structured error responses, and public format constant definition.
Unit Tests
tests/unit/test_backend_api.py, tests/unit/test_docuseal_processor.py
Added test coverage for UTC timestamp normalization, validation of enqueue_job argument ordering with normalized timestamps, invalid completed_at handling, and timestamp format conversion from offset to UTC strings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A timestamp walks through the UTC door,
Normalized, formatted, no timezone more,
From webhook to processor, validation in sight,
DocuSeal agreements signed, formatted just right! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix: normalize DocuSeal completed_at queue contract' accurately summarizes the main change: normalizing timestamp handling for DocuSeal webhooks to enforce a consistent UTC string format across the queue contract.
Docstring Coverage✅ PassedDocstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch michaelmwu/crm-member-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/worker/src/five08/worker/jobs.py (1)

16-17: Use one shared source for the DocuSeal datetime format across modules.

Line 16 defines the contract format, but the same literal is still hardcoded in apps/worker/src/five08/backend/api.py (Line 133) and apps/worker/src/five08/worker/crm/docuseal_processor.py (Line 29). Centralizing this in a neutral shared module would reduce contract drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/jobs.py` around lines 16 - 17, Replace the
duplicated DocuSeal datetime literal with a single shared constant: move
DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral shared module (e.g., a new
constant in five08 package like docuseal_constants or constants) and update all
call sites to import that constant instead of hardcoding the string;
specifically, replace literal occurrences in backend/api.py (where the format is
used around the DocuSeal handling) and worker/crm/docuseal_processor.py to
import DOCUSEAL_COMPLETED_AT_UTC_FORMAT, remove the duplicate literals, and run
tests to ensure no import errors.
apps/worker/src/five08/worker/crm/docuseal_processor.py (1)

84-100: Validate completed_at before CRM lookup to fail fast.

Right now Line 85 runs after the CRM search request, so malformed timestamps still incur external I/O and can be masked by earlier contact_not_found paths. Moving normalization ahead of the first API call would enforce the worker boundary contract more consistently.

♻️ Suggested refactor sketch
 def process_agreement(...):
masked_email = mask_email(email)
+ try:+ crm_completed_at = self._normalize_completed_at(completed_at)+ except ValueError as exc:+ logger.error(+ "CRM update failed for invalid datetime=%s: %s",+ completed_at,+ exc,+ )+ return {+ "success": False,+ "masked_email": masked_email,+ "submission_id": submission_id,+ "error": f"invalid_completed_at: {exc}",+ }+
try:
result = self.api.request("GET", "Contact", {...})
except EspoAPIError as exc:
...
-- try:- crm_completed_at = self._normalize_completed_at(completed_at)- except ValueError as exc:- ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py` around lines 84 -
100, Move the completed_at normalization so it runs before any CRM lookup: call
self._normalize_completed_at(completed_at) at the start of the processing flow
(before the CRM search/lookup call that uses contact_id), catch ValueError and
return the same error payload (including masked_email, submission_id, contact_id
and error=f"invalid_completed_at: {exc}") and log via logger.error as already
done; update the method containing the CRM search to remove the current
try/except around _normalize_completed_at that occurs after the CRM call so
malformed timestamps fail fast and avoid performing external I/O.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py`:
- Around line 84-100: Move the completed_at normalization so it runs before any
CRM lookup: call self._normalize_completed_at(completed_at) at the start of the
processing flow (before the CRM search/lookup call that uses contact_id), catch
ValueError and return the same error payload (including masked_email,
submission_id, contact_id and error=f"invalid_completed_at: {exc}") and log via
logger.error as already done; update the method containing the CRM search to
remove the current try/except around _normalize_completed_at that occurs after
the CRM call so malformed timestamps fail fast and avoid performing external
I/O.
In `@apps/worker/src/five08/worker/jobs.py`:
- Around line 16-17: Replace the duplicated DocuSeal datetime literal with a
single shared constant: move DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral
shared module (e.g., a new constant in five08 package like docuseal_constants or
constants) and update all call sites to import that constant instead of
hardcoding the string; specifically, replace literal occurrences in
backend/api.py (where the format is used around the DocuSeal handling) and
worker/crm/docuseal_processor.py to import DOCUSEAL_COMPLETED_AT_UTC_FORMAT,
remove the duplicate literals, and run tests to ensure no import errors.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48e6f50 and 7d2c938.

📒 Files selected for processing (7)
  • README.md
  • apps/worker/README.md
  • apps/worker/src/five08/backend/api.py
  • apps/worker/src/five08/worker/crm/docuseal_processor.py
  • apps/worker/src/five08/worker/jobs.py
  • tests/unit/test_backend_api.py
  • tests/unit/test_docuseal_processor.py

@michaelmwu
michaelmwu merged commit 950f144 into mainMar 2, 2026
5 checks passed
@michaelmwu
michaelmwu deleted the michaelmwu/crm-member-fix branch March 2, 2026 10:39
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@michaelmwu
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: normalize DocuSeal completed_at queue contract by michaelmwu · Pull Request #78 · 508-dev/508-workflows · GitHub
Skip to content

fix: normalize DocuSeal completed_at queue contract - #78

Merged
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix
Mar 2, 2026
Merged

fix: normalize DocuSeal completed_at queue contract#78
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix

Conversation

@michaelmwu

@michaelmwumichaelmwu commented Mar 2, 2026

Copy link
Copy Markdown
Member

Description

Normalize DocuSeal webhook agreement timestamps into an explicit UTC queue contract (YYYY-MM-DD HH:mm:ss) before enqueuing jobs and preserve the same contract through worker processing to prevent CRM validation failures.
The webhook handler now normalizes completed_at from raw ISO input (including offsets) into UTC before queueing process_docuseal_agreement_job.
process_docuseal_agreement_job and DocusealAgreementProcessor are documented to treat completed_at as a UTC string for JSON-serializable queue payloads.
I added a worker-scoped docs file (apps/worker/README.md) and pointed the root README webhook list to this contract.
Regression coverage was added for webhook enqueue contract normalization and processor-side datetime formatting plus invalid timestamp handling.

Related Issue

None.

How Has This Been Tested?

Unit tests for backend/api.py and docuseal_processor.py were updated to assert UTC string conversion and persisted argument format.

Summary by CodeRabbit

  • Documentation

    • Added webhook endpoint documentation for DocuSeal contract processing and other service integrations.
  • Bug Fixes

    • Fixed timestamp handling in the DocuSeal webhook to consistently normalize completion times to UTC format (YYYY-MM-DD HH:MM:SS) for reliable CRM synchronization.
    • Added validation to gracefully handle invalid timestamp inputs with descriptive error responses.

@coderabbitai

coderabbitaiBot commented Mar 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Added webhook endpoint documentation and implemented UTC timestamp normalization for DocuSeal contract completion timestamps across the API layer, worker processor, and supporting tests. Includes validation logic to handle invalid timestamp inputs gracefully.

Changes

Cohort / File(s)Summary
Documentation
README.md, apps/worker/README.md
Added webhook endpoint documentation including POST /webhooks/docuseal, /webhooks/{source}, /webhooks/espocrm, and /webhooks/espocrm/people-sync with expected input contract details for completed_at UTC format.
Timestamp Normalization Logic
apps/worker/src/five08/backend/api.py, apps/worker/src/five08/worker/crm/docuseal_processor.py, apps/worker/src/five08/worker/jobs.py
Introduced UTC coercion and normalization helper functions for completed_at timestamps (format: YYYY-MM-DD HH:MM:SS), validation in processor with structured error responses, and public format constant definition.
Unit Tests
tests/unit/test_backend_api.py, tests/unit/test_docuseal_processor.py
Added test coverage for UTC timestamp normalization, validation of enqueue_job argument ordering with normalized timestamps, invalid completed_at handling, and timestamp format conversion from offset to UTC strings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A timestamp walks through the UTC door,
Normalized, formatted, no timezone more,
From webhook to processor, validation in sight,
DocuSeal agreements signed, formatted just right! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix: normalize DocuSeal completed_at queue contract' accurately summarizes the main change: normalizing timestamp handling for DocuSeal webhooks to enforce a consistent UTC string format across the queue contract.
Docstring Coverage✅ PassedDocstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch michaelmwu/crm-member-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/worker/src/five08/worker/jobs.py (1)

16-17: Use one shared source for the DocuSeal datetime format across modules.

Line 16 defines the contract format, but the same literal is still hardcoded in apps/worker/src/five08/backend/api.py (Line 133) and apps/worker/src/five08/worker/crm/docuseal_processor.py (Line 29). Centralizing this in a neutral shared module would reduce contract drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/jobs.py` around lines 16 - 17, Replace the
duplicated DocuSeal datetime literal with a single shared constant: move
DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral shared module (e.g., a new
constant in five08 package like docuseal_constants or constants) and update all
call sites to import that constant instead of hardcoding the string;
specifically, replace literal occurrences in backend/api.py (where the format is
used around the DocuSeal handling) and worker/crm/docuseal_processor.py to
import DOCUSEAL_COMPLETED_AT_UTC_FORMAT, remove the duplicate literals, and run
tests to ensure no import errors.
apps/worker/src/five08/worker/crm/docuseal_processor.py (1)

84-100: Validate completed_at before CRM lookup to fail fast.

Right now Line 85 runs after the CRM search request, so malformed timestamps still incur external I/O and can be masked by earlier contact_not_found paths. Moving normalization ahead of the first API call would enforce the worker boundary contract more consistently.

♻️ Suggested refactor sketch
 def process_agreement(...):
masked_email = mask_email(email)
+ try:+ crm_completed_at = self._normalize_completed_at(completed_at)+ except ValueError as exc:+ logger.error(+ "CRM update failed for invalid datetime=%s: %s",+ completed_at,+ exc,+ )+ return {+ "success": False,+ "masked_email": masked_email,+ "submission_id": submission_id,+ "error": f"invalid_completed_at: {exc}",+ }+
try:
result = self.api.request("GET", "Contact", {...})
except EspoAPIError as exc:
...
-- try:- crm_completed_at = self._normalize_completed_at(completed_at)- except ValueError as exc:- ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py` around lines 84 -
100, Move the completed_at normalization so it runs before any CRM lookup: call
self._normalize_completed_at(completed_at) at the start of the processing flow
(before the CRM search/lookup call that uses contact_id), catch ValueError and
return the same error payload (including masked_email, submission_id, contact_id
and error=f"invalid_completed_at: {exc}") and log via logger.error as already
done; update the method containing the CRM search to remove the current
try/except around _normalize_completed_at that occurs after the CRM call so
malformed timestamps fail fast and avoid performing external I/O.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py`:
- Around line 84-100: Move the completed_at normalization so it runs before any
CRM lookup: call self._normalize_completed_at(completed_at) at the start of the
processing flow (before the CRM search/lookup call that uses contact_id), catch
ValueError and return the same error payload (including masked_email,
submission_id, contact_id and error=f"invalid_completed_at: {exc}") and log via
logger.error as already done; update the method containing the CRM search to
remove the current try/except around _normalize_completed_at that occurs after
the CRM call so malformed timestamps fail fast and avoid performing external
I/O.
In `@apps/worker/src/five08/worker/jobs.py`:
- Around line 16-17: Replace the duplicated DocuSeal datetime literal with a
single shared constant: move DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral
shared module (e.g., a new constant in five08 package like docuseal_constants or
constants) and update all call sites to import that constant instead of
hardcoding the string; specifically, replace literal occurrences in
backend/api.py (where the format is used around the DocuSeal handling) and
worker/crm/docuseal_processor.py to import DOCUSEAL_COMPLETED_AT_UTC_FORMAT,
remove the duplicate literals, and run tests to ensure no import errors.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48e6f50 and 7d2c938.

📒 Files selected for processing (7)
  • README.md
  • apps/worker/README.md
  • apps/worker/src/five08/backend/api.py
  • apps/worker/src/five08/worker/crm/docuseal_processor.py
  • apps/worker/src/five08/worker/jobs.py
  • tests/unit/test_backend_api.py
  • tests/unit/test_docuseal_processor.py

@michaelmwu
michaelmwu merged commit 950f144 into mainMar 2, 2026
5 checks passed
@michaelmwu
michaelmwu deleted the michaelmwu/crm-member-fix branch March 2, 2026 10:39
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@michaelmwu
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: normalize DocuSeal completed_at queue contract by michaelmwu · Pull Request #78 · 508-dev/508-workflows · GitHub
Skip to content

fix: normalize DocuSeal completed_at queue contract - #78

Merged
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix
Mar 2, 2026
Merged

fix: normalize DocuSeal completed_at queue contract#78
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix

Conversation

@michaelmwu

@michaelmwumichaelmwu commented Mar 2, 2026

Copy link
Copy Markdown
Member

Description

Normalize DocuSeal webhook agreement timestamps into an explicit UTC queue contract (YYYY-MM-DD HH:mm:ss) before enqueuing jobs and preserve the same contract through worker processing to prevent CRM validation failures.
The webhook handler now normalizes completed_at from raw ISO input (including offsets) into UTC before queueing process_docuseal_agreement_job.
process_docuseal_agreement_job and DocusealAgreementProcessor are documented to treat completed_at as a UTC string for JSON-serializable queue payloads.
I added a worker-scoped docs file (apps/worker/README.md) and pointed the root README webhook list to this contract.
Regression coverage was added for webhook enqueue contract normalization and processor-side datetime formatting plus invalid timestamp handling.

Related Issue

None.

How Has This Been Tested?

Unit tests for backend/api.py and docuseal_processor.py were updated to assert UTC string conversion and persisted argument format.

Summary by CodeRabbit

  • Documentation

    • Added webhook endpoint documentation for DocuSeal contract processing and other service integrations.
  • Bug Fixes

    • Fixed timestamp handling in the DocuSeal webhook to consistently normalize completion times to UTC format (YYYY-MM-DD HH:MM:SS) for reliable CRM synchronization.
    • Added validation to gracefully handle invalid timestamp inputs with descriptive error responses.

@coderabbitai

coderabbitaiBot commented Mar 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Added webhook endpoint documentation and implemented UTC timestamp normalization for DocuSeal contract completion timestamps across the API layer, worker processor, and supporting tests. Includes validation logic to handle invalid timestamp inputs gracefully.

Changes

Cohort / File(s)Summary
Documentation
README.md, apps/worker/README.md
Added webhook endpoint documentation including POST /webhooks/docuseal, /webhooks/{source}, /webhooks/espocrm, and /webhooks/espocrm/people-sync with expected input contract details for completed_at UTC format.
Timestamp Normalization Logic
apps/worker/src/five08/backend/api.py, apps/worker/src/five08/worker/crm/docuseal_processor.py, apps/worker/src/five08/worker/jobs.py
Introduced UTC coercion and normalization helper functions for completed_at timestamps (format: YYYY-MM-DD HH:MM:SS), validation in processor with structured error responses, and public format constant definition.
Unit Tests
tests/unit/test_backend_api.py, tests/unit/test_docuseal_processor.py
Added test coverage for UTC timestamp normalization, validation of enqueue_job argument ordering with normalized timestamps, invalid completed_at handling, and timestamp format conversion from offset to UTC strings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A timestamp walks through the UTC door,
Normalized, formatted, no timezone more,
From webhook to processor, validation in sight,
DocuSeal agreements signed, formatted just right! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix: normalize DocuSeal completed_at queue contract' accurately summarizes the main change: normalizing timestamp handling for DocuSeal webhooks to enforce a consistent UTC string format across the queue contract.
Docstring Coverage✅ PassedDocstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch michaelmwu/crm-member-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/worker/src/five08/worker/jobs.py (1)

16-17: Use one shared source for the DocuSeal datetime format across modules.

Line 16 defines the contract format, but the same literal is still hardcoded in apps/worker/src/five08/backend/api.py (Line 133) and apps/worker/src/five08/worker/crm/docuseal_processor.py (Line 29). Centralizing this in a neutral shared module would reduce contract drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/jobs.py` around lines 16 - 17, Replace the
duplicated DocuSeal datetime literal with a single shared constant: move
DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral shared module (e.g., a new
constant in five08 package like docuseal_constants or constants) and update all
call sites to import that constant instead of hardcoding the string;
specifically, replace literal occurrences in backend/api.py (where the format is
used around the DocuSeal handling) and worker/crm/docuseal_processor.py to
import DOCUSEAL_COMPLETED_AT_UTC_FORMAT, remove the duplicate literals, and run
tests to ensure no import errors.
apps/worker/src/five08/worker/crm/docuseal_processor.py (1)

84-100: Validate completed_at before CRM lookup to fail fast.

Right now Line 85 runs after the CRM search request, so malformed timestamps still incur external I/O and can be masked by earlier contact_not_found paths. Moving normalization ahead of the first API call would enforce the worker boundary contract more consistently.

♻️ Suggested refactor sketch
 def process_agreement(...):
masked_email = mask_email(email)
+ try:+ crm_completed_at = self._normalize_completed_at(completed_at)+ except ValueError as exc:+ logger.error(+ "CRM update failed for invalid datetime=%s: %s",+ completed_at,+ exc,+ )+ return {+ "success": False,+ "masked_email": masked_email,+ "submission_id": submission_id,+ "error": f"invalid_completed_at: {exc}",+ }+
try:
result = self.api.request("GET", "Contact", {...})
except EspoAPIError as exc:
...
-- try:- crm_completed_at = self._normalize_completed_at(completed_at)- except ValueError as exc:- ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py` around lines 84 -
100, Move the completed_at normalization so it runs before any CRM lookup: call
self._normalize_completed_at(completed_at) at the start of the processing flow
(before the CRM search/lookup call that uses contact_id), catch ValueError and
return the same error payload (including masked_email, submission_id, contact_id
and error=f"invalid_completed_at: {exc}") and log via logger.error as already
done; update the method containing the CRM search to remove the current
try/except around _normalize_completed_at that occurs after the CRM call so
malformed timestamps fail fast and avoid performing external I/O.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py`:
- Around line 84-100: Move the completed_at normalization so it runs before any
CRM lookup: call self._normalize_completed_at(completed_at) at the start of the
processing flow (before the CRM search/lookup call that uses contact_id), catch
ValueError and return the same error payload (including masked_email,
submission_id, contact_id and error=f"invalid_completed_at: {exc}") and log via
logger.error as already done; update the method containing the CRM search to
remove the current try/except around _normalize_completed_at that occurs after
the CRM call so malformed timestamps fail fast and avoid performing external
I/O.
In `@apps/worker/src/five08/worker/jobs.py`:
- Around line 16-17: Replace the duplicated DocuSeal datetime literal with a
single shared constant: move DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral
shared module (e.g., a new constant in five08 package like docuseal_constants or
constants) and update all call sites to import that constant instead of
hardcoding the string; specifically, replace literal occurrences in
backend/api.py (where the format is used around the DocuSeal handling) and
worker/crm/docuseal_processor.py to import DOCUSEAL_COMPLETED_AT_UTC_FORMAT,
remove the duplicate literals, and run tests to ensure no import errors.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48e6f50 and 7d2c938.

📒 Files selected for processing (7)
  • README.md
  • apps/worker/README.md
  • apps/worker/src/five08/backend/api.py
  • apps/worker/src/five08/worker/crm/docuseal_processor.py
  • apps/worker/src/five08/worker/jobs.py
  • tests/unit/test_backend_api.py
  • tests/unit/test_docuseal_processor.py

@michaelmwu
michaelmwu merged commit 950f144 into mainMar 2, 2026
5 checks passed
@michaelmwu
michaelmwu deleted the michaelmwu/crm-member-fix branch March 2, 2026 10:39
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@michaelmwu
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix: normalize DocuSeal completed_at queue contract by michaelmwu · Pull Request #78 · 508-dev/508-workflows · GitHub
Skip to content

fix: normalize DocuSeal completed_at queue contract - #78

Merged
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix
Mar 2, 2026
Merged

fix: normalize DocuSeal completed_at queue contract#78
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix

Conversation

@michaelmwu

@michaelmwumichaelmwu commented Mar 2, 2026

Copy link
Copy Markdown
Member

Description

Normalize DocuSeal webhook agreement timestamps into an explicit UTC queue contract (YYYY-MM-DD HH:mm:ss) before enqueuing jobs and preserve the same contract through worker processing to prevent CRM validation failures.
The webhook handler now normalizes completed_at from raw ISO input (including offsets) into UTC before queueing process_docuseal_agreement_job.
process_docuseal_agreement_job and DocusealAgreementProcessor are documented to treat completed_at as a UTC string for JSON-serializable queue payloads.
I added a worker-scoped docs file (apps/worker/README.md) and pointed the root README webhook list to this contract.
Regression coverage was added for webhook enqueue contract normalization and processor-side datetime formatting plus invalid timestamp handling.

Related Issue

None.

How Has This Been Tested?

Unit tests for backend/api.py and docuseal_processor.py were updated to assert UTC string conversion and persisted argument format.

Summary by CodeRabbit

  • Documentation

    • Added webhook endpoint documentation for DocuSeal contract processing and other service integrations.
  • Bug Fixes

    • Fixed timestamp handling in the DocuSeal webhook to consistently normalize completion times to UTC format (YYYY-MM-DD HH:MM:SS) for reliable CRM synchronization.
    • Added validation to gracefully handle invalid timestamp inputs with descriptive error responses.

@coderabbitai

coderabbitaiBot commented Mar 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Added webhook endpoint documentation and implemented UTC timestamp normalization for DocuSeal contract completion timestamps across the API layer, worker processor, and supporting tests. Includes validation logic to handle invalid timestamp inputs gracefully.

Changes

Cohort / File(s)Summary
Documentation
README.md, apps/worker/README.md
Added webhook endpoint documentation including POST /webhooks/docuseal, /webhooks/{source}, /webhooks/espocrm, and /webhooks/espocrm/people-sync with expected input contract details for completed_at UTC format.
Timestamp Normalization Logic
apps/worker/src/five08/backend/api.py, apps/worker/src/five08/worker/crm/docuseal_processor.py, apps/worker/src/five08/worker/jobs.py
Introduced UTC coercion and normalization helper functions for completed_at timestamps (format: YYYY-MM-DD HH:MM:SS), validation in processor with structured error responses, and public format constant definition.
Unit Tests
tests/unit/test_backend_api.py, tests/unit/test_docuseal_processor.py
Added test coverage for UTC timestamp normalization, validation of enqueue_job argument ordering with normalized timestamps, invalid completed_at handling, and timestamp format conversion from offset to UTC strings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A timestamp walks through the UTC door,
Normalized, formatted, no timezone more,
From webhook to processor, validation in sight,
DocuSeal agreements signed, formatted just right! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix: normalize DocuSeal completed_at queue contract' accurately summarizes the main change: normalizing timestamp handling for DocuSeal webhooks to enforce a consistent UTC string format across the queue contract.
Docstring Coverage✅ PassedDocstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch michaelmwu/crm-member-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/worker/src/five08/worker/jobs.py (1)

16-17: Use one shared source for the DocuSeal datetime format across modules.

Line 16 defines the contract format, but the same literal is still hardcoded in apps/worker/src/five08/backend/api.py (Line 133) and apps/worker/src/five08/worker/crm/docuseal_processor.py (Line 29). Centralizing this in a neutral shared module would reduce contract drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/jobs.py` around lines 16 - 17, Replace the
duplicated DocuSeal datetime literal with a single shared constant: move
DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral shared module (e.g., a new
constant in five08 package like docuseal_constants or constants) and update all
call sites to import that constant instead of hardcoding the string;
specifically, replace literal occurrences in backend/api.py (where the format is
used around the DocuSeal handling) and worker/crm/docuseal_processor.py to
import DOCUSEAL_COMPLETED_AT_UTC_FORMAT, remove the duplicate literals, and run
tests to ensure no import errors.
apps/worker/src/five08/worker/crm/docuseal_processor.py (1)

84-100: Validate completed_at before CRM lookup to fail fast.

Right now Line 85 runs after the CRM search request, so malformed timestamps still incur external I/O and can be masked by earlier contact_not_found paths. Moving normalization ahead of the first API call would enforce the worker boundary contract more consistently.

♻️ Suggested refactor sketch
 def process_agreement(...):
masked_email = mask_email(email)
+ try:+ crm_completed_at = self._normalize_completed_at(completed_at)+ except ValueError as exc:+ logger.error(+ "CRM update failed for invalid datetime=%s: %s",+ completed_at,+ exc,+ )+ return {+ "success": False,+ "masked_email": masked_email,+ "submission_id": submission_id,+ "error": f"invalid_completed_at: {exc}",+ }+
try:
result = self.api.request("GET", "Contact", {...})
except EspoAPIError as exc:
...
-- try:- crm_completed_at = self._normalize_completed_at(completed_at)- except ValueError as exc:- ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py` around lines 84 -
100, Move the completed_at normalization so it runs before any CRM lookup: call
self._normalize_completed_at(completed_at) at the start of the processing flow
(before the CRM search/lookup call that uses contact_id), catch ValueError and
return the same error payload (including masked_email, submission_id, contact_id
and error=f"invalid_completed_at: {exc}") and log via logger.error as already
done; update the method containing the CRM search to remove the current
try/except around _normalize_completed_at that occurs after the CRM call so
malformed timestamps fail fast and avoid performing external I/O.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py`:
- Around line 84-100: Move the completed_at normalization so it runs before any
CRM lookup: call self._normalize_completed_at(completed_at) at the start of the
processing flow (before the CRM search/lookup call that uses contact_id), catch
ValueError and return the same error payload (including masked_email,
submission_id, contact_id and error=f"invalid_completed_at: {exc}") and log via
logger.error as already done; update the method containing the CRM search to
remove the current try/except around _normalize_completed_at that occurs after
the CRM call so malformed timestamps fail fast and avoid performing external
I/O.
In `@apps/worker/src/five08/worker/jobs.py`:
- Around line 16-17: Replace the duplicated DocuSeal datetime literal with a
single shared constant: move DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral
shared module (e.g., a new constant in five08 package like docuseal_constants or
constants) and update all call sites to import that constant instead of
hardcoding the string; specifically, replace literal occurrences in
backend/api.py (where the format is used around the DocuSeal handling) and
worker/crm/docuseal_processor.py to import DOCUSEAL_COMPLETED_AT_UTC_FORMAT,
remove the duplicate literals, and run tests to ensure no import errors.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48e6f50 and 7d2c938.

📒 Files selected for processing (7)
  • README.md
  • apps/worker/README.md
  • apps/worker/src/five08/backend/api.py
  • apps/worker/src/five08/worker/crm/docuseal_processor.py
  • apps/worker/src/five08/worker/jobs.py
  • tests/unit/test_backend_api.py
  • tests/unit/test_docuseal_processor.py

@michaelmwu
michaelmwu merged commit 950f144 into mainMar 2, 2026
5 checks passed
@michaelmwu
michaelmwu deleted the michaelmwu/crm-member-fix branch March 2, 2026 10:39
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@michaelmwu
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: normalize DocuSeal completed_at queue contract by michaelmwu · Pull Request #78 · 508-dev/508-workflows · GitHub
Skip to content

fix: normalize DocuSeal completed_at queue contract - #78

Merged
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix
Mar 2, 2026
Merged

fix: normalize DocuSeal completed_at queue contract#78
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix

Conversation

@michaelmwu

@michaelmwumichaelmwu commented Mar 2, 2026

Copy link
Copy Markdown
Member

Description

Normalize DocuSeal webhook agreement timestamps into an explicit UTC queue contract (YYYY-MM-DD HH:mm:ss) before enqueuing jobs and preserve the same contract through worker processing to prevent CRM validation failures.
The webhook handler now normalizes completed_at from raw ISO input (including offsets) into UTC before queueing process_docuseal_agreement_job.
process_docuseal_agreement_job and DocusealAgreementProcessor are documented to treat completed_at as a UTC string for JSON-serializable queue payloads.
I added a worker-scoped docs file (apps/worker/README.md) and pointed the root README webhook list to this contract.
Regression coverage was added for webhook enqueue contract normalization and processor-side datetime formatting plus invalid timestamp handling.

Related Issue

None.

How Has This Been Tested?

Unit tests for backend/api.py and docuseal_processor.py were updated to assert UTC string conversion and persisted argument format.

Summary by CodeRabbit

  • Documentation

    • Added webhook endpoint documentation for DocuSeal contract processing and other service integrations.
  • Bug Fixes

    • Fixed timestamp handling in the DocuSeal webhook to consistently normalize completion times to UTC format (YYYY-MM-DD HH:MM:SS) for reliable CRM synchronization.
    • Added validation to gracefully handle invalid timestamp inputs with descriptive error responses.

@coderabbitai

coderabbitaiBot commented Mar 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Added webhook endpoint documentation and implemented UTC timestamp normalization for DocuSeal contract completion timestamps across the API layer, worker processor, and supporting tests. Includes validation logic to handle invalid timestamp inputs gracefully.

Changes

Cohort / File(s)Summary
Documentation
README.md, apps/worker/README.md
Added webhook endpoint documentation including POST /webhooks/docuseal, /webhooks/{source}, /webhooks/espocrm, and /webhooks/espocrm/people-sync with expected input contract details for completed_at UTC format.
Timestamp Normalization Logic
apps/worker/src/five08/backend/api.py, apps/worker/src/five08/worker/crm/docuseal_processor.py, apps/worker/src/five08/worker/jobs.py
Introduced UTC coercion and normalization helper functions for completed_at timestamps (format: YYYY-MM-DD HH:MM:SS), validation in processor with structured error responses, and public format constant definition.
Unit Tests
tests/unit/test_backend_api.py, tests/unit/test_docuseal_processor.py
Added test coverage for UTC timestamp normalization, validation of enqueue_job argument ordering with normalized timestamps, invalid completed_at handling, and timestamp format conversion from offset to UTC strings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A timestamp walks through the UTC door,
Normalized, formatted, no timezone more,
From webhook to processor, validation in sight,
DocuSeal agreements signed, formatted just right! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix: normalize DocuSeal completed_at queue contract' accurately summarizes the main change: normalizing timestamp handling for DocuSeal webhooks to enforce a consistent UTC string format across the queue contract.
Docstring Coverage✅ PassedDocstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch michaelmwu/crm-member-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/worker/src/five08/worker/jobs.py (1)

16-17: Use one shared source for the DocuSeal datetime format across modules.

Line 16 defines the contract format, but the same literal is still hardcoded in apps/worker/src/five08/backend/api.py (Line 133) and apps/worker/src/five08/worker/crm/docuseal_processor.py (Line 29). Centralizing this in a neutral shared module would reduce contract drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/jobs.py` around lines 16 - 17, Replace the
duplicated DocuSeal datetime literal with a single shared constant: move
DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral shared module (e.g., a new
constant in five08 package like docuseal_constants or constants) and update all
call sites to import that constant instead of hardcoding the string;
specifically, replace literal occurrences in backend/api.py (where the format is
used around the DocuSeal handling) and worker/crm/docuseal_processor.py to
import DOCUSEAL_COMPLETED_AT_UTC_FORMAT, remove the duplicate literals, and run
tests to ensure no import errors.
apps/worker/src/five08/worker/crm/docuseal_processor.py (1)

84-100: Validate completed_at before CRM lookup to fail fast.

Right now Line 85 runs after the CRM search request, so malformed timestamps still incur external I/O and can be masked by earlier contact_not_found paths. Moving normalization ahead of the first API call would enforce the worker boundary contract more consistently.

♻️ Suggested refactor sketch
 def process_agreement(...):
masked_email = mask_email(email)
+ try:+ crm_completed_at = self._normalize_completed_at(completed_at)+ except ValueError as exc:+ logger.error(+ "CRM update failed for invalid datetime=%s: %s",+ completed_at,+ exc,+ )+ return {+ "success": False,+ "masked_email": masked_email,+ "submission_id": submission_id,+ "error": f"invalid_completed_at: {exc}",+ }+
try:
result = self.api.request("GET", "Contact", {...})
except EspoAPIError as exc:
...
-- try:- crm_completed_at = self._normalize_completed_at(completed_at)- except ValueError as exc:- ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py` around lines 84 -
100, Move the completed_at normalization so it runs before any CRM lookup: call
self._normalize_completed_at(completed_at) at the start of the processing flow
(before the CRM search/lookup call that uses contact_id), catch ValueError and
return the same error payload (including masked_email, submission_id, contact_id
and error=f"invalid_completed_at: {exc}") and log via logger.error as already
done; update the method containing the CRM search to remove the current
try/except around _normalize_completed_at that occurs after the CRM call so
malformed timestamps fail fast and avoid performing external I/O.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py`:
- Around line 84-100: Move the completed_at normalization so it runs before any
CRM lookup: call self._normalize_completed_at(completed_at) at the start of the
processing flow (before the CRM search/lookup call that uses contact_id), catch
ValueError and return the same error payload (including masked_email,
submission_id, contact_id and error=f"invalid_completed_at: {exc}") and log via
logger.error as already done; update the method containing the CRM search to
remove the current try/except around _normalize_completed_at that occurs after
the CRM call so malformed timestamps fail fast and avoid performing external
I/O.
In `@apps/worker/src/five08/worker/jobs.py`:
- Around line 16-17: Replace the duplicated DocuSeal datetime literal with a
single shared constant: move DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral
shared module (e.g., a new constant in five08 package like docuseal_constants or
constants) and update all call sites to import that constant instead of
hardcoding the string; specifically, replace literal occurrences in
backend/api.py (where the format is used around the DocuSeal handling) and
worker/crm/docuseal_processor.py to import DOCUSEAL_COMPLETED_AT_UTC_FORMAT,
remove the duplicate literals, and run tests to ensure no import errors.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48e6f50 and 7d2c938.

📒 Files selected for processing (7)
  • README.md
  • apps/worker/README.md
  • apps/worker/src/five08/backend/api.py
  • apps/worker/src/five08/worker/crm/docuseal_processor.py
  • apps/worker/src/five08/worker/jobs.py
  • tests/unit/test_backend_api.py
  • tests/unit/test_docuseal_processor.py

@michaelmwu
michaelmwu merged commit 950f144 into mainMar 2, 2026
5 checks passed
@michaelmwu
michaelmwu deleted the michaelmwu/crm-member-fix branch March 2, 2026 10:39
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@michaelmwu
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: normalize DocuSeal completed_at queue contract by michaelmwu · Pull Request #78 · 508-dev/508-workflows · GitHub
Skip to content

fix: normalize DocuSeal completed_at queue contract - #78

Merged
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix
Mar 2, 2026
Merged

fix: normalize DocuSeal completed_at queue contract#78
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix

Conversation

@michaelmwu

@michaelmwumichaelmwu commented Mar 2, 2026

Copy link
Copy Markdown
Member

Description

Normalize DocuSeal webhook agreement timestamps into an explicit UTC queue contract (YYYY-MM-DD HH:mm:ss) before enqueuing jobs and preserve the same contract through worker processing to prevent CRM validation failures.
The webhook handler now normalizes completed_at from raw ISO input (including offsets) into UTC before queueing process_docuseal_agreement_job.
process_docuseal_agreement_job and DocusealAgreementProcessor are documented to treat completed_at as a UTC string for JSON-serializable queue payloads.
I added a worker-scoped docs file (apps/worker/README.md) and pointed the root README webhook list to this contract.
Regression coverage was added for webhook enqueue contract normalization and processor-side datetime formatting plus invalid timestamp handling.

Related Issue

None.

How Has This Been Tested?

Unit tests for backend/api.py and docuseal_processor.py were updated to assert UTC string conversion and persisted argument format.

Summary by CodeRabbit

  • Documentation

    • Added webhook endpoint documentation for DocuSeal contract processing and other service integrations.
  • Bug Fixes

    • Fixed timestamp handling in the DocuSeal webhook to consistently normalize completion times to UTC format (YYYY-MM-DD HH:MM:SS) for reliable CRM synchronization.
    • Added validation to gracefully handle invalid timestamp inputs with descriptive error responses.

@coderabbitai

coderabbitaiBot commented Mar 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Added webhook endpoint documentation and implemented UTC timestamp normalization for DocuSeal contract completion timestamps across the API layer, worker processor, and supporting tests. Includes validation logic to handle invalid timestamp inputs gracefully.

Changes

Cohort / File(s)Summary
Documentation
README.md, apps/worker/README.md
Added webhook endpoint documentation including POST /webhooks/docuseal, /webhooks/{source}, /webhooks/espocrm, and /webhooks/espocrm/people-sync with expected input contract details for completed_at UTC format.
Timestamp Normalization Logic
apps/worker/src/five08/backend/api.py, apps/worker/src/five08/worker/crm/docuseal_processor.py, apps/worker/src/five08/worker/jobs.py
Introduced UTC coercion and normalization helper functions for completed_at timestamps (format: YYYY-MM-DD HH:MM:SS), validation in processor with structured error responses, and public format constant definition.
Unit Tests
tests/unit/test_backend_api.py, tests/unit/test_docuseal_processor.py
Added test coverage for UTC timestamp normalization, validation of enqueue_job argument ordering with normalized timestamps, invalid completed_at handling, and timestamp format conversion from offset to UTC strings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A timestamp walks through the UTC door,
Normalized, formatted, no timezone more,
From webhook to processor, validation in sight,
DocuSeal agreements signed, formatted just right! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix: normalize DocuSeal completed_at queue contract' accurately summarizes the main change: normalizing timestamp handling for DocuSeal webhooks to enforce a consistent UTC string format across the queue contract.
Docstring Coverage✅ PassedDocstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch michaelmwu/crm-member-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/worker/src/five08/worker/jobs.py (1)

16-17: Use one shared source for the DocuSeal datetime format across modules.

Line 16 defines the contract format, but the same literal is still hardcoded in apps/worker/src/five08/backend/api.py (Line 133) and apps/worker/src/five08/worker/crm/docuseal_processor.py (Line 29). Centralizing this in a neutral shared module would reduce contract drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/jobs.py` around lines 16 - 17, Replace the
duplicated DocuSeal datetime literal with a single shared constant: move
DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral shared module (e.g., a new
constant in five08 package like docuseal_constants or constants) and update all
call sites to import that constant instead of hardcoding the string;
specifically, replace literal occurrences in backend/api.py (where the format is
used around the DocuSeal handling) and worker/crm/docuseal_processor.py to
import DOCUSEAL_COMPLETED_AT_UTC_FORMAT, remove the duplicate literals, and run
tests to ensure no import errors.
apps/worker/src/five08/worker/crm/docuseal_processor.py (1)

84-100: Validate completed_at before CRM lookup to fail fast.

Right now Line 85 runs after the CRM search request, so malformed timestamps still incur external I/O and can be masked by earlier contact_not_found paths. Moving normalization ahead of the first API call would enforce the worker boundary contract more consistently.

♻️ Suggested refactor sketch
 def process_agreement(...):
masked_email = mask_email(email)
+ try:+ crm_completed_at = self._normalize_completed_at(completed_at)+ except ValueError as exc:+ logger.error(+ "CRM update failed for invalid datetime=%s: %s",+ completed_at,+ exc,+ )+ return {+ "success": False,+ "masked_email": masked_email,+ "submission_id": submission_id,+ "error": f"invalid_completed_at: {exc}",+ }+
try:
result = self.api.request("GET", "Contact", {...})
except EspoAPIError as exc:
...
-- try:- crm_completed_at = self._normalize_completed_at(completed_at)- except ValueError as exc:- ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py` around lines 84 -
100, Move the completed_at normalization so it runs before any CRM lookup: call
self._normalize_completed_at(completed_at) at the start of the processing flow
(before the CRM search/lookup call that uses contact_id), catch ValueError and
return the same error payload (including masked_email, submission_id, contact_id
and error=f"invalid_completed_at: {exc}") and log via logger.error as already
done; update the method containing the CRM search to remove the current
try/except around _normalize_completed_at that occurs after the CRM call so
malformed timestamps fail fast and avoid performing external I/O.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py`:
- Around line 84-100: Move the completed_at normalization so it runs before any
CRM lookup: call self._normalize_completed_at(completed_at) at the start of the
processing flow (before the CRM search/lookup call that uses contact_id), catch
ValueError and return the same error payload (including masked_email,
submission_id, contact_id and error=f"invalid_completed_at: {exc}") and log via
logger.error as already done; update the method containing the CRM search to
remove the current try/except around _normalize_completed_at that occurs after
the CRM call so malformed timestamps fail fast and avoid performing external
I/O.
In `@apps/worker/src/five08/worker/jobs.py`:
- Around line 16-17: Replace the duplicated DocuSeal datetime literal with a
single shared constant: move DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral
shared module (e.g., a new constant in five08 package like docuseal_constants or
constants) and update all call sites to import that constant instead of
hardcoding the string; specifically, replace literal occurrences in
backend/api.py (where the format is used around the DocuSeal handling) and
worker/crm/docuseal_processor.py to import DOCUSEAL_COMPLETED_AT_UTC_FORMAT,
remove the duplicate literals, and run tests to ensure no import errors.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48e6f50 and 7d2c938.

📒 Files selected for processing (7)
  • README.md
  • apps/worker/README.md
  • apps/worker/src/five08/backend/api.py
  • apps/worker/src/five08/worker/crm/docuseal_processor.py
  • apps/worker/src/five08/worker/jobs.py
  • tests/unit/test_backend_api.py
  • tests/unit/test_docuseal_processor.py

@michaelmwu
michaelmwu merged commit 950f144 into mainMar 2, 2026
5 checks passed
@michaelmwu
michaelmwu deleted the michaelmwu/crm-member-fix branch March 2, 2026 10:39
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@michaelmwu
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix: normalize DocuSeal completed_at queue contract by michaelmwu · Pull Request #78 · 508-dev/508-workflows · GitHub
Skip to content

fix: normalize DocuSeal completed_at queue contract - #78

Merged
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix
Mar 2, 2026
Merged

fix: normalize DocuSeal completed_at queue contract#78
michaelmwu merged 1 commit into
mainfrom
michaelmwu/crm-member-fix

Conversation

@michaelmwu

@michaelmwumichaelmwu commented Mar 2, 2026

Copy link
Copy Markdown
Member

Description

Normalize DocuSeal webhook agreement timestamps into an explicit UTC queue contract (YYYY-MM-DD HH:mm:ss) before enqueuing jobs and preserve the same contract through worker processing to prevent CRM validation failures.
The webhook handler now normalizes completed_at from raw ISO input (including offsets) into UTC before queueing process_docuseal_agreement_job.
process_docuseal_agreement_job and DocusealAgreementProcessor are documented to treat completed_at as a UTC string for JSON-serializable queue payloads.
I added a worker-scoped docs file (apps/worker/README.md) and pointed the root README webhook list to this contract.
Regression coverage was added for webhook enqueue contract normalization and processor-side datetime formatting plus invalid timestamp handling.

Related Issue

None.

How Has This Been Tested?

Unit tests for backend/api.py and docuseal_processor.py were updated to assert UTC string conversion and persisted argument format.

Summary by CodeRabbit

  • Documentation

    • Added webhook endpoint documentation for DocuSeal contract processing and other service integrations.
  • Bug Fixes

    • Fixed timestamp handling in the DocuSeal webhook to consistently normalize completion times to UTC format (YYYY-MM-DD HH:MM:SS) for reliable CRM synchronization.
    • Added validation to gracefully handle invalid timestamp inputs with descriptive error responses.

@coderabbitai

coderabbitaiBot commented Mar 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Added webhook endpoint documentation and implemented UTC timestamp normalization for DocuSeal contract completion timestamps across the API layer, worker processor, and supporting tests. Includes validation logic to handle invalid timestamp inputs gracefully.

Changes

Cohort / File(s)Summary
Documentation
README.md, apps/worker/README.md
Added webhook endpoint documentation including POST /webhooks/docuseal, /webhooks/{source}, /webhooks/espocrm, and /webhooks/espocrm/people-sync with expected input contract details for completed_at UTC format.
Timestamp Normalization Logic
apps/worker/src/five08/backend/api.py, apps/worker/src/five08/worker/crm/docuseal_processor.py, apps/worker/src/five08/worker/jobs.py
Introduced UTC coercion and normalization helper functions for completed_at timestamps (format: YYYY-MM-DD HH:MM:SS), validation in processor with structured error responses, and public format constant definition.
Unit Tests
tests/unit/test_backend_api.py, tests/unit/test_docuseal_processor.py
Added test coverage for UTC timestamp normalization, validation of enqueue_job argument ordering with normalized timestamps, invalid completed_at handling, and timestamp format conversion from offset to UTC strings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A timestamp walks through the UTC door,
Normalized, formatted, no timezone more,
From webhook to processor, validation in sight,
DocuSeal agreements signed, formatted just right! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix: normalize DocuSeal completed_at queue contract' accurately summarizes the main change: normalizing timestamp handling for DocuSeal webhooks to enforce a consistent UTC string format across the queue contract.
Docstring Coverage✅ PassedDocstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch michaelmwu/crm-member-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/worker/src/five08/worker/jobs.py (1)

16-17: Use one shared source for the DocuSeal datetime format across modules.

Line 16 defines the contract format, but the same literal is still hardcoded in apps/worker/src/five08/backend/api.py (Line 133) and apps/worker/src/five08/worker/crm/docuseal_processor.py (Line 29). Centralizing this in a neutral shared module would reduce contract drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/jobs.py` around lines 16 - 17, Replace the
duplicated DocuSeal datetime literal with a single shared constant: move
DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral shared module (e.g., a new
constant in five08 package like docuseal_constants or constants) and update all
call sites to import that constant instead of hardcoding the string;
specifically, replace literal occurrences in backend/api.py (where the format is
used around the DocuSeal handling) and worker/crm/docuseal_processor.py to
import DOCUSEAL_COMPLETED_AT_UTC_FORMAT, remove the duplicate literals, and run
tests to ensure no import errors.
apps/worker/src/five08/worker/crm/docuseal_processor.py (1)

84-100: Validate completed_at before CRM lookup to fail fast.

Right now Line 85 runs after the CRM search request, so malformed timestamps still incur external I/O and can be masked by earlier contact_not_found paths. Moving normalization ahead of the first API call would enforce the worker boundary contract more consistently.

♻️ Suggested refactor sketch
 def process_agreement(...):
masked_email = mask_email(email)
+ try:+ crm_completed_at = self._normalize_completed_at(completed_at)+ except ValueError as exc:+ logger.error(+ "CRM update failed for invalid datetime=%s: %s",+ completed_at,+ exc,+ )+ return {+ "success": False,+ "masked_email": masked_email,+ "submission_id": submission_id,+ "error": f"invalid_completed_at: {exc}",+ }+
try:
result = self.api.request("GET", "Contact", {...})
except EspoAPIError as exc:
...
-- try:- crm_completed_at = self._normalize_completed_at(completed_at)- except ValueError as exc:- ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py` around lines 84 -
100, Move the completed_at normalization so it runs before any CRM lookup: call
self._normalize_completed_at(completed_at) at the start of the processing flow
(before the CRM search/lookup call that uses contact_id), catch ValueError and
return the same error payload (including masked_email, submission_id, contact_id
and error=f"invalid_completed_at: {exc}") and log via logger.error as already
done; update the method containing the CRM search to remove the current
try/except around _normalize_completed_at that occurs after the CRM call so
malformed timestamps fail fast and avoid performing external I/O.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/worker/src/five08/worker/crm/docuseal_processor.py`:
- Around line 84-100: Move the completed_at normalization so it runs before any
CRM lookup: call self._normalize_completed_at(completed_at) at the start of the
processing flow (before the CRM search/lookup call that uses contact_id), catch
ValueError and return the same error payload (including masked_email,
submission_id, contact_id and error=f"invalid_completed_at: {exc}") and log via
logger.error as already done; update the method containing the CRM search to
remove the current try/except around _normalize_completed_at that occurs after
the CRM call so malformed timestamps fail fast and avoid performing external
I/O.
In `@apps/worker/src/five08/worker/jobs.py`:
- Around line 16-17: Replace the duplicated DocuSeal datetime literal with a
single shared constant: move DOCUSEAL_COMPLETED_AT_UTC_FORMAT into a neutral
shared module (e.g., a new constant in five08 package like docuseal_constants or
constants) and update all call sites to import that constant instead of
hardcoding the string; specifically, replace literal occurrences in
backend/api.py (where the format is used around the DocuSeal handling) and
worker/crm/docuseal_processor.py to import DOCUSEAL_COMPLETED_AT_UTC_FORMAT,
remove the duplicate literals, and run tests to ensure no import errors.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48e6f50 and 7d2c938.

📒 Files selected for processing (7)
  • README.md
  • apps/worker/README.md
  • apps/worker/src/five08/backend/api.py
  • apps/worker/src/five08/worker/crm/docuseal_processor.py
  • apps/worker/src/five08/worker/jobs.py
  • tests/unit/test_backend_api.py
  • tests/unit/test_docuseal_processor.py

@michaelmwu
michaelmwu merged commit 950f144 into mainMar 2, 2026
5 checks passed
@michaelmwu
michaelmwu deleted the michaelmwu/crm-member-fix branch March 2, 2026 10:39
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@michaelmwu