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..4465de7 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-7e6397bddc220d1a59b5e2c7e7c3ff38f1a6eb174f4e383e03bc49cf78c8c44f.yml +openapi_spec_hash: cb852eeb4ce89c80f4246815cbe21f72 config_hash: cb5d75abef6264b5d86448caf7295afa diff --git a/CHANGELOG.md b/CHANGELOG.md index 077729d..25b6824 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## 0.1.0 (2026-01-05) + +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 ([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..ff985d5 100644 --- a/README.md +++ b/README.md @@ -28,14 +28,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 +128,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..cf396b9 100644 --- a/api.md +++ b/api.md @@ -12,11 +12,3 @@ Methods: - 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..78ebeaa 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,6 +1556,26 @@ 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"` 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/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"),