Skip to content

chore(grammarly): migrate to autohive-integrations-sdk 2.0.0#361

Open
Shubhank-Jonnada wants to merge 13 commits into
masterfrom
sj/grammarly-migration-2.0.0
Open

chore(grammarly): migrate to autohive-integrations-sdk 2.0.0#361
Shubhank-Jonnada wants to merge 13 commits into
masterfrom
sj/grammarly-migration-2.0.0

Conversation

@Shubhank-Jonnada

Copy link
Copy Markdown
Contributor

Summary

  • Bump autohive-integrations-sdk to ~=2.0.0; drop aiohttp dependency
  • Fix auth: flat context.auth.get() instead of nested credentials dict
  • Replace aiohttp with context.fetch() for all API calls (token, API requests, file upload)
  • Error returns use ActionError instead of ActionResult with result:False
  • Bump config.json version to 2.0.0; remove result/error fields from all output schemas
  • Replace context.py + test_grammarly.py with conftest.py + unit + integration tests

Test plan

  • 13 unit tests, all 7 actions covered: pytest grammarly/tests/test_grammarly_unit.py
  • validate_integration.py autohive-integrations/grammarly - 0 errors, 0 warnings
  • check_code.py autohive-integrations/grammarly - all checks passed
  • Live tests require Grammarly OAuth2 credentials (GRAMMARLY_CLIENT_ID, GRAMMARLY_CLIENT_SECRET)

- Bump SDK to ~=2.0.0 in requirements.txt (drop aiohttp dependency)
- Bump config.json version to 2.0.0; remove result/error from output schemas
- Fix auth: flat context.auth.get() instead of nested credentials dict
- Replace aiohttp with context.fetch() for all API calls
- Error returns now use ActionError instead of ActionResult with result:False
- Replace context.py + test_grammarly.py with conftest.py + unit + integration tests
- All 7 actions covered by 13 unit tests
- Add Grammarly env vars to root .env.example
@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown

🔍 Integration Validation Results

Commit: a2d2e71423e5c5858c0fc2371a20d84658272c9c · fix(grammarly): compute analytics date range dynamically in integration test
Updated: 2026-06-23T10:37:32Z

Changed directories: grammarly

Check Result
Structure ✅ Passed
Code ✅ Passed
Tests ✅ Passed
README ✅ Passed
Version ✅ Passed
✅ Structure Check output
Validating 1 integration(s)...

============================================================
Integration: grammarly
============================================================
✅ All checks passed!

============================================================
SUMMARY
============================================================
Integrations validated: 1
Total errors: 0
Total warnings: 0

✅ All validations passed!
✅ Code Check output
----------------------------------------
Checking: grammarly
----------------------------------------

📦 Installing dependencies...

🐍 Checking Python syntax...
   ✅ Syntax OK

📥 Checking imports...
   ✅ Imports OK

📄 Checking JSON files...
   ✅ JSON files OK

🔍 Linting with ruff...
   ✅ Lint OK

🎨 Checking formatting with ruff...
   ✅ Formatting OK

🔒 Scanning for security issues with bandit...
   ✅ Security OK

🛡️ Checking dependencies for vulnerabilities with pip-audit...
   ✅ Dependencies OK

🔗 Checking config-code sync...
   ✅ Config-code sync OK

🔄 Checking fetch patterns...
   ✅ Fetch patterns OK

========================================
✅ CODE CHECK PASSED
========================================
✅ Tests Check output

Integration   Tests  Coverage        Status
-------------------------------------------
grammarly     13/13       93%      ✅ Passed
-------------------------------------------
Total         13/13            ✅ All passed

✅ Tests passed: grammarly
✅ README Check output
========================================
✅ README CHECK PASSED
========================================
✅ Version Check output
✅ grammarly: 1.0.0 → 2.0.0 (major bump)

========================================
✅ VERSION CHECK PASSED
========================================

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 75d5d8dce9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread grammarly/grammarly.py Outdated
@Shubhank-Jonnada

Copy link
Copy Markdown
Contributor Author

All unit tests performed and passing. Live integration tests not yet run — Grammarly OAuth credentials required.

@TheRealAgentK TheRealAgentK left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Requesting changes for the Grammarly SDK 2 blockers.

Comment thread grammarly/grammarly.py Outdated
Comment thread grammarly/grammarly.py Outdated
Comment thread grammarly/tests/test_grammarly_integration.py Outdated
Comment thread grammarly/tests/test_grammarly_integration.py Outdated
Comment thread grammarly/tests/test_grammarly_integration.py Outdated
Comment thread grammarly/tests/test_grammarly_unit.py Outdated
@TheRealAgentK

Copy link
Copy Markdown
Collaborator

Before approval or merge, we need a real integration test run with valid credentials, using the SDK 2 request path. The live tests must fail on ACTION_ERROR or VALIDATION_ERROR instead of accepting them as passing outcomes.

@TheRealAgentK TheRealAgentK left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Added a coverage follow-up.

Comment thread grammarly/tests/test_grammarly_integration.py Outdated
SDK 2 ExecutionContext.fetch() accepts data= and content_type=, not body=.
The token request (form-encoded) and file upload (text/plain) were both
passing body= which would raise TypeError at runtime.

Also read client_id/client_secret from context.auth['credentials'] first
(production SDK 2 shape) with a flat fallback for unit-test mocks.
MagicMock(data=...) doesn't match the SDK 2 fetch() contract. Replace all
mock responses with FetchResponse(status=200, headers={}, data=...) so
unit tests exercise the same response shape the real SDK returns.
…verage

- Add pytest.mark.integration to pytestmark so -m integration selects them
- Replace stub _Ctx with aiohttp-backed real_fetch via make_context fixture,
  matching the SDK 2 fetch() contract (data=, content_type=, HTTPError/RateLimitError)
- Assert result.type == ResultType.ACTION for all live-path tests
- Add coverage for get_writing_score_results, analyze_ai_detection,
  get_ai_detection_results, analyze_plagiarism_detection,
  get_plagiarism_detection_results (all previously missing)
@Shubhank-Jonnada

Copy link
Copy Markdown
Contributor Author

Live integration tests passed - 7/7, 19-06-2026.

One fix applied during testing: test_get_user_analytics was using date_from: 2024-01-01 which the Grammarly API rejects with date_from_can_not_exceed_one_year. Updated to a date range within the last year.

All actions verified against the live API:

  • analyze_writing_score
  • get_writing_score_results
  • get_user_analytics
  • analyze_ai_detection
  • get_ai_detection_results
  • analyze_plagiarism_detection
  • get_plagiarism_detection_results

@TheRealAgentK TheRealAgentK left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Posted two inline review comments from the follow-up review.

After these are addressed, please also add the output of a new integration test run, @Shubhank-Jonnada

Comment thread grammarly/grammarly.py Outdated
# ---- Writing Score API Actions ----
async def upload_file(context: ExecutionContext, upload_url: str, file_content: str) -> bool:
await context.fetch(
upload_url,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Blocker: this upload path now passes the pre-signed URL to context.fetch() as a plain string, which loses the old yarl.URL(upload_url, encoded=True) safeguard. These upload URLs are S3-style signed URLs, and aiohttp can canonicalize encoded query parameters (for example %2F in X-Amz-Credential) when it builds the request URL, invalidating the signature. Please preserve the encoded URL here, e.g. by passing URL(upload_url, encoded=True) through to context.fetch() if supported, or by keeping a dedicated aiohttp upload helper that uses encoded=True. A small unit test around the upload URL/call shape would help prevent this from regressing again.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

A minimal version of the fix should be possible by passing a yarl.URL object through to context.fetch() (aiohttp accepts this even though the SDK annotation is str):

from yarl import URL

async def upload_file(context: ExecutionContext, upload_url: str, file_content: str) -> bool:
    await context.fetch(
        URL(upload_url, encoded=True),
        method="PUT",
        data=file_content,
        content_type="text/plain",
    )
    return True

If we import yarl directly, please add it to grammarly/requirements.txt rather than relying on aiohttp/SDK transitive dependencies. If passing a non-str into context.fetch() is considered too loose, the fallback is to keep a dedicated aiohttp upload helper that does session.put(URL(upload_url, encoded=True), ...) for this one signed-URL upload path.

@Shubhank-Jonnada Shubhank-Jonnada Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in b78f8de . upload_file now wraps the pre-signed URL in URL(upload_url, encoded=True) from yarl to prevent aiohttp from re-encoding query parameters and invalidating the S3 signature. Added yarl>=1.9 to requirements.txt.

async def test_get_user_analytics(live_context):
result = await grammarly.execute_action(
"get_user_analytics",
{"date_from": "2025-12-01", "date_to": "2025-12-31"},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should fix: this hard-coded analytics date range will eventually age out of Grammarly's “past 365 days with 2-day lag” API window, making the live test fail over time. Please compute this dynamically, e.g. date_to = today - 2 days and date_from = date_to - 30 days, then format both as YYYY-MM-DD.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in a2d2e71. The analytics test now computes date_to as today minus 2 days and date_from as date_to minus 30 days, formatted as YYYY-MM-DD, so the range stays inside Grammarly's 365-day window indefinitely.

… signed URL integrity

S3 pre-signed URLs contain percent-encoded query params (e.g. %2F in
X-Amz-Credential) that aiohttp can re-encode when building the request
URL, invalidating the signature. Wrapping in URL(upload_url, encoded=True)
tells aiohttp the URL is already encoded. Added yarl>=1.9 to requirements.txt
rather than relying on it as a transitive dependency.
…on test

Hard-coded dates age out of Grammarly's 365-day window with 2-day lag.
Changed to date_to = today - 2 days, date_from = date_to - 30 days,
formatted as YYYY-MM-DD, so the test stays valid indefinitely.
Sign up for free to 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.

2 participants