diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4ad9441..98273bb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,7 +20,7 @@ jobs:
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Setup go
uses: actions/setup-go@v5
@@ -35,7 +35,7 @@ jobs:
runs-on: ${{ github.repository == 'stainless-sdks/cas-parser-go' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Setup go
uses: actions/setup-go@v5
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 28e831b..3d2ac0b 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.0.3"
+ ".": "0.1.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 92721c7..2c90fa6 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 5
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser%2Fcas-parser-b7fdba3d3f97c7debc22c7ca30b828bce81bcd64648df8c94029b27a3321ebb9.yml
-openapi_spec_hash: 03f1315f1d32ada42445ca920f047dff
+configured_endpoints: 4
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser%2Fcas-parser-56b0f699c5437d9e5326626d35dfc972c17d01f12cb416c7f4854c8ea6d0e95e.yml
+openapi_spec_hash: 158f405c1880706266d83e6ff16b9d2f
config_hash: cb5d75abef6264b5d86448caf7295afa
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 077729d..c7c7cd5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,33 @@
# Changelog
+## 0.1.0 (2026-02-03)
+
+Full Changelog: [v0.0.3...v0.1.0](https://github.com/CASParser/cas-parser-go/compare/v0.0.3...v0.1.0)
+
+### Features
+
+* **api:** api update ([321c851](https://github.com/CASParser/cas-parser-go/commit/321c851d5ce4729962559904ec4e13db1d8febdd))
+* **api:** api update ([58bd341](https://github.com/CASParser/cas-parser-go/commit/58bd3413a106d7e2e99011664649d9ea80f47286))
+* **api:** api update ([58f353e](https://github.com/CASParser/cas-parser-go/commit/58f353ea9385874ac4f84ae9107bcee481fd79f9))
+* **api:** api update ([4de0c4b](https://github.com/CASParser/cas-parser-go/commit/4de0c4b7936cbe00370980ab20aad02586090fcd))
+* **api:** api update ([c4b3ed4](https://github.com/CASParser/cas-parser-go/commit/c4b3ed40ff9e5d6ad1ac4ca57e0219d184038d20))
+* **api:** api update ([4cafc4e](https://github.com/CASParser/cas-parser-go/commit/4cafc4e634e376310e635eeeacd9c6bc1656d7ca))
+
+
+### Bug Fixes
+
+* bugfix for setting JSON keys with special characters ([48f9ffd](https://github.com/CASParser/cas-parser-go/commit/48f9ffda3a0053adcf1a68c5378bbeac9de9027c))
+* use slices.Concat instead of sometimes modifying r.Options ([c30d79a](https://github.com/CASParser/cas-parser-go/commit/c30d79aa4eb1f8b4d793fe5e641db7d0f385a9dc))
+
+
+### Chores
+
+* bump minimum go version to 1.22 ([54f1ae1](https://github.com/CASParser/cas-parser-go/commit/54f1ae1bedb207621a92b70a64220456781a8e37))
+* do not install brew dependencies in ./scripts/bootstrap by default ([a665c6a](https://github.com/CASParser/cas-parser-go/commit/a665c6aa0d14754a7f4ea644b2b53ac49dec0f0c))
+* **internal:** codegen related update ([b0d066b](https://github.com/CASParser/cas-parser-go/commit/b0d066be8e48533c4e0bf3f004c3c6ad96af0008))
+* **internal:** grammar fix (it's -> its) ([0af28e9](https://github.com/CASParser/cas-parser-go/commit/0af28e95581cfd4c2d9f729ee59f8213253da54a))
+* update more docs for 1.22 ([fc3b56e](https://github.com/CASParser/cas-parser-go/commit/fc3b56e861603f65fd1a61d6c843b220e94154cc))
+
## 0.0.3 (2025-09-06)
Full Changelog: [v0.0.2...v0.0.3](https://github.com/CASParser/cas-parser-go/compare/v0.0.2...v0.0.3)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a02f386..5be4390 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -9,7 +9,7 @@ $ ./scripts/lint
This will install all the required dependencies and build the SDK.
-You can also [install go 1.18+ manually](https://go.dev/doc/install).
+You can also [install go 1.22+ manually](https://go.dev/doc/install).
## Modifying/Adding code
diff --git a/LICENSE b/LICENSE
index f1756ce..6bbb512 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2025 Cas Parser
+ Copyright 2026 Cas Parser
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index 14db163..5d464fc 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,15 @@ from applications written in Go.
It is generated with [Stainless](https://www.stainless.com/).
+## MCP Server
+
+Use the Cas Parser MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.
+
+[](https://cursor.com/en-US/install-mcp?name=cas-parser-node-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImNhcy1wYXJzZXItbm9kZS1tY3AiXSwiZW52Ijp7IkNBU19QQVJTRVJfQVBJX0tFWSI6Ik15IEFQSSBLZXkifX0)
+[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22cas-parser-node-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22cas-parser-node-mcp%22%5D%2C%22env%22%3A%7B%22CAS_PARSER_API_KEY%22%3A%22My%20API%20Key%22%7D%7D)
+
+> Note: You may need to set environment variables in your MCP client.
+
## Installation
@@ -28,14 +37,14 @@ Or to pin the version:
```sh
-go get -u 'github.com/CASParser/cas-parser-go@v0.0.3'
+go get -u 'github.com/CASParser/cas-parser-go@v0.1.0'
```
## Requirements
-This library requires Go 1.18+.
+This library requires Go 1.22+.
## Usage
@@ -128,7 +137,7 @@ custom := param.Override[casparser.FooParams](12)
### Request unions
-Unions are represented as a struct with fields prefixed by "Of" for each of it's variants,
+Unions are represented as a struct with fields prefixed by "Of" for each of its variants,
only one field can be non-zero. The non-zero field will be serialized.
Sub-properties of the union can be accessed via methods on the union struct.
diff --git a/api.md b/api.md
index ddecff9..30672c5 100644
--- a/api.md
+++ b/api.md
@@ -6,17 +6,9 @@ Response Types:
Methods:
-- client.CasParser.CamsKfintech(ctx context.Context, body casparser.CasParserCamsKfintechParams) (casparser.UnifiedResponse, error)
-- client.CasParser.Cdsl(ctx context.Context, body casparser.CasParserCdslParams) (casparser.UnifiedResponse, error)
-- client.CasParser.Nsdl(ctx context.Context, body casparser.CasParserNsdlParams) (casparser.UnifiedResponse, error)
-- client.CasParser.SmartParse(ctx context.Context, body casparser.CasParserSmartParseParams) (casparser.UnifiedResponse, error)
+- client.CasParser.CamsKfintech(ctx context.Context, body casparser.CasParserCamsKfintechParams) (\*casparser.UnifiedResponse, error)
+- client.CasParser.Cdsl(ctx context.Context, body casparser.CasParserCdslParams) (\*casparser.UnifiedResponse, error)
+- client.CasParser.Nsdl(ctx context.Context, body casparser.CasParserNsdlParams) (\*casparser.UnifiedResponse, error)
+- client.CasParser.SmartParse(ctx context.Context, body casparser.CasParserSmartParseParams) (\*casparser.UnifiedResponse, error)
# CasGenerator
-
-Response Types:
-
-- casparser.CasGeneratorGenerateCasResponse
-
-Methods:
-
-- client.CasGenerator.GenerateCas(ctx context.Context, body casparser.CasGeneratorGenerateCasParams) (casparser.CasGeneratorGenerateCasResponse, error)
diff --git a/casgenerator.go b/casgenerator.go
index e1dea8b..4b3b086 100644
--- a/casgenerator.go
+++ b/casgenerator.go
@@ -3,14 +3,7 @@
package casparser
import (
- "context"
- "net/http"
-
- "github.com/CASParser/cas-parser-go/internal/apijson"
- "github.com/CASParser/cas-parser-go/internal/requestconfig"
"github.com/CASParser/cas-parser-go/option"
- "github.com/CASParser/cas-parser-go/packages/param"
- "github.com/CASParser/cas-parser-go/packages/respjson"
)
// CasGeneratorService contains methods and other services that help with
@@ -31,69 +24,3 @@ func NewCasGeneratorService(opts ...option.RequestOption) (r CasGeneratorService
r.Options = opts
return
}
-
-// This endpoint generates CAS (Consolidated Account Statement) documents by
-// submitting a mailback request to the specified CAS authority. Currently only
-// supports KFintech, with plans to support CAMS, CDSL, and NSDL in the future.
-func (r *CasGeneratorService) GenerateCas(ctx context.Context, body CasGeneratorGenerateCasParams, opts ...option.RequestOption) (res *CasGeneratorGenerateCasResponse, err error) {
- opts = append(r.Options[:], opts...)
- path := "v4/generate"
- err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
- return
-}
-
-type CasGeneratorGenerateCasResponse struct {
- Msg string `json:"msg"`
- Status string `json:"status"`
- // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
- JSON struct {
- Msg respjson.Field
- Status respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
- } `json:"-"`
-}
-
-// Returns the unmodified JSON received from the API
-func (r CasGeneratorGenerateCasResponse) RawJSON() string { return r.JSON.raw }
-func (r *CasGeneratorGenerateCasResponse) UnmarshalJSON(data []byte) error {
- return apijson.UnmarshalRoot(data, r)
-}
-
-type CasGeneratorGenerateCasParams struct {
- // Email address to receive the CAS document
- Email string `json:"email,required"`
- // Start date for the CAS period (format YYYY-MM-DD)
- FromDate string `json:"from_date,required"`
- // Password to protect the generated CAS PDF
- Password string `json:"password,required"`
- // End date for the CAS period (format YYYY-MM-DD)
- ToDate string `json:"to_date,required"`
- // PAN number (optional for some CAS authorities)
- PanNo param.Opt[string] `json:"pan_no,omitzero"`
- // CAS authority to generate the document from (currently only kfintech is
- // supported)
- //
- // Any of "kfintech", "cams", "cdsl", "nsdl".
- CasAuthority CasGeneratorGenerateCasParamsCasAuthority `json:"cas_authority,omitzero"`
- paramObj
-}
-
-func (r CasGeneratorGenerateCasParams) MarshalJSON() (data []byte, err error) {
- type shadow CasGeneratorGenerateCasParams
- return param.MarshalObject(r, (*shadow)(&r))
-}
-func (r *CasGeneratorGenerateCasParams) UnmarshalJSON(data []byte) error {
- return apijson.UnmarshalRoot(data, r)
-}
-
-// CAS authority to generate the document from (currently only kfintech is
-// supported)
-type CasGeneratorGenerateCasParamsCasAuthority string
-
-const (
- CasGeneratorGenerateCasParamsCasAuthorityKfintech CasGeneratorGenerateCasParamsCasAuthority = "kfintech"
- CasGeneratorGenerateCasParamsCasAuthorityCams CasGeneratorGenerateCasParamsCasAuthority = "cams"
- CasGeneratorGenerateCasParamsCasAuthorityCdsl CasGeneratorGenerateCasParamsCasAuthority = "cdsl"
- CasGeneratorGenerateCasParamsCasAuthorityNsdl CasGeneratorGenerateCasParamsCasAuthority = "nsdl"
-)
diff --git a/casgenerator_test.go b/casgenerator_test.go
deleted file mode 100644
index e093e18..0000000
--- a/casgenerator_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-package casparser_test
-
-import (
- "context"
- "errors"
- "os"
- "testing"
-
- "github.com/CASParser/cas-parser-go"
- "github.com/CASParser/cas-parser-go/internal/testutil"
- "github.com/CASParser/cas-parser-go/option"
-)
-
-func TestCasGeneratorGenerateCasWithOptionalParams(t *testing.T) {
- t.Skip("Prism tests are disabled")
- baseURL := "http://localhost:4010"
- if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
- baseURL = envURL
- }
- if !testutil.CheckTestServer(t, baseURL) {
- return
- }
- client := casparser.NewClient(
- option.WithBaseURL(baseURL),
- option.WithAPIKey("My API Key"),
- )
- _, err := client.CasGenerator.GenerateCas(context.TODO(), casparser.CasGeneratorGenerateCasParams{
- Email: "user@example.com",
- FromDate: "2023-01-01",
- Password: "Abcdefghi12$",
- ToDate: "2023-12-31",
- CasAuthority: casparser.CasGeneratorGenerateCasParamsCasAuthorityKfintech,
- PanNo: casparser.String("ABCDE1234F"),
- })
- if err != nil {
- var apierr *casparser.Error
- if errors.As(err, &apierr) {
- t.Log(string(apierr.DumpRequest(true)))
- }
- t.Fatalf("err should be nil: %s", err.Error())
- }
-}
diff --git a/casparser.go b/casparser.go
index 66e6cca..577c26f 100644
--- a/casparser.go
+++ b/casparser.go
@@ -5,6 +5,7 @@ package casparser
import (
"context"
"net/http"
+ "slices"
"time"
"github.com/CASParser/cas-parser-go/internal/apijson"
@@ -37,7 +38,7 @@ func NewCasParserService(opts ...option.RequestOption) (r CasParserService) {
// Statement) PDF files and returns data in a unified format. Use this endpoint
// when you know the PDF is from CAMS or KFintech.
func (r *CasParserService) CamsKfintech(ctx context.Context, body CasParserCamsKfintechParams, opts ...option.RequestOption) (res *UnifiedResponse, err error) {
- opts = append(r.Options[:], opts...)
+ opts = slices.Concat(r.Options, opts)
path := "v4/cams_kfintech/parse"
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
return
@@ -47,7 +48,7 @@ func (r *CasParserService) CamsKfintech(ctx context.Context, body CasParserCamsK
// files and returns data in a unified format. Use this endpoint when you know the
// PDF is from CDSL.
func (r *CasParserService) Cdsl(ctx context.Context, body CasParserCdslParams, opts ...option.RequestOption) (res *UnifiedResponse, err error) {
- opts = append(r.Options[:], opts...)
+ opts = slices.Concat(r.Options, opts)
path := "v4/cdsl/parse"
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
return
@@ -57,7 +58,7 @@ func (r *CasParserService) Cdsl(ctx context.Context, body CasParserCdslParams, o
// files and returns data in a unified format. Use this endpoint when you know the
// PDF is from NSDL.
func (r *CasParserService) Nsdl(ctx context.Context, body CasParserNsdlParams, opts ...option.RequestOption) (res *UnifiedResponse, err error) {
- opts = append(r.Options[:], opts...)
+ opts = slices.Concat(r.Options, opts)
path := "v4/nsdl/parse"
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
return
@@ -68,7 +69,7 @@ func (r *CasParserService) Nsdl(ctx context.Context, body CasParserNsdlParams, o
// CAS type and transforms the data into a consistent structure regardless of the
// source.
func (r *CasParserService) SmartParse(ctx context.Context, body CasParserSmartParseParams, opts ...option.RequestOption) (res *UnifiedResponse, err error) {
- opts = append(r.Options[:], opts...)
+ opts = slices.Concat(r.Options, opts)
path := "v4/smart/parse"
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
return
@@ -80,7 +81,9 @@ type UnifiedResponse struct {
Investor UnifiedResponseInvestor `json:"investor"`
Meta UnifiedResponseMeta `json:"meta"`
MutualFunds []UnifiedResponseMutualFund `json:"mutual_funds"`
- Summary UnifiedResponseSummary `json:"summary"`
+ // List of NPS accounts
+ Nps []UnifiedResponseNp `json:"nps"`
+ Summary UnifiedResponseSummary `json:"summary"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
DematAccounts respjson.Field
@@ -88,6 +91,7 @@ type UnifiedResponse struct {
Investor respjson.Field
Meta respjson.Field
MutualFunds respjson.Field
+ Nps respjson.Field
Summary respjson.Field
ExtraFields map[string]respjson.Field
raw string
@@ -116,6 +120,8 @@ type UnifiedResponseDematAccount struct {
// Depository Participant name
DpName string `json:"dp_name"`
Holdings UnifiedResponseDematAccountHoldings `json:"holdings"`
+ // List of account holders linked to this demat account
+ LinkedHolders []UnifiedResponseDematAccountLinkedHolder `json:"linked_holders"`
// Total value of the demat account
Value float64 `json:"value"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
@@ -127,6 +133,7 @@ type UnifiedResponseDematAccount struct {
DpID respjson.Field
DpName respjson.Field
Holdings respjson.Field
+ LinkedHolders respjson.Field
Value respjson.Field
ExtraFields map[string]respjson.Field
raw string
@@ -204,11 +211,13 @@ func (r *UnifiedResponseDematAccountHoldings) UnmarshalJSON(data []byte) error {
type UnifiedResponseDematAccountHoldingsAif struct {
// Additional information specific to the AIF
- AdditionalInfo any `json:"additional_info"`
+ AdditionalInfo UnifiedResponseDematAccountHoldingsAifAdditionalInfo `json:"additional_info"`
// ISIN code of the AIF
Isin string `json:"isin"`
// Name of the AIF
Name string `json:"name"`
+ // List of transactions for this holding (beta)
+ Transactions []UnifiedResponseDematAccountHoldingsAifTransaction `json:"transactions"`
// Number of units held
Units float64 `json:"units"`
// Current market value of the holding
@@ -218,6 +227,7 @@ type UnifiedResponseDematAccountHoldingsAif struct {
AdditionalInfo respjson.Field
Isin respjson.Field
Name respjson.Field
+ Transactions respjson.Field
Units respjson.Field
Value respjson.Field
ExtraFields map[string]respjson.Field
@@ -231,13 +241,125 @@ func (r *UnifiedResponseDematAccountHoldingsAif) UnmarshalJSON(data []byte) erro
return apijson.UnmarshalRoot(data, r)
}
+// Additional information specific to the AIF
+type UnifiedResponseDematAccountHoldingsAifAdditionalInfo struct {
+ // Closing balance units for the statement period (beta)
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period (beta)
+ OpenUnits float64 `json:"open_units,nullable"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CloseUnits respjson.Field
+ OpenUnits respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsAifAdditionalInfo) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseDematAccountHoldingsAifAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
+type UnifiedResponseDematAccountHoldingsAifTransaction struct {
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseDematAccountHoldingsAifTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
+ // Balance units after transaction
+ Balance float64 `json:"balance"`
+ // Transaction date (YYYY-MM-DD)
+ Date time.Time `json:"date" format:"date"`
+ // Transaction description/particulars
+ Description string `json:"description"`
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
+ Type string `json:"type"`
+ // Number of units involved in transaction
+ Units float64 `json:"units"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsAifTransaction) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseDematAccountHoldingsAifTransaction) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseDematAccountHoldingsAifTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsAifTransactionAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsAifTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type UnifiedResponseDematAccountHoldingsCorporateBond struct {
// Additional information specific to the corporate bond
- AdditionalInfo any `json:"additional_info"`
+ AdditionalInfo UnifiedResponseDematAccountHoldingsCorporateBondAdditionalInfo `json:"additional_info"`
// ISIN code of the corporate bond
Isin string `json:"isin"`
// Name of the corporate bond
Name string `json:"name"`
+ // List of transactions for this holding (beta)
+ Transactions []UnifiedResponseDematAccountHoldingsCorporateBondTransaction `json:"transactions"`
// Number of units held
Units float64 `json:"units"`
// Current market value of the holding
@@ -247,6 +369,7 @@ type UnifiedResponseDematAccountHoldingsCorporateBond struct {
AdditionalInfo respjson.Field
Isin respjson.Field
Name respjson.Field
+ Transactions respjson.Field
Units respjson.Field
Value respjson.Field
ExtraFields map[string]respjson.Field
@@ -260,13 +383,129 @@ func (r *UnifiedResponseDematAccountHoldingsCorporateBond) UnmarshalJSON(data []
return apijson.UnmarshalRoot(data, r)
}
+// Additional information specific to the corporate bond
+type UnifiedResponseDematAccountHoldingsCorporateBondAdditionalInfo struct {
+ // Closing balance units for the statement period (beta)
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period (beta)
+ OpenUnits float64 `json:"open_units,nullable"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CloseUnits respjson.Field
+ OpenUnits respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsCorporateBondAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsCorporateBondAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
+type UnifiedResponseDematAccountHoldingsCorporateBondTransaction struct {
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseDematAccountHoldingsCorporateBondTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
+ // Balance units after transaction
+ Balance float64 `json:"balance"`
+ // Transaction date (YYYY-MM-DD)
+ Date time.Time `json:"date" format:"date"`
+ // Transaction description/particulars
+ Description string `json:"description"`
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
+ Type string `json:"type"`
+ // Number of units involved in transaction
+ Units float64 `json:"units"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsCorporateBondTransaction) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsCorporateBondTransaction) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseDematAccountHoldingsCorporateBondTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsCorporateBondTransactionAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsCorporateBondTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type UnifiedResponseDematAccountHoldingsDematMutualFund struct {
// Additional information specific to the mutual fund
- AdditionalInfo any `json:"additional_info"`
+ AdditionalInfo UnifiedResponseDematAccountHoldingsDematMutualFundAdditionalInfo `json:"additional_info"`
// ISIN code of the mutual fund
Isin string `json:"isin"`
// Name of the mutual fund
Name string `json:"name"`
+ // List of transactions for this holding (beta)
+ Transactions []UnifiedResponseDematAccountHoldingsDematMutualFundTransaction `json:"transactions"`
// Number of units held
Units float64 `json:"units"`
// Current market value of the holding
@@ -276,6 +515,7 @@ type UnifiedResponseDematAccountHoldingsDematMutualFund struct {
AdditionalInfo respjson.Field
Isin respjson.Field
Name respjson.Field
+ Transactions respjson.Field
Units respjson.Field
Value respjson.Field
ExtraFields map[string]respjson.Field
@@ -289,13 +529,129 @@ func (r *UnifiedResponseDematAccountHoldingsDematMutualFund) UnmarshalJSON(data
return apijson.UnmarshalRoot(data, r)
}
+// Additional information specific to the mutual fund
+type UnifiedResponseDematAccountHoldingsDematMutualFundAdditionalInfo struct {
+ // Closing balance units for the statement period (beta)
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period (beta)
+ OpenUnits float64 `json:"open_units,nullable"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CloseUnits respjson.Field
+ OpenUnits respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsDematMutualFundAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsDematMutualFundAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
+type UnifiedResponseDematAccountHoldingsDematMutualFundTransaction struct {
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseDematAccountHoldingsDematMutualFundTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
+ // Balance units after transaction
+ Balance float64 `json:"balance"`
+ // Transaction date (YYYY-MM-DD)
+ Date time.Time `json:"date" format:"date"`
+ // Transaction description/particulars
+ Description string `json:"description"`
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
+ Type string `json:"type"`
+ // Number of units involved in transaction
+ Units float64 `json:"units"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsDematMutualFundTransaction) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsDematMutualFundTransaction) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseDematAccountHoldingsDematMutualFundTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsDematMutualFundTransactionAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsDematMutualFundTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type UnifiedResponseDematAccountHoldingsEquity struct {
// Additional information specific to the equity
- AdditionalInfo any `json:"additional_info"`
+ AdditionalInfo UnifiedResponseDematAccountHoldingsEquityAdditionalInfo `json:"additional_info"`
// ISIN code of the equity
Isin string `json:"isin"`
// Name of the equity
Name string `json:"name"`
+ // List of transactions for this holding (beta)
+ Transactions []UnifiedResponseDematAccountHoldingsEquityTransaction `json:"transactions"`
// Number of units held
Units float64 `json:"units"`
// Current market value of the holding
@@ -305,6 +661,7 @@ type UnifiedResponseDematAccountHoldingsEquity struct {
AdditionalInfo respjson.Field
Isin respjson.Field
Name respjson.Field
+ Transactions respjson.Field
Units respjson.Field
Value respjson.Field
ExtraFields map[string]respjson.Field
@@ -318,13 +675,125 @@ func (r *UnifiedResponseDematAccountHoldingsEquity) UnmarshalJSON(data []byte) e
return apijson.UnmarshalRoot(data, r)
}
+// Additional information specific to the equity
+type UnifiedResponseDematAccountHoldingsEquityAdditionalInfo struct {
+ // Closing balance units for the statement period (beta)
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period (beta)
+ OpenUnits float64 `json:"open_units,nullable"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CloseUnits respjson.Field
+ OpenUnits respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsEquityAdditionalInfo) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseDematAccountHoldingsEquityAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
+type UnifiedResponseDematAccountHoldingsEquityTransaction struct {
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseDematAccountHoldingsEquityTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
+ // Balance units after transaction
+ Balance float64 `json:"balance"`
+ // Transaction date (YYYY-MM-DD)
+ Date time.Time `json:"date" format:"date"`
+ // Transaction description/particulars
+ Description string `json:"description"`
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
+ Type string `json:"type"`
+ // Number of units involved in transaction
+ Units float64 `json:"units"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsEquityTransaction) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseDematAccountHoldingsEquityTransaction) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseDematAccountHoldingsEquityTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsEquityTransactionAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsEquityTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type UnifiedResponseDematAccountHoldingsGovernmentSecurity struct {
// Additional information specific to the government security
- AdditionalInfo any `json:"additional_info"`
+ AdditionalInfo UnifiedResponseDematAccountHoldingsGovernmentSecurityAdditionalInfo `json:"additional_info"`
// ISIN code of the government security
Isin string `json:"isin"`
// Name of the government security
Name string `json:"name"`
+ // List of transactions for this holding (beta)
+ Transactions []UnifiedResponseDematAccountHoldingsGovernmentSecurityTransaction `json:"transactions"`
// Number of units held
Units float64 `json:"units"`
// Current market value of the holding
@@ -334,6 +803,7 @@ type UnifiedResponseDematAccountHoldingsGovernmentSecurity struct {
AdditionalInfo respjson.Field
Isin respjson.Field
Name respjson.Field
+ Transactions respjson.Field
Units respjson.Field
Value respjson.Field
ExtraFields map[string]respjson.Field
@@ -347,6 +817,140 @@ func (r *UnifiedResponseDematAccountHoldingsGovernmentSecurity) UnmarshalJSON(da
return apijson.UnmarshalRoot(data, r)
}
+// Additional information specific to the government security
+type UnifiedResponseDematAccountHoldingsGovernmentSecurityAdditionalInfo struct {
+ // Closing balance units for the statement period (beta)
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period (beta)
+ OpenUnits float64 `json:"open_units,nullable"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CloseUnits respjson.Field
+ OpenUnits respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsGovernmentSecurityAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsGovernmentSecurityAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
+type UnifiedResponseDematAccountHoldingsGovernmentSecurityTransaction struct {
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseDematAccountHoldingsGovernmentSecurityTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
+ // Balance units after transaction
+ Balance float64 `json:"balance"`
+ // Transaction date (YYYY-MM-DD)
+ Date time.Time `json:"date" format:"date"`
+ // Transaction description/particulars
+ Description string `json:"description"`
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
+ Type string `json:"type"`
+ // Number of units involved in transaction
+ Units float64 `json:"units"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsGovernmentSecurityTransaction) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsGovernmentSecurityTransaction) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseDematAccountHoldingsGovernmentSecurityTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsGovernmentSecurityTransactionAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsGovernmentSecurityTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+type UnifiedResponseDematAccountLinkedHolder struct {
+ // Name of the account holder
+ Name string `json:"name"`
+ // PAN of the account holder
+ Pan string `json:"pan"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Name respjson.Field
+ Pan respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountLinkedHolder) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseDematAccountLinkedHolder) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type UnifiedResponseInsurance struct {
LifeInsurancePolicies []UnifiedResponseInsuranceLifeInsurancePolicy `json:"life_insurance_policies"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
@@ -490,6 +1094,8 @@ type UnifiedResponseMutualFund struct {
Amc string `json:"amc"`
// Folio number
FolioNumber string `json:"folio_number"`
+ // List of account holders linked to this mutual fund folio
+ LinkedHolders []UnifiedResponseMutualFundLinkedHolder `json:"linked_holders"`
// Registrar and Transfer Agent name
Registrar string `json:"registrar"`
Schemes []UnifiedResponseMutualFundScheme `json:"schemes"`
@@ -500,6 +1106,7 @@ type UnifiedResponseMutualFund struct {
AdditionalInfo respjson.Field
Amc respjson.Field
FolioNumber respjson.Field
+ LinkedHolders respjson.Field
Registrar respjson.Field
Schemes respjson.Field
Value respjson.Field
@@ -538,6 +1145,26 @@ func (r *UnifiedResponseMutualFundAdditionalInfo) UnmarshalJSON(data []byte) err
return apijson.UnmarshalRoot(data, r)
}
+type UnifiedResponseMutualFundLinkedHolder struct {
+ // Name of the account holder
+ Name string `json:"name"`
+ // PAN of the account holder
+ Pan string `json:"pan"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Name respjson.Field
+ Pan respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseMutualFundLinkedHolder) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseMutualFundLinkedHolder) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type UnifiedResponseMutualFundScheme struct {
// Additional information specific to the scheme
AdditionalInfo UnifiedResponseMutualFundSchemeAdditionalInfo `json:"additional_info"`
@@ -591,10 +1218,10 @@ type UnifiedResponseMutualFundSchemeAdditionalInfo struct {
Advisor string `json:"advisor"`
// AMFI code for the scheme (CAMS/KFintech)
Amfi string `json:"amfi"`
- // Closing balance units (CAMS/KFintech)
- CloseUnits float64 `json:"close_units"`
- // Opening balance units (CAMS/KFintech)
- OpenUnits float64 `json:"open_units"`
+ // Closing balance units for the statement period
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period
+ OpenUnits float64 `json:"open_units,nullable"`
// RTA code for the scheme (CAMS/KFintech)
RtaCode string `json:"rta_code"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
@@ -635,38 +1262,48 @@ func (r *UnifiedResponseMutualFundSchemeGain) UnmarshalJSON(data []byte) error {
return apijson.UnmarshalRoot(data, r)
}
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
type UnifiedResponseMutualFundSchemeTransaction struct {
- // Transaction amount
- Amount float64 `json:"amount"`
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseMutualFundSchemeTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
// Balance units after transaction
Balance float64 `json:"balance"`
- // Transaction date
+ // Transaction date (YYYY-MM-DD)
Date time.Time `json:"date" format:"date"`
- // Transaction description
+ // Transaction description/particulars
Description string `json:"description"`
- // Dividend rate (for dividend transactions)
- DividendRate float64 `json:"dividend_rate"`
- // NAV on transaction date
- Nav float64 `json:"nav"`
- // Transaction type detected based on description. Possible values are
- // PURCHASE,PURCHASE_SIP,REDEMPTION,SWITCH_IN,SWITCH_IN_MERGER,SWITCH_OUT,SWITCH_OUT_MERGER,DIVIDEND_PAYOUT,DIVIDEND_REINVESTMENT,SEGREGATION,STAMP_DUTY_TAX,TDS_TAX,STT_TAX,MISC.
- // If dividend_rate is present, then possible values are dividend_rate is
- // applicable only for DIVIDEND_PAYOUT and DIVIDEND_REINVESTMENT.
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
Type string `json:"type"`
- // Number of units involved
+ // Number of units involved in transaction
Units float64 `json:"units"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
- Amount respjson.Field
- Balance respjson.Field
- Date respjson.Field
- Description respjson.Field
- DividendRate respjson.Field
- Nav respjson.Field
- Type respjson.Field
- Units respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
} `json:"-"`
}
@@ -676,6 +1313,148 @@ func (r *UnifiedResponseMutualFundSchemeTransaction) UnmarshalJSON(data []byte)
return apijson.UnmarshalRoot(data, r)
}
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseMutualFundSchemeTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseMutualFundSchemeTransactionAdditionalInfo) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseMutualFundSchemeTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+type UnifiedResponseNp struct {
+ // Additional information specific to the NPS account
+ AdditionalInfo any `json:"additional_info"`
+ // Central Record Keeping Agency name
+ Cra string `json:"cra"`
+ Funds []UnifiedResponseNpFund `json:"funds"`
+ // List of account holders linked to this NPS account
+ LinkedHolders []UnifiedResponseNpLinkedHolder `json:"linked_holders"`
+ // Permanent Retirement Account Number (PRAN)
+ Pran string `json:"pran"`
+ // Total value of the NPS account
+ Value float64 `json:"value"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Cra respjson.Field
+ Funds respjson.Field
+ LinkedHolders respjson.Field
+ Pran respjson.Field
+ Value respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseNp) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseNp) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+type UnifiedResponseNpFund struct {
+ // Additional information specific to the NPS fund
+ AdditionalInfo UnifiedResponseNpFundAdditionalInfo `json:"additional_info"`
+ // Cost of investment
+ Cost float64 `json:"cost"`
+ // Name of the NPS fund
+ Name string `json:"name"`
+ // Net Asset Value per unit
+ Nav float64 `json:"nav"`
+ // Number of units held
+ Units float64 `json:"units"`
+ // Current market value of the holding
+ Value float64 `json:"value"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Cost respjson.Field
+ Name respjson.Field
+ Nav respjson.Field
+ Units respjson.Field
+ Value respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseNpFund) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseNpFund) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Additional information specific to the NPS fund
+type UnifiedResponseNpFundAdditionalInfo struct {
+ // Fund manager name
+ Manager string `json:"manager"`
+ // NPS tier (Tier I or Tier II)
+ //
+ // Any of 1, 2.
+ Tier float64 `json:"tier,nullable"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Manager respjson.Field
+ Tier respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseNpFundAdditionalInfo) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseNpFundAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+type UnifiedResponseNpLinkedHolder struct {
+ // Name of the account holder
+ Name string `json:"name"`
+ // PAN of the account holder
+ Pan string `json:"pan"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Name respjson.Field
+ Pan respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseNpLinkedHolder) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseNpLinkedHolder) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type UnifiedResponseSummary struct {
Accounts UnifiedResponseSummaryAccounts `json:"accounts"`
// Total portfolio value across all accounts
@@ -699,11 +1478,13 @@ type UnifiedResponseSummaryAccounts struct {
Demat UnifiedResponseSummaryAccountsDemat `json:"demat"`
Insurance UnifiedResponseSummaryAccountsInsurance `json:"insurance"`
MutualFunds UnifiedResponseSummaryAccountsMutualFunds `json:"mutual_funds"`
+ Nps UnifiedResponseSummaryAccountsNps `json:"nps"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
Demat respjson.Field
Insurance respjson.Field
MutualFunds respjson.Field
+ Nps respjson.Field
ExtraFields map[string]respjson.Field
raw string
} `json:"-"`
@@ -775,12 +1556,32 @@ func (r *UnifiedResponseSummaryAccountsMutualFunds) UnmarshalJSON(data []byte) e
return apijson.UnmarshalRoot(data, r)
}
+type UnifiedResponseSummaryAccountsNps struct {
+ // Number of NPS accounts
+ Count int64 `json:"count"`
+ // Total value of NPS accounts
+ TotalValue float64 `json:"total_value"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Count respjson.Field
+ TotalValue respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseSummaryAccountsNps) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseSummaryAccountsNps) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type CasParserCamsKfintechParams struct {
// Password for the PDF file (if required)
Password param.Opt[string] `json:"password,omitzero"`
- // Base64 encoded CAS PDF file
+ // Base64 encoded CAS PDF file (required if pdf_url not provided)
PdfFile param.Opt[string] `json:"pdf_file,omitzero" format:"base64"`
- // URL to the CAS PDF file
+ // URL to the CAS PDF file (required if pdf_file not provided)
PdfURL param.Opt[string] `json:"pdf_url,omitzero" format:"uri"`
paramObj
}
@@ -796,9 +1597,9 @@ func (r *CasParserCamsKfintechParams) UnmarshalJSON(data []byte) error {
type CasParserCdslParams struct {
// Password for the PDF file (if required)
Password param.Opt[string] `json:"password,omitzero"`
- // Base64 encoded CAS PDF file
+ // Base64 encoded CAS PDF file (required if pdf_url not provided)
PdfFile param.Opt[string] `json:"pdf_file,omitzero" format:"base64"`
- // URL to the CAS PDF file
+ // URL to the CAS PDF file (required if pdf_file not provided)
PdfURL param.Opt[string] `json:"pdf_url,omitzero" format:"uri"`
paramObj
}
@@ -814,9 +1615,9 @@ func (r *CasParserCdslParams) UnmarshalJSON(data []byte) error {
type CasParserNsdlParams struct {
// Password for the PDF file (if required)
Password param.Opt[string] `json:"password,omitzero"`
- // Base64 encoded CAS PDF file
+ // Base64 encoded CAS PDF file (required if pdf_url not provided)
PdfFile param.Opt[string] `json:"pdf_file,omitzero" format:"base64"`
- // URL to the CAS PDF file
+ // URL to the CAS PDF file (required if pdf_file not provided)
PdfURL param.Opt[string] `json:"pdf_url,omitzero" format:"uri"`
paramObj
}
@@ -832,9 +1633,9 @@ func (r *CasParserNsdlParams) UnmarshalJSON(data []byte) error {
type CasParserSmartParseParams struct {
// Password for the PDF file (if required)
Password param.Opt[string] `json:"password,omitzero"`
- // Base64 encoded CAS PDF file
+ // Base64 encoded CAS PDF file (required if pdf_url not provided)
PdfFile param.Opt[string] `json:"pdf_file,omitzero" format:"base64"`
- // URL to the CAS PDF file
+ // URL to the CAS PDF file (required if pdf_file not provided)
PdfURL param.Opt[string] `json:"pdf_url,omitzero" format:"uri"`
paramObj
}
diff --git a/client.go b/client.go
index a757680..ab47a50 100644
--- a/client.go
+++ b/client.go
@@ -6,6 +6,7 @@ import (
"context"
"net/http"
"os"
+ "slices"
"github.com/CASParser/cas-parser-go/internal/requestconfig"
"github.com/CASParser/cas-parser-go/option"
@@ -80,7 +81,7 @@ func NewClient(opts ...option.RequestOption) (r Client) {
// For even greater flexibility, see [option.WithResponseInto] and
// [option.WithResponseBodyInto].
func (r *Client) Execute(ctx context.Context, method string, path string, params any, res any, opts ...option.RequestOption) error {
- opts = append(r.Options, opts...)
+ opts = slices.Concat(r.Options, opts)
return requestconfig.ExecuteNewRequest(ctx, method, path, params, res, opts...)
}
diff --git a/go.mod b/go.mod
index d01a0e4..967552e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,9 +1,9 @@
module github.com/CASParser/cas-parser-go
-go 1.21
+go 1.22
require (
- github.com/tidwall/gjson v1.14.4
+ github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
)
diff --git a/go.sum b/go.sum
index a70a5e0..32ba293 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,6 @@
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
-github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
diff --git a/internal/apiform/encoder.go b/internal/apiform/encoder.go
index 82f21a7..c2bfd45 100644
--- a/internal/apiform/encoder.go
+++ b/internal/apiform/encoder.go
@@ -60,6 +60,7 @@ type encoderField struct {
type encoderEntry struct {
reflect.Type
dateFormat string
+ arrayFmt string
root bool
}
@@ -77,6 +78,7 @@ func (e *encoder) typeEncoder(t reflect.Type) encoderFunc {
entry := encoderEntry{
Type: t,
dateFormat: e.dateFormat,
+ arrayFmt: e.arrayFmt,
root: e.root,
}
@@ -178,34 +180,9 @@ func (e *encoder) newPrimitiveTypeEncoder(t reflect.Type) encoderFunc {
}
}
-func arrayKeyEncoder(arrayFmt string) func(string, int) string {
- var keyFn func(string, int) string
- switch arrayFmt {
- case "comma", "repeat":
- keyFn = func(k string, _ int) string { return k }
- case "brackets":
- keyFn = func(key string, _ int) string { return key + "[]" }
- case "indices:dots":
- keyFn = func(k string, i int) string {
- if k == "" {
- return strconv.Itoa(i)
- }
- return k + "." + strconv.Itoa(i)
- }
- case "indices:brackets":
- keyFn = func(k string, i int) string {
- if k == "" {
- return strconv.Itoa(i)
- }
- return k + "[" + strconv.Itoa(i) + "]"
- }
- }
- return keyFn
-}
-
func (e *encoder) newArrayTypeEncoder(t reflect.Type) encoderFunc {
itemEncoder := e.typeEncoder(t.Elem())
- keyFn := arrayKeyEncoder(e.arrayFmt)
+ keyFn := e.arrayKeyEncoder()
return func(key string, v reflect.Value, writer *multipart.Writer) error {
if keyFn == nil {
return fmt.Errorf("apiform: unsupported array format")
@@ -303,13 +280,10 @@ func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc {
})
return func(key string, value reflect.Value, writer *multipart.Writer) error {
- if key != "" {
- key = key + "."
- }
-
+ keyFn := e.objKeyEncoder(key)
for _, ef := range encoderFields {
field := value.FieldByIndex(ef.idx)
- err := ef.fn(key+ef.tag.name, field, writer)
+ err := ef.fn(keyFn(ef.tag.name), field, writer)
if err != nil {
return err
}
@@ -405,6 +379,43 @@ func (e *encoder) newReaderTypeEncoder() encoderFunc {
}
}
+func (e encoder) arrayKeyEncoder() func(string, int) string {
+ var keyFn func(string, int) string
+ switch e.arrayFmt {
+ case "comma", "repeat":
+ keyFn = func(k string, _ int) string { return k }
+ case "brackets":
+ keyFn = func(key string, _ int) string { return key + "[]" }
+ case "indices:dots":
+ keyFn = func(k string, i int) string {
+ if k == "" {
+ return strconv.Itoa(i)
+ }
+ return k + "." + strconv.Itoa(i)
+ }
+ case "indices:brackets":
+ keyFn = func(k string, i int) string {
+ if k == "" {
+ return strconv.Itoa(i)
+ }
+ return k + "[" + strconv.Itoa(i) + "]"
+ }
+ }
+ return keyFn
+}
+
+func (e encoder) objKeyEncoder(parent string) func(string) string {
+ if parent == "" {
+ return func(child string) string { return child }
+ }
+ switch e.arrayFmt {
+ case "brackets":
+ return func(child string) string { return parent + "[" + child + "]" }
+ default:
+ return func(child string) string { return parent + "." + child }
+ }
+}
+
// Given a []byte of json (may either be an empty object or an object that already contains entries)
// encode all of the entries in the map to the json byte array.
func (e *encoder) encodeMapEntries(key string, v reflect.Value, writer *multipart.Writer) error {
@@ -413,10 +424,6 @@ func (e *encoder) encodeMapEntries(key string, v reflect.Value, writer *multipar
value reflect.Value
}
- if key != "" {
- key = key + "."
- }
-
pairs := []mapPair{}
iter := v.MapRange()
@@ -434,8 +441,9 @@ func (e *encoder) encodeMapEntries(key string, v reflect.Value, writer *multipar
})
elementEncoder := e.typeEncoder(v.Type().Elem())
+ keyFn := e.objKeyEncoder(key)
for _, p := range pairs {
- err := elementEncoder(key+string(p.key), p.value, writer)
+ err := elementEncoder(keyFn(p.key), p.value, writer)
if err != nil {
return err
}
diff --git a/internal/apiform/form_test.go b/internal/apiform/form_test.go
index 227e50a..a660e74 100644
--- a/internal/apiform/form_test.go
+++ b/internal/apiform/form_test.go
@@ -123,6 +123,18 @@ type StructUnion struct {
param.APIUnion
}
+type MultipartMarshalerParent struct {
+ Middle MultipartMarshalerMiddleNext `form:"middle"`
+}
+
+type MultipartMarshalerMiddleNext struct {
+ MiddleNext MultipartMarshalerMiddle `form:"middleNext"`
+}
+
+type MultipartMarshalerMiddle struct {
+ Child int `form:"child"`
+}
+
var tests = map[string]struct {
buf string
val any
@@ -366,6 +378,19 @@ true
},
},
},
+ "recursive_struct,brackets": {
+ `--xxx
+Content-Disposition: form-data; name="child[name]"
+
+Alex
+--xxx
+Content-Disposition: form-data; name="name"
+
+Robert
+--xxx--
+`,
+ Recursive{Name: "Robert", Child: &Recursive{Name: "Alex"}},
+ },
"recursive_struct": {
`--xxx
@@ -529,6 +554,30 @@ Content-Disposition: form-data; name="union"
Union: UnionTime(time.Date(2010, 05, 23, 0, 0, 0, 0, time.UTC)),
},
},
+ "deeply-nested-struct,brackets": {
+ `--xxx
+Content-Disposition: form-data; name="middle[middleNext][child]"
+
+10
+--xxx--
+`,
+ MultipartMarshalerParent{
+ Middle: MultipartMarshalerMiddleNext{
+ MiddleNext: MultipartMarshalerMiddle{
+ Child: 10,
+ },
+ },
+ },
+ },
+ "deeply-nested-map,brackets": {
+ `--xxx
+Content-Disposition: form-data; name="middle[middleNext][child]"
+
+10
+--xxx--
+`,
+ map[string]any{"middle": map[string]any{"middleNext": map[string]any{"child": 10}}},
+ },
}
func TestEncode(t *testing.T) {
@@ -553,7 +602,7 @@ func TestEncode(t *testing.T) {
}
raw := buf.Bytes()
if string(raw) != strings.ReplaceAll(test.buf, "\n", "\r\n") {
- t.Errorf("expected %+#v to serialize to '%s' but got '%s'", test.val, test.buf, string(raw))
+ t.Errorf("expected %+#v to serialize to '%s' but got '%s' (with format %s)", test.val, test.buf, string(raw), arrayFmt)
}
})
}
diff --git a/internal/apijson/encoder.go b/internal/apijson/encoder.go
index 8358a2f..ab7a3c1 100644
--- a/internal/apijson/encoder.go
+++ b/internal/apijson/encoder.go
@@ -16,6 +16,10 @@ import (
var encoders sync.Map // map[encoderEntry]encoderFunc
+// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have
+// special characters that sjson interprets as a path.
+var EscapeSJSONKey = strings.NewReplacer("\\", "\\\\", "|", "\\|", "#", "\\#", "@", "\\@", "*", "\\*", ".", "\\.", ":", "\\:", "?", "\\?").Replace
+
func Marshal(value any) ([]byte, error) {
e := &encoder{dateFormat: time.RFC3339}
return e.marshal(value)
@@ -270,7 +274,7 @@ func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc {
if encoded == nil {
continue
}
- json, err = sjson.SetRawBytes(json, ef.tag.name, encoded)
+ json, err = sjson.SetRawBytes(json, EscapeSJSONKey(ef.tag.name), encoded)
if err != nil {
return nil, err
}
@@ -348,7 +352,7 @@ func (e *encoder) encodeMapEntries(json []byte, v reflect.Value) ([]byte, error)
}
encodedKeyString = string(encodedKeyBytes)
}
- encodedKey := []byte(sjsonReplacer.Replace(encodedKeyString))
+ encodedKey := []byte(encodedKeyString)
pairs = append(pairs, mapPair{key: encodedKey, value: iter.Value()})
}
@@ -366,7 +370,7 @@ func (e *encoder) encodeMapEntries(json []byte, v reflect.Value) ([]byte, error)
if len(encodedValue) == 0 {
continue
}
- json, err = sjson.SetRawBytes(json, string(p.key), encodedValue)
+ json, err = sjson.SetRawBytes(json, EscapeSJSONKey(string(p.key)), encodedValue)
if err != nil {
return nil, err
}
@@ -386,7 +390,3 @@ func (e *encoder) newMapEncoder(_ reflect.Type) encoderFunc {
return json, nil
}
}
-
-// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have
-// special characters that sjson interprets as a path.
-var sjsonReplacer *strings.Replacer = strings.NewReplacer(".", "\\.", ":", "\\:", "*", "\\*")
diff --git a/internal/apijson/enum.go b/internal/apijson/enum.go
index 18b218a..5bef11c 100644
--- a/internal/apijson/enum.go
+++ b/internal/apijson/enum.go
@@ -29,7 +29,7 @@ type validatorFunc func(reflect.Value) exactness
var validators sync.Map
var validationRegistry = map[reflect.Type][]validationEntry{}
-func RegisterFieldValidator[T any, V string | bool | int](fieldName string, values ...V) {
+func RegisterFieldValidator[T any, V string | bool | int | float64](fieldName string, values ...V) {
var t T
parentType := reflect.TypeOf(t)
diff --git a/internal/apijson/union.go b/internal/apijson/union.go
index 87eeb20..7b37be1 100644
--- a/internal/apijson/union.go
+++ b/internal/apijson/union.go
@@ -78,7 +78,7 @@ func (d *decoderBuilder) newStructUnionDecoder(t reflect.Type) decoderFunc {
return func(n gjson.Result, v reflect.Value, state *decoderState) error {
if discriminated && n.Type == gjson.JSON && len(unionEntry.discriminatorKey) != 0 {
- discriminator := n.Get(unionEntry.discriminatorKey).Value()
+ discriminator := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value()
for _, decoder := range discriminatedDecoders {
if discriminator == decoder.discriminator {
inner := v.FieldByIndex(decoder.field.Index)
@@ -162,7 +162,7 @@ func (d *decoderBuilder) newUnionDecoder(t reflect.Type) decoderFunc {
}
if len(unionEntry.discriminatorKey) != 0 {
- discriminatorValue := n.Get(unionEntry.discriminatorKey).Value()
+ discriminatorValue := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value()
if discriminatorValue == variant.DiscriminatorValue {
inner := reflect.New(variant.Type).Elem()
err := decoder(n, inner, state)
diff --git a/internal/encoding/json/shims/shims.go b/internal/encoding/json/shims/shims.go
index b65a016..fe9a71a 100644
--- a/internal/encoding/json/shims/shims.go
+++ b/internal/encoding/json/shims/shims.go
@@ -1,5 +1,5 @@
// This package provides shims over Go 1.2{2,3} APIs
-// which are missing from Go 1.21, and used by the Go 1.24 encoding/json package.
+// which are missing from Go 1.22, and used by the Go 1.24 encoding/json package.
//
// Inside the vendored package, all shim code has comments that begin look like
// // SHIM(...): ...
diff --git a/internal/version.go b/internal/version.go
index b5b3e63..02eac73 100644
--- a/internal/version.go
+++ b/internal/version.go
@@ -2,4 +2,4 @@
package internal
-const PackageVersion = "0.0.3" // x-release-please-version
+const PackageVersion = "0.1.0" // x-release-please-version
diff --git a/packages/param/encoder.go b/packages/param/encoder.go
index 3ec5f41..42f2ae7 100644
--- a/packages/param/encoder.go
+++ b/packages/param/encoder.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"reflect"
+ "strings"
"time"
shimjson "github.com/CASParser/cas-parser-go/internal/encoding/json"
@@ -14,6 +15,10 @@ import (
// EncodedAsDate is not be stable and shouldn't be relied upon
type EncodedAsDate Opt[time.Time]
+// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have
+// special characters that sjson interprets as a path.
+var EscapeSJSONKey = strings.NewReplacer("\\", "\\\\", "|", "\\|", "#", "\\#", "@", "\\@", "*", "\\*", ".", "\\.", ":", "\\:", "?", "\\?").Replace
+
type forceOmit int
func (m EncodedAsDate) MarshalJSON() ([]byte, error) {
@@ -52,7 +57,7 @@ func MarshalWithExtras[T ParamStruct, R any](f T, underlying any, extras map[str
}
continue
}
- bytes, err = sjson.SetBytes(bytes, k, v)
+ bytes, err = sjson.SetBytes(bytes, EscapeSJSONKey(k), v)
if err != nil {
return nil, err
}
diff --git a/packages/param/param.go b/packages/param/param.go
index 3335a9a..dcc3083 100644
--- a/packages/param/param.go
+++ b/packages/param/param.go
@@ -41,6 +41,19 @@ func Override[T ParamStruct, PtrT InferPtr[T]](v any) T {
return *pt
}
+// SetJSON configures a param struct to serialize with the provided raw JSON data.
+// Use this when you have existing JSON that you want to send as request parameters.
+//
+// var req example.NewUserParams
+// var rawJSON = []byte(`{"name": "...", "age": 40}`)
+// param.SetJSON(rawJSON, &req)
+// res, err := client.Users.New(ctx, req)
+//
+// Note: The struct's existing fields will be ignored; only the provided JSON is serialized.
+func SetJSON(rawJSON []byte, ptr anyParamStruct) {
+ ptr.setMetadata(json.RawMessage(rawJSON))
+}
+
// IsOmitted returns true if v is the zero value of its type.
//
// If IsOmitted is true, and the field uses a `json:"...,omitzero"` tag,
@@ -91,6 +104,11 @@ type ParamStruct interface {
extraFields() map[string]any
}
+// A pointer to ParamStruct
+type anyParamStruct interface {
+ setMetadata(any)
+}
+
// This is an implementation detail and should never be explicitly set.
type InferPtr[T ParamStruct] interface {
setMetadata(any)
diff --git a/packages/respjson/respjson.go b/packages/respjson/respjson.go
index cc0088c..9e61c5c 100644
--- a/packages/respjson/respjson.go
+++ b/packages/respjson/respjson.go
@@ -5,7 +5,7 @@ package respjson
// Use [Field.Valid] to check if an optional value was null or omitted.
//
// A Field will always occur in the following structure, where it
-// mirrors the original field in it's parent struct:
+// mirrors the original field in its parent struct:
//
// type ExampleObject struct {
// Foo bool `json:"foo"`
diff --git a/scripts/bootstrap b/scripts/bootstrap
index d6ac165..5ab3066 100755
--- a/scripts/bootstrap
+++ b/scripts/bootstrap
@@ -4,10 +4,18 @@ set -e
cd "$(dirname "$0")/.."
-if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ]; then
+if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then
brew bundle check >/dev/null 2>&1 || {
- echo "==> Installing Homebrew dependencies…"
- brew bundle
+ echo -n "==> Install Homebrew dependencies? (y/N): "
+ read -r response
+ case "$response" in
+ [yY][eE][sS]|[yY])
+ brew bundle
+ ;;
+ *)
+ ;;
+ esac
+ echo
}
fi
diff --git a/usage_test.go b/usage_test.go
index 071d9f8..5b017e4 100644
--- a/usage_test.go
+++ b/usage_test.go
@@ -24,6 +24,7 @@ func TestUsage(t *testing.T) {
option.WithBaseURL(baseURL),
option.WithAPIKey("My API Key"),
)
+ t.Skip("Prism tests are disabled")
unifiedResponse, err := client.CasParser.SmartParse(context.TODO(), casparser.CasParserSmartParseParams{
Password: casparser.String("ABCDF"),
PdfURL: casparser.String("https://your-cas-pdf-url-here.com"),