Skip to content

refactor(api): migrate workspace account marshal_with responses to BaseModel#35190

Merged
asukaminato0721 merged 1 commit intolanggenius:mainfrom
ai-hpc:refactor/migrate-workspace-account-response-models-28015
Apr 14, 2026
Merged

refactor(api): migrate workspace account marshal_with responses to BaseModel#35190
asukaminato0721 merged 1 commit intolanggenius:mainfrom
ai-hpc:refactor/migrate-workspace-account-response-models-28015

Conversation

@ai-hpc
Copy link
Copy Markdown
Contributor

@ai-hpc ai-hpc commented Apr 14, 2026

Summary

  • migrate remaining @marshal_with response paths in api/controllers/console/workspace/account.py to Pydantic response models
  • replace RESTX response dict models for account integrates and education endpoints with ResponseModel DTOs
  • preserve timestamp output shape by normalizing datetime fields to unix timestamps

Related

Validation

  • ruff check api/controllers/console/workspace/account.py
  • python3 -m py_compile api/controllers/console/workspace/account.py
  • pytest -q api/tests/unit_tests/controllers/console/workspace/test_accounts.py (fails in this environment: missing graphon dependency)

@ai-hpc ai-hpc requested a review from QuantumGhost as a code owner April 14, 2026 16:27
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. refactor labels Apr 14, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Pyrefly Diff

No changes detected.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Apr 14, 2026
@asukaminato0721 asukaminato0721 requested a review from Copilot April 14, 2026 17:53
@asukaminato0721 asukaminato0721 added this pull request to the merge queue Apr 14, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors the console workspace account controller to replace remaining Flask-RESTX @marshal_with response handling with Pydantic ResponseModel DTOs, while keeping timestamp fields serialized as unix timestamps.

Changes:

  • Replaced RESTX dict/field models for account integrates + education endpoints with Pydantic ResponseModel responses.
  • Added timestamp normalization via Pydantic validators (created_at, expire_at).
  • Updated endpoint decorators to use console_ns.response(..., console_ns.models[...]) and return model_dump(mode="json").

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 533 to 535
# convert expire_at to UTC timestamp from isoformat
if res and "expire_at" in res:
res["expire_at"] = datetime.fromisoformat(res["expire_at"]).astimezone(pytz.utc)
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

EducationIdentity.status() is wrapped with or {} and then expire_at is unconditionally parsed via datetime.fromisoformat(...). If the billing API returns expire_at: null (or any non-string type), fromisoformat will raise a TypeError. Consider only parsing when expire_at is a non-empty str (and handle common ISO forms like a trailing Z), otherwise leave it as-is for the expire_at field validator to normalize when it’s already a datetime/timestamp.

Suggested change
# convert expire_at to UTC timestamp from isoformat
if res and "expire_at" in res:
res["expire_at"] = datetime.fromisoformat(res["expire_at"]).astimezone(pytz.utc)
# convert expire_at to UTC timestamp from isoformat when it is a non-empty string
if res and "expire_at" in res:
expire_at = res["expire_at"]
if isinstance(expire_at, str) and expire_at:
if expire_at.endswith("Z"):
expire_at = f"{expire_at[:-1]}+00:00"
res["expire_at"] = datetime.fromisoformat(expire_at).astimezone(pytz.utc)

Copilot uses AI. Check for mistakes.
Comment on lines +552 to 556
return EducationAutocompleteResponse.model_validate(
BillingService.EducationIdentity.autocomplete(args.keywords, args.page, args.limit) or {}
).model_dump(mode="json")


Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

EducationAutocompleteResponse defaults data to an empty list and ignores unknown keys (ResponseModel.extra = "ignore"). If the billing API returns a different top-level key (e.g. institutions as used in api/tests/unit_tests/services/test_billing_service.py), this endpoint will now silently return {data: []} instead of surfacing the mismatch. Consider explicitly transforming the billing response into the {data, curr_page, has_next} shape (or adding field aliases / a validator to map known alternative keys) so a schema mismatch doesn’t become a silent empty result.

Suggested change
return EducationAutocompleteResponse.model_validate(
BillingService.EducationIdentity.autocomplete(args.keywords, args.page, args.limit) or {}
).model_dump(mode="json")
raw_res = BillingService.EducationIdentity.autocomplete(args.keywords, args.page, args.limit) or {}
if not isinstance(raw_res, dict):
raise ValueError("Invalid education autocomplete response")
if "data" in raw_res:
res = {
"data": raw_res["data"],
"curr_page": raw_res.get("curr_page"),
"has_next": raw_res.get("has_next"),
}
elif "institutions" in raw_res:
res = {
"data": raw_res["institutions"],
"curr_page": raw_res.get("curr_page"),
"has_next": raw_res.get("has_next"),
}
elif raw_res:
raise ValueError("Unexpected education autocomplete response schema")
else:
res = {}
return EducationAutocompleteResponse.model_validate(res).model_dump(mode="json")

Copilot uses AI. Check for mistakes.
Comment on lines 500 to +504
account, _ = current_account_with_tenant()

return BillingService.EducationIdentity.verify(account.id, account.email)
return EducationVerifyResponse.model_validate(
BillingService.EducationIdentity.verify(account.id, account.email) or {}
).model_dump(mode="json")
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

Education verify/status/autocomplete response paths were refactored to use Pydantic response models and timestamp normalization, but the controller unit tests in api/tests/unit_tests/controllers/console/workspace/test_accounts.py don’t cover these endpoints (it currently stops at AccountIntegrateApi). Adding tests for these endpoints would help catch schema/key mismatches and ensure expire_at stays normalized as expected.

Copilot uses AI. Check for mistakes.
Merged via the queue into langgenius:main with commit a2ea7ca Apr 14, 2026
32 checks passed
HanqingZ pushed a commit to HanqingZ/dify that referenced this pull request Apr 23, 2026
…seModel (langgenius#35190)

Co-authored-by: ai-hpc <ai-hpc@users.noreply.github.com>
asukaminato0721 pushed a commit to asukaminato0721/dify that referenced this pull request Apr 24, 2026
…seModel (langgenius#35190)

Co-authored-by: ai-hpc <ai-hpc@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer refactor size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants