Skip to content

Add JsonImport resource for bulk data import (PIP-310)#137

Merged
wscourge merged 3 commits intomainfrom
wiktor/pip-310-datasource-json-imports
Apr 15, 2026
Merged

Add JsonImport resource for bulk data import (PIP-310)#137
wscourge merged 3 commits intomainfrom
wiktor/pip-310-datasource-json-imports

Conversation

@wscourge
Copy link
Copy Markdown
Contributor

@wscourge wscourge commented Apr 9, 2026

Summary

  • New JsonImport resource class with dedicated schema for bulk import API responses
  • JsonImport.create(config, uuid='ds_...', data={...}): POST /data_sources/{uuid}/json_imports
  • JsonImport.retrieve(config, uuid='ds_...', import_id='...'): GET /data_sources/{uuid}/json_imports/{import_id}

Usage

import chartmogul

config = chartmogul.Config("token")

# Import data in bulk
result = chartmogul.JsonImport.create(
    config, uuid='ds_45d064ca-fcf8-11f0-903f-33618f80d753',
    data={
        'external_id': 'import_batch_001',
        'customers': [
            {
                'external_id': 'cus_acme_001',
                'name': 'Acme Corp',
                'email': 'billing@acme.com',
                'company': 'Acme Corporation',
                'country': 'US',
                'state': 'US-CA',
                'city': 'San Francisco',
                'zip': '94105',
                'lead_created_at': '2025-10-15T00:00:00Z',
                'free_trial_started_at': '2025-11-01T00:00:00Z',
            }
        ],
        'plans': [
            {
                'name': 'Professional Monthly',
                'external_id': 'plan_pro_monthly',
                'interval_count': 1,
                'interval_unit': 'month',
            }
        ],
        'invoices': [
            {
                'external_id': 'inv_2025_11_001',
                'customer_external_id': 'cus_acme_001',
                'date': '2025-11-01T00:00:00Z',
                'due_date': '2025-12-01T00:00:00Z',
                'currency': 'USD',
            }
        ],
        'line_items': [
            {
                'invoice_external_id': 'inv_2025_11_001',
                'type': 'subscription',
                'amount_in_cents': 9900,
                'quantity': 5,
                'plan_external_id': 'plan_pro_monthly',
                'subscription_external_id': 'sub_acme_pro',
                'service_period_start': '2025-11-01T00:00:00Z',
                'service_period_end': '2025-12-01T00:00:00Z',
            }
        ],
        'transactions': [
            {
                'invoice_external_id': 'inv_2025_11_001',
                'external_id': 'txn_001',
                'type': 'payment',
                'result': 'successful',
                'date': '2025-11-01T12:30:00Z',
            }
        ],
        'subscription_events': [
            {
                'external_id': 'evt_acme_start',
                'customer_external_id': 'cus_acme_001',
                'subscription_external_id': 'sub_acme_pro',
                'plan_external_id': 'plan_pro_monthly',
                'event_type': 'subscription_start',
                'event_date': '2025-11-01T00:00:00Z',
                'effective_date': '2025-11-01T00:00:00Z',
                'currency': 'USD',
                'amount_in_cents': 9900,
                'quantity': 5,
            }
        ],
    }
).get()
result.id      # "4815d987-..." (use for status tracking)
result.status  # "queued"

# Track import status
status = chartmogul.JsonImport.retrieve(
    config, uuid='ds_45d064ca-fcf8-11f0-903f-33618f80d753',
    import_id=result.id
).get()
status.status          # "completed"
status.status_details  # per-customer breakdown

Backwards compatibility

Change Breaking?
Added JsonImport resource class No — new class
Added JsonImport.create method No — new method
Added JsonImport.retrieve method No — new method

Test plan

  • Added tests for JsonImport.create with realistic bulk import payload (customers, plans, invoices, line items, transactions, subscription events)
  • Added tests for JsonImport.retrieve with status response assertions
  • Tests assert on all response fields (id, status, external_id, data_source_uuid, status_details)
  • All existing tests pass (140 total)

🤖 Generated with Claude Code

@wscourge wscourge marked this pull request as draft April 9, 2026 12:16
wscourge and others added 2 commits April 14, 2026 08:44
- DataSource.import_json: POST /data_sources/{uuid}/json_imports
- DataSource.import_status: GET /data_sources/{uuid}/json_imports/{import_id}

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace monkey-patched DataSource.import_json/import_status with a
dedicated JsonImport class that has its own schema, so response fields
(id, external_id, status, status_details) are properly deserialized
instead of being silently dropped by DataSource's EXCLUDE schema.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@wscourge wscourge force-pushed the wiktor/pip-310-datasource-json-imports branch from bd13501 to 7f3d54e Compare April 14, 2026 07:27
@wscourge wscourge changed the title Add DataSource bulk import and status tracking (PIP-310) Add JsonImport resource for bulk data import (PIP-310) Apr 14, 2026
@wscourge wscourge marked this pull request as ready for review April 14, 2026 08:39
@loomchild loomchild self-assigned this Apr 14, 2026
Comment thread chartmogul/api/json_import.py Outdated


JsonImport.create = JsonImport._method(
"create", "post", "/data_sources{/uuid}/json_imports")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[minor] IMO param should be called data_source_uuid. import_id should be passed as id or perhaps even uuid (but then inconsistent with field name, not sure what we do in other resources in such case; it also doesn't have a prefix, not sure if it's because it's not an independent object.).

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.

For me it is obvious that if uuid path param is under /data_sources/{uuid} then it is the DataSource's UUID, but being explicit won't hurt, I updated it to data_source_uuid 👍 .

I like the import_id to id update, I don't want to use uuid because the id is what Import Data in Bulk responds with:

image

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Makes sense, thanks.

Address review feedback from loomchild: use data_source_uuid
consistently with the rest of the SDK, and id for the import
identifier. Add custom _validate_arguments for both params.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@loomchild loomchild left a comment

Choose a reason for hiding this comment

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

LGTM and works for me, thanks for the changes.

@wscourge wscourge merged commit 3d22b1a into main Apr 15, 2026
7 checks passed
@wscourge wscourge deleted the wiktor/pip-310-datasource-json-imports branch April 15, 2026 11:02
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.

3 participants