Author SHA1 Message Date
joe 1fd2dd50ce Add correlation_id to TransactionCreateRequest and TransactionHeader
Callers can set correlation_id when creating transactions to link
related transactions together (e.g. all transactions in a workflow).
The field propagates through the PRIME pipeline and child transactions.
2026-03-31 06:17:05 -05:00
amiller ee3c6941ee Added functions to get API partial credentials 2025-12-29 11:04:45 -05:00
amiller eb6100b736 Add context.Context parameter to all API methods
Enable request timeout and cancellation control by adding context.Context
as the first parameter to all SDK API methods. This allows users to:
- Set per-request timeouts
- Cancel in-flight requests
- Pass request-scoped values
2025-12-29 11:00:13 -05:00
9 changed files with 169 additions and 75 deletions
+29 -20
View File
@@ -14,8 +14,10 @@ go get git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go
package main package main
import ( import (
"context"
"fmt" "fmt"
"log" "log"
"time"
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go" "git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go"
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/models" "git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/models"
@@ -30,13 +32,17 @@ func main() {
"https://your-dragonchain-endpoint.com", "https://your-dragonchain-endpoint.com",
) )
// Create a context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Check system health // Check system health
if err := client.System.Health(); err != nil { if err := client.System.Health(ctx); err != nil {
log.Fatal("Health check failed:", err) log.Fatal("Health check failed:", err)
} }
// Get system status // Get system status
status, err := client.System.Status() status, err := client.System.Status(ctx)
if err != nil { if err != nil {
log.Fatal("Failed to get status:", err) log.Fatal("Failed to get status:", err)
} }
@@ -51,7 +57,7 @@ func main() {
Tag: "example-tag", Tag: "example-tag",
} }
resp, err := client.Transaction.Create(txn) resp, err := client.Transaction.Create(ctx, txn)
if err != nil { if err != nil {
log.Fatal("Failed to create transaction:", err) log.Fatal("Failed to create transaction:", err)
} }
@@ -61,31 +67,34 @@ func main() {
## Available Endpoints ## Available Endpoints
All API methods accept a `context.Context` as their first parameter for timeout and cancellation control.
### System ### System
- `Health()` - Check system health - `Health(ctx)` - Check system health
- `Status()` - Get system status - `Status(ctx)` - Get system status
### Transaction ### Transaction
- `Create(req)` - Create a new transaction - `Create(ctx, req)` - Create a new transaction
- `CreateBulk(req)` - Create multiple transactions - `CreateBulk(ctx, req)` - Create multiple transactions
- `Get(transactionID)` - Get transaction by ID - `Get(ctx, transactionID)` - Get transaction by ID
- `List(ctx)` - List all transactions
### Transaction Type ### Transaction Type
- `Create(req)` - Create a new transaction type - `Create(ctx, req)` - Create a new transaction type
- `Get(txnType)` - Get transaction type by name - `Get(ctx, txnType)` - Get transaction type by name
- `List()` - List all transaction types - `List(ctx)` - List all transaction types
- `Delete(txnType)` - Delete a transaction type - `Delete(ctx, txnType)` - Delete a transaction type
### Smart Contract ### Smart Contract
- `Create(req)` - Create a new smart contract - `Create(ctx, req)` - Create a new smart contract
- `Get(contractID)` - Get smart contract by ID - `Get(ctx, contractID)` - Get smart contract by ID
- `List()` - List all smart contracts - `List(ctx)` - List all smart contracts
- `Update(contractID, req)` - Update a smart contract - `Update(ctx, contractID, req)` - Update a smart contract
- `Upload(contractID, req)` - Upload smart contract code - `Upload(ctx, contractID, filepath)` - Upload smart contract code
- `Delete(contractID)` - Delete a smart contract - `Delete(ctx, contractID)` - Delete a smart contract
### Block ### Block
- `Get(blockID)` - Get block by ID - `Get(ctx, blockID)` - Get block by ID
## Authentication ## Authentication
@@ -93,4 +102,4 @@ The SDK uses HMAC-SHA256 authentication. You need to provide:
- `publicID` - Your Dragonchain public ID - `publicID` - Your Dragonchain public ID
- `authKeyID` - Your authentication key ID - `authKeyID` - Your authentication key ID
- `authKey` - Your authentication key - `authKey` - Your authentication key
- `baseURL` - The base URL of your Dragonchain node (e.g., "https://mychain.dragonchain.com") - `baseURL` - The base URL of your Dragonchain node (e.g., "https://chains.dragonchain.com")
+3 -2
View File
@@ -1,6 +1,7 @@
package block package block
import ( import (
"context"
"fmt" "fmt"
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client" "git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client"
@@ -15,10 +16,10 @@ func NewBlockClient(c *client.Client) *BlockClient {
return &BlockClient{client: c} return &BlockClient{client: c}
} }
func (bc *BlockClient) Get(blockID string) (*models.Block, error) { func (bc *BlockClient) Get(ctx context.Context, blockID string) (*models.Block, error) {
var resp models.Block var resp models.Block
path := fmt.Sprintf("/api/v1/block/%s", blockID) path := fmt.Sprintf("/api/v1/block/%s", blockID)
err := bc.client.Get(path, &resp) err := bc.client.Get(ctx, path, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+27 -10
View File
@@ -2,6 +2,7 @@ package client
import ( import (
"bytes" "bytes"
"context"
"crypto/hmac" "crypto/hmac"
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
@@ -62,7 +63,7 @@ func (c *Client) createHmac(msgStr string) []byte {
return h.Sum(nil) return h.Sum(nil)
} }
func (c *Client) doRequest(method, path, contentType string, body any, response any) error { func (c *Client) doRequest(ctx context.Context, method, path, contentType string, body any, response any) error {
var bodyBytes []byte var bodyBytes []byte
var err error var err error
@@ -83,7 +84,7 @@ func (c *Client) doRequest(method, path, contentType string, body any, response
} }
fullURL := c.baseURL + path fullURL := c.baseURL + path
req, err := http.NewRequest(method, fullURL, bytes.NewBuffer(bodyBytes)) req, err := http.NewRequestWithContext(ctx, method, fullURL, bytes.NewBuffer(bodyBytes))
if err != nil { if err != nil {
return fmt.Errorf("failed to create request: %w", err) return fmt.Errorf("failed to create request: %w", err)
} }
@@ -127,18 +128,34 @@ func checkByteSlice(body interface{}) bool {
return ok return ok
} }
func (c *Client) Get(path string, response any) error { func (c *Client) InternalId() string {
return c.doRequest(http.MethodGet, path, "", nil, response) return c.publicID
} }
func (c *Client) Post(path, contentType string, body any, response any) error { func (c *Client) PublicId() string {
return c.doRequest(http.MethodPost, path, contentType, body, response) return c.publicID
} }
func (c *Client) Put(path, contentType string, body any, response any) error { func (c *Client) AuthKeyId() string {
return c.doRequest(http.MethodPut, path, contentType, body, response) return c.authKeyID
} }
func (c *Client) Delete(path string, response any) error { func (c *Client) Endpoint() string {
return c.doRequest(http.MethodDelete, path, "", nil, response) return c.baseURL
}
func (c *Client) Get(ctx context.Context, path string, response any) error {
return c.doRequest(ctx, http.MethodGet, path, "", nil, response)
}
func (c *Client) Post(ctx context.Context, path, contentType string, body any, response any) error {
return c.doRequest(ctx, http.MethodPost, path, contentType, body, response)
}
func (c *Client) Put(ctx context.Context, path, contentType string, body any, response any) error {
return c.doRequest(ctx, http.MethodPut, path, contentType, body, response)
}
func (c *Client) Delete(ctx context.Context, path string, response any) error {
return c.doRequest(ctx, http.MethodDelete, path, "", nil, response)
} }
+13 -12
View File
@@ -1,6 +1,7 @@
package contract package contract
import ( import (
"context"
"fmt" "fmt"
"os" "os"
@@ -16,45 +17,45 @@ func NewContractClient(c *client.Client) *ContractClient {
return &ContractClient{client: c} return &ContractClient{client: c}
} }
func (cc *ContractClient) Create(req *models.SmartContractCreateRequest) (*models.SmartContract, error) { func (cc *ContractClient) Create(ctx context.Context, req *models.SmartContractCreateRequest) (*models.SmartContract, error) {
var resp models.SmartContract var resp models.SmartContract
err := cc.client.Post("/api/v1/contract", models.ContentTypeJSON, req, &resp) err := cc.client.Post(ctx, "/api/v1/contract", models.ContentTypeJSON, req, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &resp, nil return &resp, nil
} }
func (cc *ContractClient) Get(contractID string) (*models.SmartContract, error) { func (cc *ContractClient) Get(ctx context.Context, contractID string) (*models.SmartContract, error) {
var resp models.SmartContract var resp models.SmartContract
path := fmt.Sprintf("/api/v1/contract/%s", contractID) path := fmt.Sprintf("/api/v1/contract/%s", contractID)
err := cc.client.Get(path, &resp) err := cc.client.Get(ctx, path, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &resp, nil return &resp, nil
} }
func (cc *ContractClient) List() (*models.ListResponse, error) { func (cc *ContractClient) List(ctx context.Context) (*models.ListResponse, error) {
var resp models.ListResponse var resp models.ListResponse
err := cc.client.Get("/api/v1/contract", &resp) err := cc.client.Get(ctx, "/api/v1/contract", &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &resp, nil return &resp, nil
} }
func (cc *ContractClient) Update(contractID string, req *models.SmartContractUpdateRequest) (*models.SuccessResponse, error) { func (cc *ContractClient) Update(ctx context.Context, contractID string, req *models.SmartContractUpdateRequest) (*models.SuccessResponse, error) {
var resp models.SuccessResponse var resp models.SuccessResponse
path := fmt.Sprintf("/api/v1/contract/%s", contractID) path := fmt.Sprintf("/api/v1/contract/%s", contractID)
err := cc.client.Put(path, models.ContentTypeJSON, req, &resp) err := cc.client.Put(ctx, path, models.ContentTypeJSON, req, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &resp, nil return &resp, nil
} }
func (cc *ContractClient) Upload(contractID string, filepath string) (*models.SuccessResponse, error) { func (cc *ContractClient) Upload(ctx context.Context, contractID string, filepath string) (*models.SuccessResponse, error) {
fileContent, err := os.ReadFile(filepath) fileContent, err := os.ReadFile(filepath)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err) return nil, fmt.Errorf("failed to read file: %w", err)
@@ -62,17 +63,17 @@ func (cc *ContractClient) Upload(contractID string, filepath string) (*models.Su
var resp models.SuccessResponse var resp models.SuccessResponse
path := fmt.Sprintf("/api/v1/contract/%s/upload", contractID) path := fmt.Sprintf("/api/v1/contract/%s/upload", contractID)
err = cc.client.Put(path, "application/octet-stream", fileContent, &resp) err = cc.client.Put(ctx, path, "application/octet-stream", fileContent, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &resp, nil return &resp, nil
} }
func (cc *ContractClient) Delete(contractID string) (*models.SuccessResponse, error) { func (cc *ContractClient) Delete(ctx context.Context, contractID string) (*models.SuccessResponse, error) {
var resp models.SuccessResponse var resp models.SuccessResponse
path := fmt.Sprintf("/api/v1/contract/%s", contractID) path := fmt.Sprintf("/api/v1/contract/%s", contractID)
err := cc.client.Delete(path, &resp) err := cc.client.Delete(ctx, path, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+60
View File
@@ -1,3 +1,49 @@
// Package sdk provides a Go SDK for interacting with Dragonchain nodes.
//
// All API methods require a context.Context parameter for timeout and cancellation control.
//
// Example usage:
//
// package main
//
// import (
// "context"
// "fmt"
// "log"
// "time"
//
// "git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go"
// "git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/models"
// )
//
// func main() {
// client := sdk.NewDragonchainSDK(
// "your-public-id",
// "your-auth-key-id",
// "your-auth-key",
// "https://your-dragonchain-endpoint.com",
// )
//
// ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
// defer cancel()
//
// // Check system health
// if err := client.System.Health(ctx); err != nil {
// log.Fatal(err)
// }
//
// // Create a transaction
// txn := &models.TransactionCreateRequest{
// TxnType: "my-transaction-type",
// Payload: map[string]interface{}{"message": "Hello Dragonchain"},
// }
//
// resp, err := client.Transaction.Create(ctx, txn)
// if err != nil {
// log.Fatal(err)
// }
// fmt.Printf("Created transaction: %s\n", resp.TransactionID)
// }
package sdk package sdk
import ( import (
@@ -9,6 +55,8 @@ import (
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/transactiontype" "git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/transactiontype"
) )
// DragonchainSDK is the main SDK client for interacting with Dragonchain nodes.
// It provides access to all API endpoints through specialized client instances.
type DragonchainSDK struct { type DragonchainSDK struct {
client *client.Client client *client.Client
Transaction *transaction.TransactionClient Transaction *transaction.TransactionClient
@@ -18,6 +66,16 @@ type DragonchainSDK struct {
System *system.SystemClient System *system.SystemClient
} }
// NewDragonchainSDK creates a new Dragonchain SDK client.
//
// Parameters:
// - publicID: Your Dragonchain public ID
// - authKeyID: Your authentication key ID
// - authKey: Your authentication key
// - baseURL: The base URL of your Dragonchain node (e.g., "https://mychain.dragonchain.com")
//
// Returns a configured SDK client ready to make API calls.
// All API methods on the returned client require a context.Context parameter.
func NewDragonchainSDK(publicID, authKeyID, authKey, baseURL string) *DragonchainSDK { func NewDragonchainSDK(publicID, authKeyID, authKey, baseURL string) *DragonchainSDK {
c := client.NewClient(publicID, authKeyID, authKey, baseURL) c := client.NewClient(publicID, authKeyID, authKey, baseURL)
return &DragonchainSDK{ return &DragonchainSDK{
@@ -30,6 +88,8 @@ func NewDragonchainSDK(publicID, authKeyID, authKey, baseURL string) *Dragonchai
} }
} }
// GetClient returns the underlying HTTP client used by the SDK.
// This can be useful for advanced use cases or custom implementations.
func (sdk *DragonchainSDK) GetClient() *client.Client { func (sdk *DragonchainSDK) GetClient() *client.Client {
return sdk.client return sdk.client
} }
+13 -11
View File
@@ -5,10 +5,11 @@ const (
) )
type TransactionCreateRequest struct { type TransactionCreateRequest struct {
Version string `json:"version,omitempty"` Version string `json:"version,omitempty"`
TxnType string `json:"txn_type"` TxnType string `json:"txn_type"`
Payload string `json:"payload"` Payload string `json:"payload"`
Tag string `json:"tag,omitempty"` Tag string `json:"tag,omitempty"`
CorrelationId string `json:"correlation_id,omitempty"`
} }
type TransactionCreateResponse struct { type TransactionCreateResponse struct {
@@ -35,13 +36,14 @@ type Transaction struct {
} }
type TransactionHeader struct { type TransactionHeader struct {
Tag string `json:"tag"` Tag string `json:"tag"`
DcId string `json:"dc_id"` DcId string `json:"dc_id"`
TxnId string `json:"txn_id"` TxnId string `json:"txn_id"`
Invoker string `json:"invoker"` Invoker string `json:"invoker"`
BlockId string `json:"block_id"` BlockId string `json:"block_id"`
TxnType string `json:"txn_type"` TxnType string `json:"txn_type"`
Timestamp string `json:"timestamp"` Timestamp string `json:"timestamp"`
CorrelationId string `json:"correlation_id,omitempty"`
} }
type TransactionProof struct { type TransactionProof struct {
+6 -4
View File
@@ -1,6 +1,8 @@
package system package system
import ( import (
"context"
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client" "git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client"
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/models" "git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/models"
) )
@@ -13,13 +15,13 @@ func NewSystemClient(c *client.Client) *SystemClient {
return &SystemClient{client: c} return &SystemClient{client: c}
} }
func (sc *SystemClient) Health() error { func (sc *SystemClient) Health(ctx context.Context) error {
return sc.client.Get("/api/v1/health", nil) return sc.client.Get(ctx, "/api/v1/health", nil)
} }
func (sc *SystemClient) Status() (*models.SystemStatus, error) { func (sc *SystemClient) Status(ctx context.Context) (*models.SystemStatus, error) {
var resp models.SystemStatus var resp models.SystemStatus
err := sc.client.Get("/api/v1/status", &resp) err := sc.client.Get(ctx, "/api/v1/status", &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+9 -8
View File
@@ -1,6 +1,7 @@
package transaction package transaction
import ( import (
"context"
"fmt" "fmt"
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client" "git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client"
@@ -15,38 +16,38 @@ func NewTransactionClient(c *client.Client) *TransactionClient {
return &TransactionClient{client: c} return &TransactionClient{client: c}
} }
func (tc *TransactionClient) Create(req *models.TransactionCreateRequest) (*models.TransactionCreateResponse, error) { func (tc *TransactionClient) Create(ctx context.Context, req *models.TransactionCreateRequest) (*models.TransactionCreateResponse, error) {
var resp models.TransactionCreateResponse var resp models.TransactionCreateResponse
err := tc.client.Post("/api/v1/transaction", models.ContentTypeJSON, req, &resp) err := tc.client.Post(ctx, "/api/v1/transaction", models.ContentTypeJSON, req, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &resp, nil return &resp, nil
} }
func (tc *TransactionClient) CreateBulk(req *models.TransactionBulkRequest) (*models.TransactionBulkResponse, error) { func (tc *TransactionClient) CreateBulk(ctx context.Context, req *models.TransactionBulkRequest) (*models.TransactionBulkResponse, error) {
var resp models.TransactionBulkResponse var resp models.TransactionBulkResponse
err := tc.client.Post("/api/v1/transaction/bulk", models.ContentTypeJSON, req, &resp) err := tc.client.Post(ctx, "/api/v1/transaction/bulk", models.ContentTypeJSON, req, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &resp, nil return &resp, nil
} }
func (tc *TransactionClient) Get(transactionID string) (*models.Transaction, error) { func (tc *TransactionClient) Get(ctx context.Context, transactionID string) (*models.Transaction, error) {
var resp models.Transaction var resp models.Transaction
path := fmt.Sprintf("/api/v1/transaction/%s", transactionID) path := fmt.Sprintf("/api/v1/transaction/%s", transactionID)
err := tc.client.Get(path, &resp) err := tc.client.Get(ctx, path, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &resp, nil return &resp, nil
} }
func (tc *TransactionClient) List() (*models.ListTransactionsResponse, error) { func (tc *TransactionClient) List(ctx context.Context) (*models.ListTransactionsResponse, error) {
var resp models.ListTransactionsResponse var resp models.ListTransactionsResponse
path := "/api/v1/transaction/" path := "/api/v1/transaction/"
err := tc.client.Get(path, &resp) err := tc.client.Get(ctx, path, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+9 -8
View File
@@ -1,6 +1,7 @@
package transactiontype package transactiontype
import ( import (
"context"
"fmt" "fmt"
"git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client" "git.dragonchain.com/dragonchain/dragonchain-prime-sdk-go/client"
@@ -15,38 +16,38 @@ func NewTransactionTypeClient(c *client.Client) *TransactionTypeClient {
return &TransactionTypeClient{client: c} return &TransactionTypeClient{client: c}
} }
func (ttc *TransactionTypeClient) Create(req *models.TransactionTypeCreateRequest) (*models.TransactionTypeCreateResponse, error) { func (ttc *TransactionTypeClient) Create(ctx context.Context, req *models.TransactionTypeCreateRequest) (*models.TransactionTypeCreateResponse, error) {
var resp models.TransactionTypeCreateResponse var resp models.TransactionTypeCreateResponse
err := ttc.client.Post("/api/v1/transaction-type", models.ContentTypeJSON, req, &resp) err := ttc.client.Post(ctx, "/api/v1/transaction-type", models.ContentTypeJSON, req, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &resp, nil return &resp, nil
} }
func (ttc *TransactionTypeClient) Get(txnType string) (*models.TransactionType, error) { func (ttc *TransactionTypeClient) Get(ctx context.Context, txnType string) (*models.TransactionType, error) {
var resp models.TransactionType var resp models.TransactionType
path := fmt.Sprintf("/api/v1/transaction-type/%s", txnType) path := fmt.Sprintf("/api/v1/transaction-type/%s", txnType)
err := ttc.client.Get(path, &resp) err := ttc.client.Get(ctx, path, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &resp, nil return &resp, nil
} }
func (ttc *TransactionTypeClient) List() (*models.TransactionListResponse, error) { func (ttc *TransactionTypeClient) List(ctx context.Context) (*models.TransactionListResponse, error) {
var resp models.TransactionListResponse var resp models.TransactionListResponse
err := ttc.client.Get("/api/v1/transaction-types", &resp) err := ttc.client.Get(ctx, "/api/v1/transaction-types", &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &resp, nil return &resp, nil
} }
func (ttc *TransactionTypeClient) Delete(txnType string) (*models.SuccessResponse, error) { func (ttc *TransactionTypeClient) Delete(ctx context.Context, txnType string) (*models.SuccessResponse, error) {
var resp models.SuccessResponse var resp models.SuccessResponse
path := fmt.Sprintf("/api/v1/transaction-type/%s", txnType) path := fmt.Sprintf("/api/v1/transaction-type/%s", txnType)
err := ttc.client.Delete(path, &resp) err := ttc.client.Delete(ctx, path, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }