diff --git a/v2/.golangci.yml b/v2/.golangci.yml
index 9961087..b33b9e7 100644
--- a/v2/.golangci.yml
+++ b/v2/.golangci.yml
@@ -9,6 +9,7 @@ linters:
default: none
enable:
- revive
+ - goconst
exclusions:
paths:
- internal/generated
@@ -19,6 +20,15 @@ linters:
enable-all-rules: false
enable-default-rules: false
rules:
+ - name: add-constant
+ severity: warning
+ disabled: false
+ arguments:
+ - max-lit-count: "1"
+ allow-strs: '""'
+ allow-ints: "0,1,2,64,0644"
+ allow-floats: "0.0,0.,1.0,1.,2.0,2."
+
- name: var-naming
arguments:
- ["ID", "URL", "API", "HTTP", "JSON", "UUID"]
@@ -26,7 +36,13 @@ linters:
- - upper-case-const: true
skip-package-name-checks: true
- name: receiver-naming
-
+
+ - name: unexported-return
+ disabled: true
+ - name: unused-parameter
+ disabled: true
+ - name: redefines-builtin-id
+ disabled: true
- name: exported
disabled: true
- name: package-comments
@@ -34,4 +50,10 @@ linters:
- name: dot-imports
disabled: true
- name: indent-error-flow
- disabled: true
\ No newline at end of file
+ disabled: true
+
+ goconst:
+ min-len: 1
+ min-occurrences: 1
+ match-constant: true
+ numbers: true
\ No newline at end of file
diff --git a/v2/client/service_test.go b/v2/client/service_test.go
index 85843f5..6e80a7d 100644
--- a/v2/client/service_test.go
+++ b/v2/client/service_test.go
@@ -1361,12 +1361,12 @@ var _ = Describe("ConnectionController", func() {
mockServer *httptest.Server
//mockToken string
mockRequest InvokeConnectionRequest
- mockResponse map[string]interface{}
+ //mockResponse map[string]interface{}
)
BeforeEach(func() {
//mockToken = "mock-valid-token"
- mockResponse = map[string]interface{}{"key": "value"}
+ //mockResponse = map[string]interface{}{"key": "value"}
mockRequest = InvokeConnectionRequest{
Headers: map[string]string{
"Content-Type": "application/json",
@@ -1405,7 +1405,7 @@ var _ = Describe("ConnectionController", func() {
service, err := client.Connection("failed")
response, err := service.Invoke(ctx, mockRequest)
Expect(err).To(BeNil())
- Expect(response.Data).To(Equal(mockResponse))
+ Expect(response.Data).To(Equal(fmt.Sprintf("%v", `{"key": "value"}`)))
})
})
Context("Handling query parameters", func() {
diff --git a/v2/internal/constants/constants.go b/v2/internal/constants/constants.go
index ff21d0e..32a9b7c 100644
--- a/v2/internal/constants/constants.go
+++ b/v2/internal/constants/constants.go
@@ -16,4 +16,171 @@ const (
ERROR_FROM_CLIENT = "error-from-client"
REQUEST_KEY = "X-Request-Id"
SKYFLOW_ID = "skyflow_id"
+
+ // File extensions
+ FILE_EXTENSION_TXT = "txt"
+ FILE_EXTENSION_PDF = "pdf"
+ FILE_EXTENSION_JSON = "json"
+ FILE_EXTENSION_XML = "xml"
+ FILE_EXTENSION_MP3 = "mp3"
+ FILE_EXTENSION_WAV = "wav"
+ FILE_EXTENSION_JPG = "jpg"
+ FILE_EXTENSION_JPEG = "jpeg"
+ FILE_EXTENSION_PNG = "png"
+ FILE_EXTENSION_BMP = "bmp"
+ FILE_EXTENSION_TIF = "tif"
+ FILE_EXTENSION_TIFF = "tiff"
+ FILE_EXTENSION_PPT = "ppt"
+ FILE_EXTENSION_PPTX = "pptx"
+ FILE_EXTENSION_CSV = "csv"
+ FILE_EXTENSION_XLS = "xls"
+ FILE_EXTENSION_XLSX = "xlsx"
+ FILE_EXTENSION_DOC = "doc"
+ FILE_EXTENSION_DOCX = "docx"
+
+ // Encoding types
+ ENCODING_BASE64 = "base64"
+ ENCODING_UTF8 = "utf-8"
+ ENCODING_BINARY = "binary"
+
+ // File type identifiers
+ FILE_TYPE_TEXT = "text"
+ FILE_TYPE_IMAGE = "image"
+ FILE_TYPE_PDF = "pdf"
+ FILE_TYPE_PPT = "ppt"
+ FILE_TYPE_SPREAD = "spread"
+ FILE_TYPE_AUDIO = "audio"
+ FILE_TYPE_DOCUMENT = "document"
+ FILE_TYPE_STRUCTURED = "structured"
+ FILE_TYPE_GENERIC = "generic"
+
+ // Detect status
+ DETECT_STATUS_IN_PROGRESS = "IN_PROGRESS"
+ DETECT_STATUS_SUCCESS = "SUCCESS"
+ DETECT_STATUS_FAILED = "FAILED"
+
+ // HTTP schemes and protocols
+ HTTPS_PROTOCOL = "https"
+ HTTP_PROTOCOL = "http"
+
+ // PEM key type
+ PRIVATE_KEY_PEM_TYPE = "PRIVATE KEY"
+
+ // Entity types
+ ENTITY_TYPE_REDACTED = "redacted"
+ ENTITY_TYPE_MASKED = "masked"
+ ENTITY_TYPE_PLAIN_TEXT = "plain_text"
+ ENTITY_TYPE_TEXT = "text"
+ ENTITY_TYPE_ENTITY_ONLY = "entity_only"
+ ENTITY_TYPE_VAULT_TOKEN = "vault_token"
+ ENTITY_TYPE_ENTITY_UNIQUE_CTR = "entity_unique_counter"
+ ENTITY_TYPE_ENTITIES = "entities"
+
+ // Request/API names
+ REQUEST_DEIDENTIFY_FILE = "DeidentifyFileRequest"
+ REQUEST_INSERT = "Insert"
+ REQUEST_INSERT_LOWER = "insert"
+ REQUEST_DETOKENIZE = "DetokenizeRequest"
+ REQUEST_GET = "Get"
+ REQUEST_DELETE = "delete"
+ REQUEST_UPDATE = "update"
+ REQUEST_UPLOAD_FILE = "UploadFile"
+ REQUEST_INVOKE_CONN = "Invoke Connection"
+
+ // HTTP headers
+ HEADER_CONTENT_TYPE = "content-type"
+ HEADER_CONTENT_TYPE_CAPITAL = "Content-Type"
+
+ // File type mapping for Detect (removed - use FILE_TYPE_* constants instead)
+
+ // Redaction types for Detect
+ DETECT_REDACTION_TYPE_REDACTED = "redacted"
+ DETECT_REDACTION_TYPE_MASKED = "masked"
+ DETECT_REDACTION_TYPE_PLAINTEXT = "plaintext"
+
+ // File output types for Detect
+ FILE_OUTPUT_TYPE_REDACTED_FILE = "redacted_file"
+ DEIDENTIFIED_FILE_PREFIX = "deidentified."
+
+ // File processing
+ PROCESSED_PREFIX = "processed-"
+ PERMISSION_CHECK_FILE = ".permission_check"
+
+ // Error and status constants
+ UNKNOWN_STATUS = "UNKNOWN"
+ UNKNOWN_ERROR = "Unknown error"
+ HTTP_STATUS_BAD_REQUEST = "Bad Request"
+ ERROR_KEY_FROM_CLIENT = "errorFromClient"
+
+ // Environment variables
+ SKYFLOW_CREDENTIALS_ENV = "SKYFLOW_CREDENTIALS"
+
+ // HTTP headers and content types
+ HEADER_AUTHORIZATION = "x-skyflow-authorization"
+ CONTENT_TYPE_JSON = "application/json"
+ CONTENT_TYPE_TEXT_PLAIN = "text/plain"
+ CONTENT_TYPE_TEXT_CHARSET = "text/plain; charset=utf-8"
+ RESPONSE_HEADER_REQUEST_ID = "x-request-id"
+
+ // JSON error response keys
+ ERROR_KEY_ERROR = "error"
+ ERROR_KEY_MESSAGE = "message"
+ ERROR_KEY_HTTP_CODE = "http_code"
+ ERROR_KEY_GRPC_CODE = "grpc_code"
+ ERROR_KEY_HTTP_STATUS = "http_status"
+ ERROR_KEY_DETAILS = "details"
+
+ // JSON response keys
+ REQUEST_ID_KEY = "request_id"
+ RESPONSE_KEY_REQUEST_ID = "RequestId"
+ RESPONSE_KEY_HTTP_CODE = "HttpCode"
+ RESPONSE_KEY_SKYFLOW_ID = "skyflowId"
+
+ // Other constants
+ ERROR_FAILED_TO_READ = "Failed to read error"
+
+ // Credentials and JWT keys
+ CRED_KEY_PRIVATE_KEY = "privateKey"
+ CRED_KEY_CLIENT_ID = "clientID"
+ CRED_KEY_TOKEN_URI = "tokenURI"
+ CRED_KEY_KEY_ID = "keyID"
+ API_KEY_PREFIX = "sky-"
+
+ // JWT claim keys
+ JWT_CLAIM_EXP = "exp"
+ JWT_CLAIM_CTX = "ctx"
+ JWT_CLAIM_ISS = "iss"
+ JWT_CLAIM_AUD = "aud"
+ JWT_CLAIM_KEY = "key"
+ JWT_CLAIM_IAT = "iat"
+ JWT_CLAIM_SUB = "sub"
+ JWT_CLAIM_TOK = "tok"
+
+ // Request validation
+ REQUEST_INVOKE_CONNECTION = "InvokeConnectionRequest"
+ REQUEST_ENTITY_ONLY = "entity_only"
+ REQUEST_DEIDENTIFY_TEXT = "DeidentifyTextRequest"
+ REQUEST_REIDENTIFY_TEXT = "ReidentifyTextRequest"
+ REQUEST_GET_DETECT_RUN = "GetDetectRunRequest"
+ REQUEST_TOKENIZE = "Tokenize"
+
+ // JSON keys for request/response bodies
+ JSON_KEY_BODY = "Body"
+ JSON_KEY_RECORDS = "records"
+ JSON_KEY_TOKENS = "tokens"
+ JSON_KEY_REQUEST_INDEX = "request_index"
+ JSON_KEY_TOKENIZED_DATA = "tokenized_data"
+
+ // SDK and token generation
+ SDK_ISSUER = "sdk"
+ SIGNED_TOKEN_PREFIX = "signed_token_"
+
+ // SDK metadata keys for CreateJsonMetadata
+ SDK_METADATA_KEY_NAME_VERSION = "sdk_name_version"
+ SDK_METADATA_KEY_DEVICE_MODEL = "sdk_client_device_model"
+ SDK_METADATA_KEY_OS_DETAILS = "sdk_client_os_details"
+ SDK_METADATA_KEY_RUNTIME_DETAILS = "sdk_runtime_details"
+
+ // Magic numbers
+ API_KEY_LENGTH = 42
)
diff --git a/v2/internal/helpers/helpers.go b/v2/internal/helpers/helpers.go
index 3b7d31c..b3e3c5d 100644
--- a/v2/internal/helpers/helpers.go
+++ b/v2/internal/helpers/helpers.go
@@ -19,10 +19,10 @@ import (
"github.com/skyflowapi/skyflow-go/v2/internal/generated/core"
- vaultapis "github.com/skyflowapi/skyflow-go/v2/internal/generated"
- "github.com/golang-jwt/jwt/v4"
+ "github.com/golang-jwt/jwt"
constants "github.com/skyflowapi/skyflow-go/v2/internal/constants"
internal "github.com/skyflowapi/skyflow-go/v2/internal/generated"
+ vaultapis "github.com/skyflowapi/skyflow-go/v2/internal/generated"
internalAuthApi "github.com/skyflowapi/skyflow-go/v2/internal/generated/authentication"
"github.com/skyflowapi/skyflow-go/v2/internal/generated/option"
common "github.com/skyflowapi/skyflow-go/v2/utils/common"
@@ -125,23 +125,23 @@ func GetFormattedBatchInsertRecord(record interface{}, requestIndex int) (map[st
return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_RESPONSE)
}
- // Extract relevant data from "Body"
- body, bodyExists := bodyObject["Body"].(map[string]interface{})
+ // Extract relevant data from Body
+ body, bodyExists := bodyObject[constants.JSON_KEY_BODY].(map[string]interface{})
if !bodyExists {
return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_RESPONSE)
}
// Handle extracted data
- if records, ok := body["records"].([]interface{}); ok {
+ if records, ok := body[constants.JSON_KEY_RECORDS].([]interface{}); ok {
for _, rec := range records {
recordObject, isMap := rec.(map[string]interface{})
if !isMap {
continue
}
- if skyflowID, exists := recordObject["skyflow_id"].(string); exists {
- insertRecord["skyflow_id"] = skyflowID
+ if skyflowID, exists := recordObject[constants.SKYFLOW_ID].(string); exists {
+ insertRecord[constants.SKYFLOW_ID] = skyflowID
}
- if tokens, exists := recordObject["tokens"].(map[string]interface{}); exists {
+ if tokens, exists := recordObject[constants.JSON_KEY_TOKENS].(map[string]interface{}); exists {
for key, value := range tokens {
insertRecord[key] = value
}
@@ -149,16 +149,16 @@ func GetFormattedBatchInsertRecord(record interface{}, requestIndex int) (map[st
}
}
- if errorField, exists := body["error"].(string); exists {
- insertRecord["error"] = errorField
+ if errorField, exists := body[constants.ERROR_KEY_ERROR].(string); exists {
+ insertRecord[constants.ERROR_KEY_ERROR] = errorField
}
- insertRecord["request_index"] = requestIndex
+ insertRecord[constants.JSON_KEY_REQUEST_INDEX] = requestIndex
return insertRecord, nil
}
func GetFormattedBulkInsertRecord(record vaultapis.V1RecordMetaProperties) map[string]interface{} {
insertRecord := make(map[string]interface{})
- insertRecord["skyflow_id"] = *record.GetSkyflowId()
+ insertRecord[constants.SKYFLOW_ID] = *record.GetSkyflowId()
tokensMap := record.GetTokens()
if len(tokensMap) > 0 {
@@ -179,7 +179,7 @@ func GetFormattedQueryRecord(record vaultapis.V1FieldRecords) map[string]interfa
for key, value := range record.Tokens {
tokens[key] = value
}
- queryRecord["tokenized_data"] = tokens
+ queryRecord[constants.JSON_KEY_TOKENIZED_DATA] = tokens
}
}
return queryRecord
@@ -302,11 +302,11 @@ func GetFileForFileUpload(request common.FileUploadRequest) (*os.File, error) {
if request.Base64 != "" {
data, err := base64.StdEncoding.DecodeString(request.Base64)
if err != nil {
- return nil, fmt.Errorf("failed to decode base64: %w", err)
+ return nil, fmt.Errorf(logs.FAILED_TO_DECODE_BASE64, err)
}
file, err := os.Create(request.FileName)
if err != nil {
- return nil, fmt.Errorf("failed to create file: %w", err)
+ return nil, fmt.Errorf(logs.FAILED_TO_CREATE_FILE, err)
}
// Write data
_, err = file.Write(data)
@@ -346,9 +346,9 @@ func GetSignedDataTokens(credKeys map[string]interface{}, options common.SignedD
// Helper for extracting credentials
func GetCredentialParams(credKeys map[string]interface{}) (string, string, string, *skyflowError.SkyflowError) {
- clientID, ok := credKeys["clientID"].(string)
- tokenURI, ok2 := credKeys["tokenURI"].(string)
- keyID, ok3 := credKeys["keyID"].(string)
+ clientID, ok := credKeys[constants.CRED_KEY_CLIENT_ID].(string)
+ tokenURI, ok2 := credKeys[constants.CRED_KEY_TOKEN_URI].(string)
+ keyID, ok3 := credKeys[constants.CRED_KEY_KEY_ID].(string)
if !ok || !ok2 || !ok3 {
logger.Error(logs.INVALID_CREDENTIALS_FILE_FORMAT)
return "", "", "", skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_CREDENTIALS)
@@ -358,23 +358,31 @@ func GetCredentialParams(credKeys map[string]interface{}) (string, string, strin
// Generate signed tokens
func GenerateSignedDataTokensHelper(clientID, keyID string, pvtKey *rsa.PrivateKey, options common.SignedDataTokensOptions, tokenURI string) ([]common.SignedDataTokensResponse, *skyflowError.SkyflowError) {
+ if options.TokenURI != "" {
+ if !isValidURL(options.TokenURI) {
+ logger.Error(logs.INVALID_TOKEN_URI)
+ return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_TOKEN_URI)
+ }
+ tokenURI = options.TokenURI
+ }
+
var responseArray []common.SignedDataTokensResponse
for _, token := range options.DataTokens {
claims := jwt.MapClaims{
- "iss": "sdk",
- "key": keyID,
- "aud": tokenURI,
- "iat": time.Now().Unix(),
- "sub": clientID,
- "tok": token,
+ constants.JWT_CLAIM_ISS: constants.SDK_ISSUER,
+ constants.JWT_CLAIM_KEY: keyID,
+ constants.JWT_CLAIM_AUD: tokenURI,
+ constants.JWT_CLAIM_IAT: time.Now().Unix(),
+ constants.JWT_CLAIM_SUB: clientID,
+ constants.JWT_CLAIM_TOK: token,
}
if options.TimeToLive > 0 {
- claims["exp"] = time.Now().Add(time.Duration(options.TimeToLive) * time.Second).Unix()
+ claims[constants.JWT_CLAIM_EXP] = time.Now().Add(time.Duration(options.TimeToLive) * time.Second).Unix()
} else {
- claims["exp"] = time.Now().Add(time.Duration(60) * time.Second).Unix()
+ claims[constants.JWT_CLAIM_EXP] = time.Now().Add(time.Duration(60) * time.Second).Unix()
}
if options.Ctx != "" {
- claims["ctx"] = options.Ctx
+ claims[constants.JWT_CLAIM_CTX] = options.Ctx
}
tokenString, err := jwt.NewWithClaims(jwt.SigningMethodRS256, claims).SignedString(pvtKey)
@@ -382,14 +390,14 @@ func GenerateSignedDataTokensHelper(clientID, keyID string, pvtKey *rsa.PrivateK
logger.Error(logs.PARSE_JWT_PAYLOAD)
return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, fmt.Sprintf(skyflowError.ERROR_OCCURRED+"%v", err))
}
- responseArray = append(responseArray, common.SignedDataTokensResponse{Token: token, SignedToken: "signed_token_" + tokenString})
+ responseArray = append(responseArray, common.SignedDataTokensResponse{Token: token, SignedToken: constants.SIGNED_TOKEN_PREFIX + tokenString})
}
logger.Info(logs.GENERATE_SIGNED_DATA_TOKEN_SUCCESS)
return responseArray, nil
}
func GetPrivateKey(credKeys map[string]interface{}) (*rsa.PrivateKey, *skyflowError.SkyflowError) {
- privateKeyStr, ok := credKeys["privateKey"].(string)
+ privateKeyStr, ok := credKeys[constants.CRED_KEY_PRIVATE_KEY].(string)
if !ok {
logger.Error(logs.PRIVATE_KEY_NOT_FOUND)
return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.MISSING_PRIVATE_KEY)
@@ -404,7 +412,7 @@ func ParsePrivateKey(pemKey string) (*rsa.PrivateKey, *skyflowError.SkyflowError
logger.Error(logs.JWT_INVALID_FORMAT)
return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.JWT_INVALID_FORMAT)
}
- if privPem.Type != "PRIVATE KEY" {
+ if privPem.Type != constants.PRIVATE_KEY_PEM_TYPE {
logger.Error(fmt.Sprintf(logs.PRIVATE_KEY_TYPE, privPem.Type))
return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.JWT_INVALID_FORMAT)
}
@@ -428,9 +436,17 @@ func ParsePrivateKey(pemKey string) (*rsa.PrivateKey, *skyflowError.SkyflowError
var GetBaseURLHelper = GetBaseURL
+func isValidURL(urlStr string) bool {
+ parsedUrl, err := url.Parse(urlStr)
+ if err != nil {
+ return false
+ }
+ return parsedUrl.Scheme == constants.HTTPS_PROTOCOL && parsedUrl.Host != ""
+}
+
// GenerateBearerTokenHelper helper functions
func GenerateBearerTokenHelper(credKeys map[string]interface{}, options common.BearerTokenOptions) (*internal.V1GetAuthTokenResponse, *skyflowError.SkyflowError) {
- privateKey := credKeys["privateKey"]
+ privateKey := credKeys[constants.CRED_KEY_PRIVATE_KEY]
if privateKey == nil {
logger.Error(fmt.Sprintf(logs.PRIVATE_KEY_NOT_FOUND))
return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.MISSING_PRIVATE_KEY)
@@ -439,17 +455,26 @@ func GenerateBearerTokenHelper(credKeys map[string]interface{}, options common.B
if err1 != nil {
return nil, err1
}
- clientID, ok := credKeys["clientID"].(string)
+ clientID, ok := credKeys[constants.CRED_KEY_CLIENT_ID].(string)
if !ok {
logger.Error(fmt.Sprintf(logs.CLIENT_ID_NOT_FOUND))
return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.MISSING_CLIENT_ID)
}
- tokenURI, ok1 := credKeys["tokenURI"].(string)
- if !ok1 {
- logger.Error(fmt.Sprintf(logs.TOKEN_URI_NOT_FOUND))
- return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.MISSING_TOKEN_URI)
+ tokenURI := options.TokenURI
+ if tokenURI != "" {
+ if !isValidURL(tokenURI) {
+ logger.Error(logs.INVALID_TOKEN_URI)
+ return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_TOKEN_URI)
+ }
+ } else {
+ var ok1 bool
+ tokenURI, ok1 = credKeys[constants.CRED_KEY_TOKEN_URI].(string)
+ if !ok1 {
+ logger.Error(fmt.Sprintf(logs.TOKEN_URI_NOT_FOUND))
+ return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.MISSING_TOKEN_URI)
+ }
}
- keyID, ok2 := credKeys["keyID"].(string)
+ keyID, ok2 := credKeys[constants.CRED_KEY_KEY_ID].(string)
if !ok2 {
logger.Error(fmt.Sprintf(logs.KEY_ID_NOT_FOUND))
return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.MISSING_KEY_ID)
@@ -501,7 +526,7 @@ func GetScopeUsingRoles(roles []*string) string {
}
func GetBaseURL(urlStr string) (string, *skyflowError.SkyflowError) {
parsedUrl, err := url.Parse(urlStr)
- if err != nil || parsedUrl.Scheme != "https" || parsedUrl.Host == "" {
+ if err != nil || parsedUrl.Scheme != constants.HTTPS_PROTOCOL || parsedUrl.Host == "" {
logger.Error(logs.INVALID_TOKEN_URI)
return "", skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_TOKEN_URI) // return error if URL parsing fails
}
@@ -512,19 +537,19 @@ func GetBaseURL(urlStr string) (string, *skyflowError.SkyflowError) {
func GetSignedBearerUserToken(clientID, keyID, tokenURI string, pvtKey *rsa.PrivateKey, options common.BearerTokenOptions) (string, *skyflowError.SkyflowError) {
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
- "iss": clientID,
- "key": keyID,
- "aud": tokenURI,
- "sub": clientID,
- "exp": time.Now().Add(60 * time.Minute).Unix(),
+ constants.JWT_CLAIM_ISS: clientID,
+ constants.JWT_CLAIM_KEY: keyID,
+ constants.JWT_CLAIM_AUD: tokenURI,
+ constants.JWT_CLAIM_SUB: clientID,
+ constants.JWT_CLAIM_EXP: time.Now().Add(60 * time.Minute).Unix(),
})
if options.Ctx != "" {
- token.Claims.(jwt.MapClaims)["ctx"] = options.Ctx
+ token.Claims.(jwt.MapClaims)[constants.JWT_CLAIM_CTX] = options.Ctx
}
var err error
signedToken, err := token.SignedString(pvtKey)
if err != nil {
- logger.Error(fmt.Sprintf("%s", "unable to parse jwt payload"))
+ logger.Error(fmt.Sprintf("%s", logs.PARSE_JWT_PAYLOAD))
return "", skyflowError.NewSkyflowError(skyflowError.SERVER, fmt.Sprintf(skyflowError.UNKNOWN_ERROR, err))
}
return signedToken, nil
@@ -536,7 +561,7 @@ func GetPrivateKeyFromPem(pemKey string) (*rsa.PrivateKey, *skyflowError.Skyflow
logger.Error(fmt.Sprintf(logs.JWT_INVALID_FORMAT))
return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.JWT_INVALID_FORMAT)
}
- if privPem.Type != "PRIVATE KEY" {
+ if privPem.Type != constants.PRIVATE_KEY_PEM_TYPE {
logger.Error(logs.JWT_INVALID_FORMAT)
return nil, skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.JWT_INVALID_FORMAT)
}
@@ -561,16 +586,16 @@ func GetPrivateKeyFromPem(pemKey string) (*rsa.PrivateKey, *skyflowError.Skyflow
func CreateJsonMetadata() string {
// Create a map to hold the key-value pairs
data := map[string]string{
- "sdk_name_version": fmt.Sprintf("%s@%s", constants.SDK_NAME, constants.SDK_VERSION),
- "sdk_client_device_model": string(runtime.GOOS),
- "sdk_client_os_details": fmt.Sprintf("%s %s", runtime.GOOS, runtime.GOARCH),
- "sdk_runtime_details": runtime.Version(),
+ constants.SDK_METADATA_KEY_NAME_VERSION: fmt.Sprintf("%s@%s", constants.SDK_NAME, constants.SDK_VERSION),
+ constants.SDK_METADATA_KEY_DEVICE_MODEL: string(runtime.GOOS),
+ constants.SDK_METADATA_KEY_OS_DETAILS: fmt.Sprintf("%s %s", runtime.GOOS, runtime.GOARCH),
+ constants.SDK_METADATA_KEY_RUNTIME_DETAILS: runtime.Version(),
}
// Marshal the map into JSON format
jsonData, err := json.Marshal(data)
if err != nil {
- logger.Debug("failed for marshalling json data in createJSONMetadata()")
+ logger.Debug(logs.FAILED_TO_MARSHALL_JSON_METADATA)
return ""
}
return string(jsonData)
@@ -592,8 +617,8 @@ func GetHeader(err error) (http.Header, bool) {
}
func GetSkyflowID(data map[string]interface{}) (string, bool) {
- if id, ok := data["skyflow_id"].(string); ok {
+ if id, ok := data[constants.SKYFLOW_ID].(string); ok {
return id, true
}
return "", false
-}
\ No newline at end of file
+}
diff --git a/v2/internal/helpers/helpers_test.go b/v2/internal/helpers/helpers_test.go
index 5c499f5..de6efd9 100644
--- a/v2/internal/helpers/helpers_test.go
+++ b/v2/internal/helpers/helpers_test.go
@@ -336,6 +336,89 @@ MIIBAAIBADANINVALIDKEY==
})
})
+ Context("GenerateSignedDataTokensHelper", func() {
+ var (
+ credKeys map[string]interface{}
+ options common.SignedDataTokensOptions
+ response []common.SignedDataTokensResponse
+ err *SkyflowError
+ )
+
+ BeforeEach(func() {
+ credKeys = getValidCreds()
+ options = common.SignedDataTokensOptions{
+ DataTokens: []string{"testToken1", "testToken2"},
+ TimeToLive: 3600,
+ Ctx: "testContext",
+ }
+ })
+
+ Context("When tokenUri is provided in options", func() {
+ It("should use the tokenUri from options if valid", func() {
+ credKeys = getValidCreds()
+ options.TokenURI = "https://valid-token-uri.com"
+ response, err = GenerateSignedDataTokensHelper(
+ credKeys["clientID"].(string),
+ credKeys["keyID"].(string),
+ getValidPrivateKey(),
+ options,
+ "https://default-uri.com",
+ )
+ Expect(err).Should(BeNil())
+ Expect(response).Should(HaveLen(2))
+ })
+
+ It("should return error if tokenUri in options is invalid", func() {
+ options.TokenURI = "http://invalid-uri.com"
+ response, err = GenerateSignedDataTokensHelper("client123", "key456", nil, options, "https://default-uri.com")
+ Expect(err).ShouldNot(BeNil())
+ Expect(err.GetCode()).Should(Equal("Code: 400"))
+ Expect(err.GetMessage()).Should(ContainSubstring(INVALID_TOKEN_URI))
+ Expect(response).Should(BeNil())
+ })
+
+ It("should return error if tokenUri in options is malformed", func() {
+ options.TokenURI = "not-a-valid-url"
+ response, err = GenerateSignedDataTokensHelper("client123", "key456", nil, options, "https://default-uri.com")
+ Expect(err).ShouldNot(BeNil())
+ Expect(err.GetCode()).Should(Equal("Code: 400"))
+ Expect(err.GetMessage()).Should(ContainSubstring(INVALID_TOKEN_URI))
+ Expect(response).Should(BeNil())
+ })
+
+ It("should use default tokenUri if options.TokenURI is empty", func() {
+ credKeys = getValidCreds()
+ options.TokenURI = "" // Empty tokenUri
+ response, err = GenerateSignedDataTokensHelper(
+ credKeys["clientID"].(string),
+ credKeys["keyID"].(string),
+ getValidPrivateKey(),
+ options,
+ "https://default-uri.com",
+ )
+ Expect(err).Should(BeNil())
+ Expect(response).Should(HaveLen(2))
+ })
+ })
+
+ Context("When tokenUri is provided and valid", func() {
+ It("should return signed data tokens successfully with valid tokenUri", func() {
+ credKeys = getValidCreds()
+ options.TokenURI = "https://valid-token-uri.com"
+ response, err = GenerateSignedDataTokensHelper(
+ credKeys["clientID"].(string),
+ credKeys["keyID"].(string),
+ getValidPrivateKey(),
+ options,
+ "https://ignored-default-uri.com",
+ )
+ Expect(err).Should(BeNil())
+ Expect(response).Should(HaveLen(2))
+ Expect(response[0].Token).Should(Equal("testToken1"))
+ Expect(response[0].SignedToken).Should(ContainSubstring("signed_token_"))
+ })
+ })
+ })
Context("GetScopeUsingRoles", func() {
// Test case 1: roles is nil
It("should return an empty string when roles is nil", func() {
@@ -520,6 +603,70 @@ MIIBAAIBADANINVALIDKEY==
})
})
+
+ })
+ Context("GenerateBearerTokenHelper with tokenUri in options", func() {
+ var (
+ credKeys map[string]interface{}
+ options common.BearerTokenOptions
+ )
+
+ BeforeEach(func() {
+ credKeys = getValidCreds()
+ options = common.BearerTokenOptions{
+ Ctx: "testContext",
+ RoleIDs: []string{"roleid1"},
+ }
+ })
+
+ It("should use the tokenUri from options if valid", func() {
+ options.TokenURI = "https://valid-token-uri.com"
+ originalGetBaseURLHelper := GetBaseURLHelper
+
+ defer func() { GetBaseURLHelper = originalGetBaseURLHelper }()
+
+ GetBaseURLHelper = func(urlStr string) (string, *SkyflowError) {
+ return "https://valid-token-uri.com", nil
+ }
+
+ _, err := GenerateBearerTokenHelper(credKeys, options)
+ Expect(err).ShouldNot(BeNil())
+ })
+
+ It("should return error if tokenUri in options is invalid (http instead of https)", func() {
+ options.TokenURI = "http://invalid-uri.com"
+ response, err := GenerateBearerTokenHelper(credKeys, options)
+
+ Expect(err).ShouldNot(BeNil())
+ Expect(response).Should(BeNil())
+ Expect(err.GetCode()).Should(Equal("Code: 400"))
+ Expect(err.GetMessage()).Should(ContainSubstring(INVALID_TOKEN_URI))
+ })
+
+ It("should return error if tokenUri in options is malformed", func() {
+ options.TokenURI = "not-a-valid-url"
+ response, err := GenerateBearerTokenHelper(credKeys, options)
+
+ Expect(err).ShouldNot(BeNil())
+ Expect(response).Should(BeNil())
+ Expect(err.GetCode()).Should(Equal("Code: 400"))
+ Expect(err.GetMessage()).Should(ContainSubstring(INVALID_TOKEN_URI))
+ })
+
+ It("should use credKeys tokenUri when options.TokenURI is empty", func() {
+ options.TokenURI = ""
+ originalGetBaseURLHelper := GetBaseURLHelper
+
+ defer func() { GetBaseURLHelper = originalGetBaseURLHelper }()
+
+ GetBaseURLHelper = func(urlStr string) (string, *SkyflowError) {
+ Expect(urlStr).Should(ContainSubstring(credKeys["tokenURI"].(string)))
+ return "https://valid-uri.com", nil
+ }
+
+ _, err := GenerateBearerTokenHelper(credKeys, options)
+ Expect(err).ShouldNot(BeNil())
+ })
})
Context("GetHeader", func() {
@@ -884,3 +1031,9 @@ func getValidCreds() map[string]interface{} {
_ = json.Unmarshal([]byte(pvtKey), &credMap)
return credMap
}
+
+func getValidPrivateKey() *rsa.PrivateKey {
+ credKeys := getValidCreds()
+ pvtKey, _ := GetPrivateKey(credKeys)
+ return pvtKey
+}
diff --git a/v2/internal/validation/validations.go b/v2/internal/validation/validations.go
index 6669599..5bcf744 100644
--- a/v2/internal/validation/validations.go
+++ b/v2/internal/validation/validations.go
@@ -8,6 +8,7 @@ import (
"path/filepath"
"strings"
+ constants "github.com/skyflowapi/skyflow-go/v2/internal/constants"
vaultapis "github.com/skyflowapi/skyflow-go/v2/internal/generated"
"github.com/skyflowapi/skyflow-go/v2/internal/helpers"
"github.com/skyflowapi/skyflow-go/v2/utils/common"
@@ -19,13 +20,13 @@ import (
// ValidateDeidentifyTextRequest validates the required fields of DeidentifyTextRequest.
func ValidateDeidentifyTextRequest(req common.DeidentifyTextRequest) *skyflowError.SkyflowError {
if strings.TrimSpace(req.Text) == "" {
- logger.Error(fmt.Sprintf(logs.INVALID_TEXT_IN_DEIDENTIFY, "DeidentifyTextRequest"))
+ logger.Error(fmt.Sprintf(logs.INVALID_TEXT_IN_DEIDENTIFY, constants.REQUEST_DEIDENTIFY_TEXT))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_TEXT_IN_DEIDENTIFY)
}
// Validate entities
if len(req.Entities) > 0 {
- if err := validateEntities(req.Entities, "text"); err != nil {
+ if err := validateEntities(req.Entities, constants.ENTITY_TYPE_TEXT); err != nil {
return err
}
}
@@ -39,14 +40,14 @@ func ValidateDeidentifyTextRequest(req common.DeidentifyTextRequest) *skyflowErr
// Validate EntityOnly tokens
if len(req.TokenFormat.EntityOnly) > 0 {
- if err := validateEntities(req.TokenFormat.EntityOnly, "entity_only"); err != nil {
+ if err := validateEntities(req.TokenFormat.EntityOnly, constants.ENTITY_TYPE_ENTITY_ONLY); err != nil {
return err
}
}
// Validate VaultToken entities
if len(req.TokenFormat.VaultToken) > 0 {
- if err := validateEntities(req.TokenFormat.VaultToken, "vault_token"); err != nil {
+ if err := validateEntities(req.TokenFormat.VaultToken, constants.ENTITY_TYPE_VAULT_TOKEN); err != nil {
return err
}
}
@@ -61,27 +62,27 @@ func ValidateDeidentifyTextRequest(req common.DeidentifyTextRequest) *skyflowErr
// ValidateReidentifyTextRequest validates the required fields of ReidentifyTextRequest.
func ValidateReidentifyTextRequest(req common.ReidentifyTextRequest) *skyflowError.SkyflowError {
if strings.TrimSpace(req.Text) == "" {
- logger.Error(fmt.Sprintf(logs.INVALID_TEXT_IN_REIDENTIFY, "ReidentifyTextRequest"))
+ logger.Error(fmt.Sprintf(logs.INVALID_TEXT_IN_REIDENTIFY, constants.REQUEST_REIDENTIFY_TEXT))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_TEXT_IN_REIDENTIFY)
}
// Validate RedactedEntities
if len(req.RedactedEntities) > 0 {
- if err := validateEntities(req.RedactedEntities, "redacted"); err != nil {
+ if err := validateEntities(req.RedactedEntities, constants.ENTITY_TYPE_REDACTED); err != nil {
return err
}
}
// Validate MaskedEntities
if len(req.MaskedEntities) > 0 {
- if err := validateEntities(req.MaskedEntities, "masked"); err != nil {
+ if err := validateEntities(req.MaskedEntities, constants.ENTITY_TYPE_MASKED); err != nil {
return err
}
}
// Validate PlainTextEntities
if len(req.PlainTextEntities) > 0 {
- if err := validateEntities(req.PlainTextEntities, "plain_text"); err != nil {
+ if err := validateEntities(req.PlainTextEntities, constants.ENTITY_TYPE_PLAIN_TEXT); err != nil {
return err
}
}
@@ -92,31 +93,31 @@ func ValidateReidentifyTextRequest(req common.ReidentifyTextRequest) *skyflowErr
func validateEntities(entities []common.DetectEntities, entityType string) *skyflowError.SkyflowError {
for _, entity := range entities {
// add entity type validation
- if entityType == "redacted" {
+ if entityType == constants.ENTITY_TYPE_REDACTED {
if _, err := vaultapis.NewFormatRedactedItemFromString(string(entity)); err != nil {
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, fmt.Sprintf(skyflowError.INVALID_ENTITY_TYPE, entity))
}
- } else if entityType == "masked" {
+ } else if entityType == constants.ENTITY_TYPE_MASKED {
if _, err := vaultapis.NewFormatMaskedItemFromString(string(entity)); err != nil {
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, fmt.Sprintf(skyflowError.INVALID_ENTITY_TYPE, entity))
}
- } else if entityType == "plain_text" {
+ } else if entityType == constants.ENTITY_TYPE_PLAIN_TEXT {
if _, err := vaultapis.NewFormatPlaintextItemFromString(string(entity)); err != nil {
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, fmt.Sprintf(skyflowError.INVALID_ENTITY_TYPE, entity))
}
- } else if entityType == "text" {
+ } else if entityType == constants.ENTITY_TYPE_TEXT {
if _, err := vaultapis.NewDeidentifyStringRequestEntityTypesItemFromString(string(entity)); err != nil {
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, fmt.Sprintf(skyflowError.INVALID_ENTITY_TYPE, entity))
}
- } else if entityType == "entity_only" {
+ } else if entityType == constants.ENTITY_TYPE_ENTITY_ONLY {
if _, err := vaultapis.NewTokenTypeMappingEntityOnlyItemFromString(string(entity)); err != nil {
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, fmt.Sprintf(skyflowError.INVALID_ENTITY_TYPE, entity))
}
- } else if entityType == "vault_token" {
+ } else if entityType == constants.ENTITY_TYPE_VAULT_TOKEN {
if _, err := vaultapis.NewTokenTypeMappingVaultTokenItemFromString(string(entity)); err != nil {
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, fmt.Sprintf(skyflowError.INVALID_ENTITY_TYPE, entity))
}
- } else if entityType == "entity_unique_counter" {
+ } else if entityType == constants.ENTITY_TYPE_ENTITY_UNIQUE_CTR {
if _, err := vaultapis.NewTokenTypeMappingEntityUnqCounterItemFromString(string(entity)); err != nil {
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, fmt.Sprintf(skyflowError.INVALID_ENTITY_TYPE, entity))
}
@@ -143,7 +144,7 @@ func validateTransformations(transformations common.Transformations) *skyflowErr
// ValidateGetDetectRunRequest validates the required fields of GetDetectRunRequest.
func ValidateGetDetectRunRequest(req common.GetDetectRunRequest) *skyflowError.SkyflowError {
if strings.TrimSpace(req.RunId) == "" {
- logger.Error(fmt.Sprintf(logs.EMPTY_RUN_ID, "GetDetectRunRequest"))
+ logger.Error(fmt.Sprintf(logs.EMPTY_RUN_ID, constants.REQUEST_GET_DETECT_RUN))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_RUN_ID)
}
return nil
@@ -151,9 +152,7 @@ func ValidateGetDetectRunRequest(req common.GetDetectRunRequest) *skyflowError.S
// ValidateDeidentifyFileRequest validates the required fields of DeidentifyFileRequest.
func ValidateDeidentifyFileRequest(req common.DeidentifyFileRequest) *skyflowError.SkyflowError {
- tag := "DeidentifyFileRequest"
-
- // Validate required fields
+ tag := constants.REQUEST_DEIDENTIFY_FILE
// Validate if file or filepath is provided
if req.File.File == nil && req.File.FilePath == "" {
logger.Error(fmt.Sprintf(logs.EMPTY_FILE_AND_FILE_PATH_IN_DEIDENTIFY_FILE, tag))
@@ -207,7 +206,7 @@ func ValidateDeidentifyFileRequest(req common.DeidentifyFileRequest) *skyflowErr
// Validate entities
if len(req.Entities) > 0 {
- if err := validateEntities(req.Entities, "entities"); err != nil {
+ if err := validateEntities(req.Entities, constants.ENTITY_TYPE_ENTITIES); err != nil {
return err
}
}
@@ -229,13 +228,13 @@ func ValidateDeidentifyFileRequest(req common.DeidentifyFileRequest) *skyflowErr
}
if len(req.TokenFormat.EntityOnly) > 0 {
- if err := validateEntities(req.TokenFormat.EntityOnly, "entity_only"); err != nil {
+ if err := validateEntities(req.TokenFormat.EntityOnly, constants.REQUEST_ENTITY_ONLY); err != nil {
return err
}
}
if len(req.TokenFormat.EntityUniqueCounter) > 0 {
- if err := validateEntities(req.TokenFormat.EntityUniqueCounter, "entity_unique_counter"); err != nil {
+ if err := validateEntities(req.TokenFormat.EntityUniqueCounter, constants.ENTITY_TYPE_ENTITY_UNIQUE_CTR); err != nil {
return err
}
}
@@ -271,7 +270,7 @@ func ValidateDeidentifyFileRequest(req common.DeidentifyFileRequest) *skyflowErr
// Helper function to check directory write permission
func checkDirWritePermission(dir string) error {
// Try to create a temporary file
- tempFile := filepath.Join(dir, ".permission_check")
+ tempFile := filepath.Join(dir, constants.PERMISSION_CHECK_FILE)
file, err := os.Create(tempFile)
if err != nil {
return err
@@ -336,7 +335,7 @@ func ValidateFilePermissions(filePath string, file *os.File) *skyflowError.Skyfl
func ValidateInsertRequest(request common.InsertRequest, options common.InsertOptions) *skyflowError.SkyflowError {
// Validate table
- tag := "Insert"
+ tag := constants.REQUEST_INSERT
if request.Table == "" {
logger.Error(fmt.Sprintf(logs.EMPTY_TABLE, tag))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.TABLE_KEY_ERROR)
@@ -362,11 +361,8 @@ func ValidateInsertRequest(request common.InsertRequest, options common.InsertOp
// Validate each key-value pair in values
for _, valueMap := range request.Values {
- for key, value := range valueMap {
- if value == nil || value == "" {
- logger.Error(fmt.Sprintf(logs.EMPTY_OR_NULL_VALUE_IN_VALUES, tag, key))
- return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_VALUE_IN_VALUES)
- } else if key == "" {
+ for key := range valueMap {
+ if key == "" {
logger.Error(fmt.Sprintf(logs.EMPTY_OR_NULL_KEY_IN_VALUES, tag))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_KEY_IN_VALUES)
}
@@ -407,7 +403,7 @@ func ValidateInsertRequest(request common.InsertRequest, options common.InsertOp
}
func ValidateTokensForInsertRequest(tokens []map[string]interface{}, values []map[string]interface{}, mode common.BYOT) *skyflowError.SkyflowError {
- tag := "insert"
+ tag := constants.REQUEST_INSERT_LOWER
if tokens == nil || len(tokens) == 0 {
if mode == common.ENABLE || mode == common.ENABLE_STRICT {
logger.Error(fmt.Sprintf(logs.EMPTY_TOKENS, tag))
@@ -573,13 +569,20 @@ func ValidateCredentials(credentials common.Credentials) *skyflowError.SkyflowEr
// API key validation
if credentials.ApiKey != "" {
// Validate API key format
- if len(credentials.ApiKey) != 42 || !strings.Contains(credentials.ApiKey, "sky-") {
+ if len(credentials.ApiKey) != constants.API_KEY_LENGTH || !strings.Contains(credentials.ApiKey, constants.API_KEY_PREFIX) {
logger.Error(logs.INVALID_API_KEY)
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_API_KEY)
}
}
+ if credentials.TokenURI != "" {
+ if !isValidHTTPSURL(credentials.TokenURI) {
+ logger.Error(logs.INVALID_TOKEN_URI)
+ return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_TOKEN_URI)
+ }
+ }
+
// Roles validation
if credentials.Roles != nil {
if len(credentials.Roles) == 0 {
@@ -602,12 +605,12 @@ func ValidateInvokeConnectionRequest(request common.InvokeConnectionRequest) *sk
// Validate headers
if request.Headers != nil {
if len(request.Headers) == 0 {
- logger.Error(fmt.Sprintf(logs.EMPTY_REQUEST_HEADERS, "InvokeConnectionRequest"))
+ logger.Error(fmt.Sprintf(logs.EMPTY_REQUEST_HEADERS, constants.REQUEST_INVOKE_CONNECTION))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_REQUEST_HEADER)
}
for key, value := range request.Headers {
if key == "" || value == "" {
- logger.Error(fmt.Sprintf(logs.INVALID_REQUEST_HEADERS, "InvokeConnectionRequest"))
+ logger.Error(fmt.Sprintf(logs.INVALID_REQUEST_HEADERS, constants.REQUEST_INVOKE_CONNECTION))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_REQUEST_HEADERS)
}
}
@@ -616,15 +619,15 @@ func ValidateInvokeConnectionRequest(request common.InvokeConnectionRequest) *sk
// Validate path parameters
if request.PathParams != nil {
if len(request.PathParams) == 0 {
- logger.Error(fmt.Sprintf(logs.EMPTY_PATH_PARAMS, "InvokeConnectionRequest"))
+ logger.Error(fmt.Sprintf(logs.EMPTY_PATH_PARAMS, constants.REQUEST_INVOKE_CONNECTION))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_PARAMETERS)
}
for key, value := range request.PathParams {
if key == "" {
- logger.Error(fmt.Sprintf(logs.INVALID_PATH_PARAM, "InvokeConnectionRequest"))
+ logger.Error(fmt.Sprintf(logs.INVALID_PATH_PARAM, constants.REQUEST_INVOKE_CONNECTION))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_PARAMETER_NAME)
} else if value == "" {
- logger.Error(fmt.Sprintf(logs.INVALID_PATH_PARAM, "InvokeConnectionRequest"))
+ logger.Error(fmt.Sprintf(logs.INVALID_PATH_PARAM, constants.REQUEST_INVOKE_CONNECTION))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_PARAMETER_VALUE)
}
@@ -634,21 +637,24 @@ func ValidateInvokeConnectionRequest(request common.InvokeConnectionRequest) *sk
// Validate query parameters
if request.QueryParams != nil {
if len(request.QueryParams) == 0 {
- logger.Error(fmt.Sprintf(logs.EMPTY_QUERY_PARAMS, "InvokeConnectionRequest"))
+ logger.Error(fmt.Sprintf(logs.EMPTY_QUERY_PARAMS, constants.REQUEST_INVOKE_CONNECTION))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_QUERY_PARAM)
}
for key, value := range request.QueryParams {
if key == "" || value == nil || value == "" {
- logger.Error(fmt.Sprintf(logs.INVALID_QUERY_PARAM, "InvokeConnectionRequest"))
+ logger.Error(fmt.Sprintf(logs.INVALID_QUERY_PARAM, constants.REQUEST_INVOKE_CONNECTION))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_QUERY_PARAM)
}
}
}
// Validate body
if request.Body != nil {
- if len(request.Body) == 0 {
- logger.Error(fmt.Sprintf(logs.EMPTY_REQUEST_BODY, "InvokeConnectionRequest"))
- return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_REQUEST_BODY)
+ // Check if body is a map and if it's empty
+ if bodyMap, ok := request.Body.(map[string]interface{}); ok {
+ if len(bodyMap) == 0 {
+ logger.Error(fmt.Sprintf(logs.EMPTY_REQUEST_BODY, constants.REQUEST_INVOKE_CONNECTION))
+ return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_REQUEST_BODY)
+ }
}
}
if request.Method != "" {
@@ -662,7 +668,7 @@ func ValidateInvokeConnectionRequest(request common.InvokeConnectionRequest) *sk
}
func ValidateDetokenizeRequest(request common.DetokenizeRequest) *skyflowError.SkyflowError {
- tag := "DetokenizeRequest"
+ tag := constants.REQUEST_DETOKENIZE
if request.DetokenizeData == nil {
logger.Error(fmt.Sprintf(logs.DETOKENIZE_DATA_REQUIRED, tag))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.INVALID_DETOKENIZE_DATA)
@@ -682,7 +688,7 @@ func ValidateDetokenizeRequest(request common.DetokenizeRequest) *skyflowError.S
func ValidateGetRequest(getRequest common.GetRequest, options common.GetOptions) *skyflowError.SkyflowError {
// Check if the table is valid
- tag := "Get"
+ tag := constants.REQUEST_GET
if getRequest.Table == "" {
logger.Error(fmt.Sprintf(logs.EMPTY_TABLE, tag))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_TABLE)
@@ -760,7 +766,7 @@ func ValidateGetRequest(getRequest common.GetRequest, options common.GetOptions)
}
func ValidateDeleteRequest(request common.DeleteRequest) *skyflowError.SkyflowError {
- tag := "delete"
+ tag := constants.REQUEST_DELETE
if request.Table == "" {
logger.Error(fmt.Sprintf(logs.EMPTY_TABLE, tag))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_TABLE)
@@ -798,7 +804,7 @@ func ValidateTokenizeRequest(request []common.TokenizeRequest) *skyflowError.Sky
logger.Error(fmt.Sprintf(logs.EMPTY_COLUMN_GROUP_IN_COLUMN_VALUES, index))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_VALUE_IN_COLUMN_VALUES)
} else if tokenize.Value == "" {
- logger.Error(fmt.Sprintf(logs.EMPTY_OR_NULL_COLUMN_VALUE_IN_COLUMN_VALUES, "Tokenize", index))
+ logger.Error(fmt.Sprintf(logs.EMPTY_OR_NULL_COLUMN_VALUE_IN_COLUMN_VALUES, constants.REQUEST_TOKENIZE, index))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_COLUMN_VALUES)
}
}
@@ -807,7 +813,7 @@ func ValidateTokenizeRequest(request []common.TokenizeRequest) *skyflowError.Sky
}
func ValidateUpdateRequest(request common.UpdateRequest, options common.UpdateOptions) *skyflowError.SkyflowError {
- tag := "update"
+ tag := constants.REQUEST_UPDATE
skyflowId, _ := helpers.GetSkyflowID(request.Data)
if request.Table == "" {
logger.Error(fmt.Sprintf(logs.EMPTY_TABLE, tag))
@@ -821,14 +827,14 @@ func ValidateUpdateRequest(request common.UpdateRequest, options common.UpdateOp
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_DATA)
}
- for key, data := range request.Data {
- if data == "" {
- logger.Error(fmt.Sprintf(logs.EMPTY_OR_NULL_VALUE_IN_DATA, tag, key))
- return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_DATA_IN_DATA_KEY)
- } else if key == "" {
+ for key := range request.Data {
+ if key == "" {
logger.Error(fmt.Sprintf(logs.EMPTY_OR_NULL_KEY_IN_DATA, tag))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, skyflowError.EMPTY_KEY_IN_DATA)
}
+ if key == constants.SKYFLOW_ID {
+ continue
+ }
}
switch options.TokenMode {
case common.DISABLE:
@@ -859,7 +865,7 @@ func ValidateUpdateRequest(request common.UpdateRequest, options common.UpdateOp
}
// ValidateFileUploadRequest validates the required fields of FileUploadRequest.
func ValidateFileUploadRequest(req common.FileUploadRequest) *skyflowError.SkyflowError {
- tag := "UploadFile"
+tag := constants.REQUEST_UPLOAD_FILE
if strings.TrimSpace(req.Table) == "" {
@@ -917,9 +923,18 @@ func isValidHTTPURL(raw string) bool {
return false
}
- if u.Scheme != "http" && u.Scheme != "https" {
+ if u.Scheme != constants.HTTP_PROTOCOL && u.Scheme != constants.HTTPS_PROTOCOL {
return false
}
return u.Host != ""
}
+
+func isValidHTTPSURL(raw string) bool {
+ u, err := url.Parse(raw)
+ if err != nil {
+ return false
+ }
+
+ return u.Scheme == constants.HTTPS_PROTOCOL && u.Host != ""
+}
diff --git a/v2/internal/validation/validations_test.go b/v2/internal/validation/validations_test.go
index 4d8de5d..05b4298 100644
--- a/v2/internal/validation/validations_test.go
+++ b/v2/internal/validation/validations_test.go
@@ -165,8 +165,7 @@ var _ = Describe("ValidateTokensForInsertRequest", func() {
options := common.InsertOptions{}
err := ValidateInsertRequest(request, options)
- Expect(err).ToNot(BeNil())
- Expect(err.GetMessage()).To(ContainSubstring(errors.EMPTY_VALUE_IN_VALUES))
+ Expect(err).To(BeNil())
})
It("should return EMPTY_KEY_IN_VALUES when a key is empty", func() {
@@ -601,6 +600,39 @@ var _ = Describe("ValidateTokensForInsertRequest", func() {
Expect(err.GetCode()).To(ContainSubstring(string(errors.INVALID_INPUT_CODE)))
Expect(err.GetMessage()).To(ContainSubstring(errors.EMPTY_ROLE_IN_ROLES))
})
+
+ It("should return an error if tokenUri uses http scheme instead of https", func() {
+ credentials := common.Credentials{
+ Token: "token",
+ TokenURI: "http://invalid-uri.com",
+ }
+ err := ValidateCredentials(credentials)
+ Expect(err).To(HaveOccurred())
+ Expect(err.GetCode()).To(ContainSubstring(string(errors.INVALID_INPUT_CODE)))
+ Expect(err.GetMessage()).To(ContainSubstring(errors.INVALID_TOKEN_URI))
+ })
+
+ It("should return an error if tokenUri is malformed", func() {
+ credentials := common.Credentials{
+ Token: "token",
+ TokenURI: "not-a-valid-url",
+ }
+ err := ValidateCredentials(credentials)
+ Expect(err).To(HaveOccurred())
+ Expect(err.GetCode()).To(ContainSubstring(string(errors.INVALID_INPUT_CODE)))
+ Expect(err.GetMessage()).To(ContainSubstring(errors.INVALID_TOKEN_URI))
+ })
+
+ It("should return an error if tokenUri has https scheme but no host", func() {
+ credentials := common.Credentials{
+ Token: "token",
+ TokenURI: "https://",
+ }
+ err := ValidateCredentials(credentials)
+ Expect(err).To(HaveOccurred())
+ Expect(err.GetCode()).To(ContainSubstring(string(errors.INVALID_INPUT_CODE)))
+ Expect(err.GetMessage()).To(ContainSubstring(errors.INVALID_TOKEN_URI))
+ })
})
Context("Valid Credentials", func() {
@@ -644,6 +676,52 @@ var _ = Describe("ValidateTokensForInsertRequest", func() {
err := ValidateCredentials(credentials)
Expect(err).To(BeNil())
})
+
+ It("should return nil for valid tokenUri with https scheme", func() {
+ credentials := common.Credentials{
+ Token: "token",
+ TokenURI: "https://valid-token-uri.com",
+ }
+ err := ValidateCredentials(credentials)
+ Expect(err).To(BeNil())
+ })
+
+ It("should return nil for valid tokenUri with path and query parameters", func() {
+ credentials := common.Credentials{
+ Token: "token",
+ TokenURI: "https://valid-token-uri.com/path?param=value",
+ }
+ err := ValidateCredentials(credentials)
+ Expect(err).To(BeNil())
+ })
+
+ It("should return nil for credentials with Path and valid tokenUri", func() {
+ credentials := common.Credentials{
+ Path: "some/path",
+ TokenURI: "https://valid-token-uri.com",
+ }
+ err := ValidateCredentials(credentials)
+ Expect(err).To(BeNil())
+ })
+
+ It("should return nil for credentials with Token and valid roles and tokenUri", func() {
+ credentials := common.Credentials{
+ Token: "token",
+ Roles: []string{"admin", "user"},
+ TokenURI: "https://valid-token-uri.com",
+ }
+ err := ValidateCredentials(credentials)
+ Expect(err).To(BeNil())
+ })
+
+ It("should return nil for empty tokenUri (optional field)", func() {
+ credentials := common.Credentials{
+ Token: "token",
+ TokenURI: "",
+ }
+ err := ValidateCredentials(credentials)
+ Expect(err).To(BeNil())
+ })
})
})
})
@@ -837,8 +915,7 @@ var _ = Describe("ValidateTokensForInsertRequest", func() {
}
options := common.UpdateOptions{}
err := ValidateUpdateRequest(request, options)
- Expect(err).ToNot(BeNil())
- Expect(err.GetMessage()).To(ContainSubstring(errors.EMPTY_DATA_IN_DATA_KEY))
+ Expect(err).To(BeNil())
})
It("should return an error if a key is empty in data", func() {
diff --git a/v2/internal/vault/controller/connection_controller.go b/v2/internal/vault/controller/connection_controller.go
index 21135a8..552b8f7 100644
--- a/v2/internal/vault/controller/connection_controller.go
+++ b/v2/internal/vault/controller/connection_controller.go
@@ -6,14 +6,15 @@ import (
"encoding/json"
"fmt"
"io"
- "io/ioutil"
"mime/multipart"
"net/http"
+ "net/url"
"os"
"reflect"
"strconv"
"strings"
+ constants "github.com/skyflowapi/skyflow-go/v2/internal/constants"
"github.com/skyflowapi/skyflow-go/v2/internal/validation"
"github.com/skyflowapi/skyflow-go/v2/serviceaccount"
"github.com/skyflowapi/skyflow-go/v2/utils/common"
@@ -21,7 +22,10 @@ import (
"github.com/skyflowapi/skyflow-go/v2/utils/logger"
logs "github.com/skyflowapi/skyflow-go/v2/utils/messages"
- "github.com/hetiansu5/urlquery"
+)
+
+const (
+ formatValue = "%v"
)
type ConnectionController struct {
@@ -61,8 +65,8 @@ func setConnectionCredentials(config *common.ConnectionConfig, builderCreds *com
// here if builder credentials are available
if builderCreds != nil && !isCredentialsEmpty(*builderCreds) {
creds = *builderCreds
- } else if envCreds := os.Getenv("SKYFLOW_CREDENTIALS"); envCreds != "" {
- creds.CredentialsString = os.Getenv("SKYFLOW_CREDENTIALS")
+ } else if envCreds := os.Getenv(constants.SKYFLOW_CREDENTIALS_ENV); envCreds != "" {
+ creds.CredentialsString = os.Getenv(constants.SKYFLOW_CREDENTIALS_ENV)
} else {
return nil, errors.NewSkyflowError(errors.ErrorCodesEnum(errors.INVALID_INPUT_CODE), errors.EMPTY_CREDENTIALS)
}
@@ -73,7 +77,7 @@ func setConnectionCredentials(config *common.ConnectionConfig, builderCreds *com
}
func (v *ConnectionController) Invoke(ctx context.Context, request common.InvokeConnectionRequest) (*common.InvokeConnectionResponse, *errors.SkyflowError) {
- tag := "Invoke Connection"
+ tag := constants.REQUEST_INVOKE_CONN
logger.Info(logs.INVOKE_CONNECTION_TRIGGERED)
// Step 1: Validate Configuration
logger.Info(logs.VALIDATING_INVOKE_CONNECTION_REQUEST)
@@ -118,18 +122,70 @@ func (v *ConnectionController) Invoke(ctx context.Context, request common.Invoke
logger.Error(logs.INVOKE_CONNECTION_REQUEST_REJECTED)
return nil, errors.NewSkyflowError(errors.INVALID_INPUT_CODE, fmt.Sprintf(errors.UNKNOWN_ERROR, invokeErr.Error()))
}
+ // Ensure response body is closed to prevent resource leaks
+ if res.Body != nil {
+ defer res.Body.Close()
+ }
+
metaData := map[string]interface{}{
- "request_id": requestId,
+ constants.REQUEST_ID_KEY: requestId,
}
logger.Info(logs.INVOKE_CONNECTION_REQUEST_RESOLVED)
// Step 7: Parse Response
if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusMultipleChoices {
- parseRes, parseErr := parseResponse(res)
- if parseErr != nil {
- return nil, parseErr
+ response := common.InvokeConnectionResponse{Metadata: metaData}
+ if res.Body != nil {
+ contentType := res.Header.Get("Content-Type")
+ data, err := io.ReadAll(res.Body)
+ if err != nil {
+ return nil, errors.NewSkyflowError(errors.INVALID_INPUT_CODE, errors.INVALID_RESPONSE)
+ }
+ if strings.Contains(contentType, string(common.APPLICATIONXML)) || strings.Contains(contentType, string(common.TEXTORXML)) {
+ response.Data = string(data)
+ return &response, nil
+ } else if strings.Contains(contentType, string(common.APPLICATIONORJSON)) || contentType == "" {
+ var jsonData interface{}
+ err = json.Unmarshal(data, &jsonData)
+ if err != nil {
+ response.Data = data
+ return &response, nil
+ } else {
+ response.Data = jsonData
+ return &response, nil
+ }
+
+ } else if strings.Contains(contentType, string(common.TEXTORPLAIN)) {
+ response.Data = string(data)
+ return &response, nil
+ } else if strings.Contains(contentType, string(common.FORMURLENCODED)) {
+ // Parse URL-encoded form data
+ values, err := url.ParseQuery(string(data))
+ if err != nil {
+ return nil, errors.NewSkyflowError(errors.INVALID_INPUT_CODE, errors.INVALID_RESPONSE)
+ }
+ // Convert url.Values to map[string]interface{}
+ result := make(map[string]interface{})
+ for key, val := range values {
+ if len(val) == 1 {
+ result[key] = val[0]
+ } else {
+ result[key] = val
+ }
+ }
+ response.Data = result
+ return &response, nil
+ } else if strings.Contains(contentType, string(common.FORMDATA)) {
+ response.Data = string(data)
+ } else if strings.Contains(contentType, string(common.TEXTHTML)) {
+ response.Data = string(data)
+ return &response, nil
+ } else {
+ response.Data = string(data)
+ return &response, nil
+ }
}
- return &common.InvokeConnectionResponse{Data: parseRes, Metadata: metaData}, nil
+ return &response, nil
}
return nil, errors.SkyflowApiError(*res)
}
@@ -144,44 +200,185 @@ func buildRequestURL(baseURL string, pathParams map[string]string) string {
func prepareRequest(request common.InvokeConnectionRequest, url string) (*http.Request, error) {
var body io.Reader
var writer *multipart.Writer
- contentType := detectContentType(request.Headers)
+ var contentType string
+ shouldSetContentType := true
+
+ contentType = detectContentType(request.Headers)
+
+ // If no content-type and body is an object, default to JSON
+ if contentType == string(common.APPLICATIONORJSON) && request.Body != nil {
+ if _, ok := request.Body.(map[string]interface{}); ok {
+ contentType = string(common.APPLICATIONORJSON)
+ }
+ }
+
+ switch contentType {
+ case string(common.APPLICATIONORJSON):
+ if strBody, ok := request.Body.(string); ok {
+ body = strings.NewReader(strBody)
+ } else if bodyMap, ok := request.Body.(map[string]interface{}); ok {
+ data, err := json.Marshal(bodyMap)
+ if err != nil {
+ return nil, err
+ }
+ body = strings.NewReader(string(data))
+ } else if request.Body != nil {
+ if strBody, ok := request.Body.(string); ok {
+ body = strings.NewReader(strBody)
+ } else {
+ body = strings.NewReader(fmt.Sprintf(formatValue, request.Body))
+ }
+ }
- switch contentType {
case string(common.FORMURLENCODED):
- data, err := urlquery.Marshal(request.Body)
- if err != nil {
- return nil, err
+ if bodyMap, ok := request.Body.(map[string]interface{}); ok {
+ urlParams := buildURLEncodedParams(bodyMap)
+ body = strings.NewReader(urlParams.Encode())
+ } else { //need to check here
+ body = strings.NewReader("")
}
- body = strings.NewReader(string(data))
case string(common.FORMDATA):
buffer := new(bytes.Buffer)
writer = multipart.NewWriter(buffer)
- if err := writeFormData(writer, request.Body); err != nil {
- return nil, err
+
+ if bodyMap, ok := request.Body.(map[string]interface{}); ok {
+ for key, value := range bodyMap {
+ if value == nil {
+ continue
+ }
+
+ // Check if value is *os.File or io.Reader for file uploads
+ if file, ok := value.(*os.File); ok {
+ // Handle *os.File - create form file
+ part, err := writer.CreateFormFile(key, file.Name())
+ if err != nil {
+ return nil, err
+ }
+ if _, err := io.Copy(part, file); err != nil {
+ return nil, err
+ }
+ } else if reader, ok := value.(io.Reader); ok {
+ // Handle io.Reader - create form file with generic name
+ part, err := writer.CreateFormFile(key, key)
+ if err != nil {
+ return nil, err
+ }
+ if _, err := io.Copy(part, reader); err != nil {
+ return nil, err
+ }
+ } else if nestedMap, ok := value.(map[string]interface{}); ok {
+ // Check if value is a map/object - stringify it as JSON
+ jsonData, err := json.Marshal(nestedMap)
+ if err != nil {
+ return nil, err
+ }
+ if err := writer.WriteField(key, string(jsonData)); err != nil {
+ return nil, err
+ }
+ } else if arr, ok := value.([]interface{}); ok {
+ // Handle arrays - stringify as JSON
+ jsonData, err := json.Marshal(arr)
+ if err != nil {
+ return nil, err
+ }
+ if err := writer.WriteField(key, string(jsonData)); err != nil {
+ return nil, err
+ }
+ } else {
+ // Handle primitive values - convert to string
+ if err := writer.WriteField(key, fmt.Sprintf(formatValue, value)); err != nil {
+ return nil, err
+ }
+ }
+ }
+ } else if strBody, ok := request.Body.(string); ok {
+ // If body is already a string, use it as-is (though this is unusual for multipart)
+ body = strings.NewReader(strBody)
+ writer = nil // Don't use multipart writer for string body
+ shouldSetContentType = false // Keep user's content-type
+ } else if request.Body != nil {
+ // For other types, convert to string
+ body = strings.NewReader(fmt.Sprintf(formatValue, request.Body))
+ writer = nil
+ shouldSetContentType = false
+ }
+
+ if writer != nil {
+ writer.Close()
+ body = buffer
+ contentType = writer.FormDataContentType() // set with boundary
+ shouldSetContentType = true // Force set with boundary
}
- writer.Close()
- body = buffer
+ case string(common.APPLICATIONXML), string(common.TEXTORXML):
+ if strBody, ok := request.Body.(string); ok {
+ // Body is already a string (raw XML)
+ body = strings.NewReader(strBody)
+ } else if bodyMap, ok := request.Body.(map[string]interface{}); ok {
+ // Convert map to XML
+ data, err := mapToXML(bodyMap)
+ if err != nil {
+ return nil, err
+ }
+ body = bytes.NewReader(data)
+ } else {
+ // throw error for unsupported body type
+ return nil, errors.NewSkyflowError(errors.INVALID_INPUT_CODE, errors.INVALID_XML_FORMAT)
+ }
+
+ case string(common.TEXTORPLAIN):
+ if strBody, ok := request.Body.(string); ok {
+ body = strings.NewReader(strBody)
+ } else if request.Body != nil {
+ body = strings.NewReader(fmt.Sprintf(formatValue, request.Body))
+ }
+ case string(common.TEXTHTML):
+ if strBody, ok := request.Body.(string); ok {
+ body = strings.NewReader(strBody)
+ } else if bodyMap, ok := request.Body.(map[string]interface{}); ok {
+ // send map as json in body
+ data, err := json.Marshal(bodyMap)
+ if err != nil {
+ return nil, err
+ }
+ body = strings.NewReader(string(data))
+ } else if request.Body != nil {
+ body = strings.NewReader(fmt.Sprintf(formatValue, request.Body))
+ }
+
default:
- data, err := json.Marshal(request.Body)
- if err != nil {
- return nil, err
+ if strBody, ok := request.Body.(string); ok {
+ body = strings.NewReader(strBody)
+ } else if request.Body != nil {
+ if bodyMap, ok := request.Body.(map[string]interface{}); ok {
+ data, err := json.Marshal(bodyMap)
+ if err != nil {
+ return nil, err
+ }
+ body = strings.NewReader(string(data))
+ } else {
+ body = strings.NewReader(fmt.Sprintf(formatValue, request.Body))
+ }
}
- body = strings.NewReader(string(data))
}
if request.Method == "" {
request.Method = common.POST
}
request1, err := http.NewRequest(string(request.Method), url, body)
- if err == nil && writer != nil {
- request1.Header.Set("content-type", writer.FormDataContentType())
+ if err != nil {
+ return nil, err
+ }
+
+ // Set content-type header
+ if shouldSetContentType && contentType != "" {
+ request1.Header.Set("content-type", contentType)
}
- return request1, err
+ return request1, nil
}
func writeFormData(writer *multipart.Writer, requestBody interface{}) error {
- formData := rUrlencode(make([]interface{}, 0), make(map[string]string), requestBody)
+ formData := RUrlencode(make([]interface{}, 0), make(map[string]string), requestBody)
for key, value := range formData {
if err := writer.WriteField(key, value); err != nil {
return err
@@ -189,22 +386,52 @@ func writeFormData(writer *multipart.Writer, requestBody interface{}) error {
}
return nil
}
-func rUrlencode(parents []interface{}, pairs map[string]string, data interface{}) map[string]string {
+
+// buildURLEncodedParams converts a map to URL encoded params matching Node.js URLSearchParams behavior
+func buildURLEncodedParams(data map[string]interface{}) *url.Values {
+ params := url.Values{}
+
+ for key, value := range data {
+ if value == nil {
+ continue
+ }
+
+ // Check if value is a map (nested object)
+ if nestedMap, ok := value.(map[string]interface{}); ok {
+ for nestedKey, nestedValue := range nestedMap {
+ paramKey := fmt.Sprintf("%s[%s]", key, nestedKey)
+ params.Add(paramKey, fmt.Sprintf(formatValue, nestedValue))
+ }
+ } else if arr, ok := value.([]interface{}); ok {
+ // Handle arrays
+ for _, item := range arr {
+ params.Add(key, fmt.Sprintf(formatValue, item))
+ }
+ } else {
+ // Handle primitive values
+ params.Add(key, fmt.Sprintf(formatValue, value))
+ }
+ }
+
+ return ¶ms
+}
+
+func RUrlencode(parents []interface{}, pairs map[string]string, data interface{}) map[string]string {
switch reflect.TypeOf(data).Kind() {
case reflect.Int:
pairs[renderKey(parents)] = fmt.Sprintf("%d", data)
case reflect.Float32:
- pairs[renderKey(parents)] = fmt.Sprintf("%f", data)
+ pairs[renderKey(parents)] = fmt.Sprintf("%f", data) //nolint:revive
case reflect.Float64:
- pairs[renderKey(parents)] = fmt.Sprintf("%f", data)
+ pairs[renderKey(parents)] = fmt.Sprintf("%f", data) //nolint:revive
case reflect.Bool:
pairs[renderKey(parents)] = fmt.Sprintf("%t", data)
case reflect.Map:
var mapOfdata = (data).(map[string]interface{})
for index, value := range mapOfdata {
parents = append(parents, index)
- rUrlencode(parents, pairs, value)
+ RUrlencode(parents, pairs, value)
parents = parents[:len(parents)-1]
}
default:
@@ -218,7 +445,7 @@ func renderKey(parents []interface{}) string {
for index := range parents {
var typeOfindex = reflect.TypeOf(parents[index]).Kind()
if depth > 0 || typeOfindex == reflect.Int {
- outputString = outputString + fmt.Sprintf("[%v]", parents[index])
+ outputString = outputString + fmt.Sprintf("["+formatValue+"]", parents[index])
} else {
outputString = outputString + (parents[index]).(string)
}
@@ -228,7 +455,7 @@ func renderKey(parents []interface{}) string {
}
func detectContentType(headers map[string]string) string {
for key, value := range headers {
- if strings.ToLower(key) == "content-type" {
+ if strings.ToLower(key) == constants.HEADER_CONTENT_TYPE {
return value
}
}
@@ -255,13 +482,21 @@ func setQueryParams(request *http.Request, queryParams map[string]interface{}) *
}
func setHeaders(request *http.Request, api ConnectionController, invokeRequest common.InvokeConnectionRequest) {
if api.ApiKey != "" {
- request.Header.Set("x-skyflow-authorization", api.ApiKey)
+ request.Header.Set(constants.HEADER_AUTHORIZATION, api.ApiKey)
} else {
- request.Header.Set("x-skyflow-authorization", api.Token)
+ request.Header.Set(constants.HEADER_AUTHORIZATION, api.Token)
+ }
+
+ // Only set default content-type if not already set (preserve multipart boundary)
+ if request.Header.Get(constants.HEADER_CONTENT_TYPE) == "" {
+ request.Header.Set(constants.HEADER_CONTENT_TYPE, constants.CONTENT_TYPE_JSON)
}
- request.Header.Set("content-type", "application/json")
for key, value := range invokeRequest.Headers {
+ // Skip content-type from user headers to preserve the one set in prepareRequest (especially multipart boundaries)
+ if strings.ToLower(key) == constants.HEADER_CONTENT_TYPE {
+ continue
+ }
request.Header.Set(key, value)
}
}
@@ -269,21 +504,59 @@ func sendRequest(request *http.Request) (*http.Response, string, error) {
response, err := http.DefaultClient.Do(request)
requestId := ""
if response != nil {
- requestId = response.Header.Get("x-request-id")
+ requestId = response.Header.Get(constants.RESPONSE_HEADER_REQUEST_ID)
}
if err != nil {
return nil, requestId, err
}
return response, requestId, nil
}
-func parseResponse(response *http.Response) (map[string]interface{}, *errors.SkyflowError) {
- data, err := ioutil.ReadAll(response.Body)
- if err != nil {
- return nil, errors.NewSkyflowError(errors.INVALID_INPUT_CODE, errors.INVALID_RESPONSE)
+
+// mapToXML converts a map[string]interface{} to XML format
+func mapToXML(data map[string]interface{}) ([]byte, error) {
+ var buf bytes.Buffer
+ buf.WriteString("")
+ buf.WriteString("")
+
+ for key, value := range data {
+ writeXMLElement(&buf, key, value)
}
- var result map[string]interface{}
- if err1 := json.Unmarshal(data, &result); err1 != nil {
- return nil, errors.NewSkyflowError(errors.INVALID_INPUT_CODE, errors.INVALID_RESPONSE)
+
+ buf.WriteString("")
+ return buf.Bytes(), nil
+}
+
+// writeXMLElement recursively writes XML elements with proper escaping
+func writeXMLElement(buf *bytes.Buffer, key string, value interface{}) {
+ if value == nil {
+ buf.WriteString(fmt.Sprintf("<%s/>", key))
+ return
}
- return result, nil
+
+ switch v := value.(type) {
+ case map[string]interface{}:
+ buf.WriteString(fmt.Sprintf("<%s>", key))
+ for k, val := range v {
+ writeXMLElement(buf, k, val)
+ }
+ buf.WriteString(fmt.Sprintf("%s>", key))
+ case []interface{}:
+ for _, item := range v {
+ writeXMLElement(buf, key, item)
+ }
+ default:
+ // Escape special XML characters
+ escapedValue := escapeXML(fmt.Sprintf(formatValue, v))
+ buf.WriteString(fmt.Sprintf("<%s>%s%s>", key, escapedValue, key))
+ }
+}
+
+// escapeXML escapes special XML characters
+func escapeXML(s string) string {
+ s = strings.ReplaceAll(s, "&", "&")
+ s = strings.ReplaceAll(s, "<", "<")
+ s = strings.ReplaceAll(s, ">", ">")
+ s = strings.ReplaceAll(s, "\"", """)
+ s = strings.ReplaceAll(s, "'", "'")
+ return s
}
diff --git a/v2/internal/vault/controller/controller_test.go b/v2/internal/vault/controller/controller_test.go
index 1f3bae3..2179752 100644
--- a/v2/internal/vault/controller/controller_test.go
+++ b/v2/internal/vault/controller/controller_test.go
@@ -1,13 +1,18 @@
package controller_test
import (
+ // "bytes"
"context"
"encoding/json"
+ "errors"
"fmt"
+ // "io"
+ // "mime/multipart"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
+ "strings"
"testing"
"github.com/skyflowapi/skyflow-go/v2/internal/generated/option"
@@ -22,6 +27,13 @@ import (
skyflowError "github.com/skyflowapi/skyflow-go/v2/utils/error"
)
+// errorReader is a custom io.Reader that always returns an error
+type errorReader struct{}
+
+func (e *errorReader) Read(p []byte) (n int, err error) {
+ return 0, errors.New("simulated read error")
+}
+
var (
mockInsertSuccessJSON = `{"vaultID":"id", "responses":[{"Body":{"records":[{"skyflow_id":"skyflowid", "tokens":{"name_on_card":"token1"}}]}, "Status":200}]}`
mockInsertContinueFalseSuccessJSON = `{"records":[{"skyflow_id":"skyflowid1", "tokens":{"name":"nameToken1"}}, {"skyflow_id":"skyflowid2", "tokens":{"expiry_month":"monthToken", "name":"nameToken3"}}]}`
@@ -97,7 +109,7 @@ var _ = Describe("Vault controller Test cases", func() {
header := http.Header{}
header.Set("Content-Type", "application/json")
CreateRequestClientFunc = func(v *VaultController) *skyflowError.SkyflowError {
- if (v.CustomHeaders != nil ) {
+ if v.CustomHeaders != nil {
for key, value := range v.CustomHeaders {
header.Set(key, value)
}
@@ -1178,6 +1190,7 @@ var _ = Describe("ConnectionController", func() {
Context("when making a valid request", func() {
BeforeEach(func() {
mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"key": "value"}`))
}))
@@ -1240,6 +1253,7 @@ var _ = Describe("ConnectionController", func() {
})
It("should return an success from api with invalid body", func() {
mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Length", "0")
_, _ = w.Write([]byte(`67676`))
@@ -1249,13 +1263,15 @@ var _ = Describe("ConnectionController", func() {
return nil
}
response, err := ctrl.Invoke(ctx, mockRequest)
- Expect(response).To(BeNil())
- Expect(err).ToNot(BeNil())
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ Expect(response.Data).To(Equal(float64(67676)))
})
})
Context("Invoke with different content types", func() {
BeforeEach(func() {
mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"key": "value"}`))
}))
@@ -1432,20 +1448,2061 @@ var _ = Describe("ConnectionController", func() {
})
})
- })
+ Context("Handling XML content types", func() {
+ BeforeEach(func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/xml")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`value`))
+ }))
+ ctrl.Config.ConnectionUrl = mockServer.URL
+ })
-})
-var _ = Describe("VaultController", func() {
- var vaultController *VaultController
+ AfterEach(func() {
+ mockServer.Close()
+ })
- BeforeEach(func() {
- vaultController = &VaultController{
- Config: &VaultConfig{
- Credentials: Credentials{
- Path: "test/path",
- },
- },
- }
+ It("should handle application/xml content type with map body", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/xml",
+ },
+ Body: map[string]interface{}{
+ "key": "value",
+ "nested": map[string]interface{}{
+ "inner": "data",
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ Expect(response.Data).To(ContainSubstring("value"))
+ })
+
+ It("should handle text/xml content type", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "text/xml",
+ },
+ Body: map[string]interface{}{
+ "user": "john",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle XML with special characters requiring escaping", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/xml",
+ },
+ Body: map[string]interface{}{
+ "key": "value with & \"characters\" 'here'",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle XML with string body", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/xml",
+ },
+ Body: "- test
",
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle XML with arrays", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/xml",
+ },
+ Body: map[string]interface{}{
+ "items": []interface{}{"item1", "item2", "item3"},
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+ })
+
+ Context("Handling URL-encoded content with nested objects", func() {
+ BeforeEach(func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`key=value&nested=data`))
+ }))
+ ctrl.Config.ConnectionUrl = mockServer.URL
+ })
+
+ AfterEach(func() {
+ mockServer.Close()
+ })
+
+ It("should handle nested objects in URL-encoded format", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ Body: map[string]interface{}{
+ "user": map[string]interface{}{
+ "name": "john",
+ "age": 30,
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle arrays in URL-encoded format", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ Body: map[string]interface{}{
+ "tags": []interface{}{"tag1", "tag2", "tag3"},
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle mixed nested objects and arrays", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ Body: map[string]interface{}{
+ "user": map[string]interface{}{
+ "name": "john",
+ },
+ "tags": []interface{}{"tag1", "tag2"},
+ "key": "value",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+ })
+
+ Context("Handling multipart/form-data with file uploads", func() {
+ BeforeEach(func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"success": true}`))
+ }))
+ ctrl.Config.ConnectionUrl = mockServer.URL
+ })
+
+ AfterEach(func() {
+ mockServer.Close()
+ })
+
+ It("should handle multipart/form-data with *os.File", func() {
+ // Create a temporary file for testing
+ tmpFile, err := os.CreateTemp("", "test-*.txt")
+ Expect(err).To(BeNil())
+ defer os.Remove(tmpFile.Name())
+ _, _ = tmpFile.WriteString("test file content")
+ tmpFile.Close()
+
+ // Reopen for reading
+ file, err := os.Open(tmpFile.Name())
+ Expect(err).To(BeNil())
+ defer file.Close()
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "file": file,
+ "key": "value",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with io.Reader", func() {
+ reader := strings.NewReader("test content from reader")
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "upload": reader,
+ "name": "test",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with nested maps (JSON stringified)", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "user": map[string]interface{}{
+ "name": "john",
+ "age": 30,
+ },
+ "simple": "value",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with arrays (JSON stringified)", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "tags": []interface{}{"tag1", "tag2", "tag3"},
+ "key": "value",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+ })
+
+ Context("Handling text/plain and text/html content types", func() {
+ BeforeEach(func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ contentType := r.Header.Get("Content-Type")
+ w.Header().Set("Content-Type", contentType)
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte("plain text response"))
+ }))
+ ctrl.Config.ConnectionUrl = mockServer.URL
+ })
+
+ AfterEach(func() {
+ mockServer.Close()
+ })
+
+ It("should handle text/plain content type", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "text/plain",
+ },
+ Body: "This is plain text content",
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ Expect(response.Data).To(Equal("plain text response"))
+ })
+
+ It("should handle text/html content type", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "text/html",
+ },
+ Body: "Hello",
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle text/html with map body (converted to JSON)", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "text/html",
+ },
+ Body: map[string]interface{}{
+ "key": "value",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+ })
+
+ Context("Handling response parsing for different content types", func() {
+ It("should parse XML response correctly", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/xml")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`value`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "GET",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ Expect(response.Data).To(ContainSubstring("value"))
+ })
+
+ It("should parse URL-encoded response correctly", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`key1=value1&key2=value2&key3=value3a&key3=value3b`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "GET",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ // Response should be a map
+ dataMap, ok := response.Data.(map[string]interface{})
+ Expect(ok).To(BeTrue())
+ Expect(dataMap).To(HaveKey("key1"))
+ Expect(dataMap["key1"]).To(Equal("value1"))
+ // key3 should be an array since it has multiple values
+ Expect(dataMap).To(HaveKey("key3"))
+ })
+
+ It("should parse JSON response correctly", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"key": "value", "number": 42}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "GET",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ dataMap, ok := response.Data.(map[string]interface{})
+ Expect(ok).To(BeTrue())
+ Expect(dataMap["key"]).To(Equal("value"))
+ Expect(dataMap["number"]).To(Equal(float64(42)))
+ })
+
+ It("should parse text/plain response correctly", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/plain")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte("Simple plain text"))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "GET",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ Expect(response.Data).To(Equal("Simple plain text"))
+ })
+
+ It("should handle invalid JSON response gracefully", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`invalid json content`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "GET",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ // Should return as bytes when JSON parsing fails
+ Expect(response.Data).To(Equal([]byte("invalid json content")))
+ })
+
+ It("should handle invalid URL-encoded response gracefully", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`%invalid%`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "GET",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(response).To(BeNil())
+ Expect(err).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data response correctly", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "multipart/form-data")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`boundary data`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "GET",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ Expect(response.Data).To(Equal("boundary data"))
+ })
+
+ It("should handle URL-encoded response with multiple values for same key", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`color=red&color=blue&color=green`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "GET",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ dataMap, ok := response.Data.(map[string]interface{})
+ Expect(ok).To(BeTrue())
+ colors, ok := dataMap["color"].([]string)
+ Expect(ok).To(BeTrue())
+ Expect(len(colors)).To(Equal(3))
+ })
+
+ It("should handle multipart/form-data body as string", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: "raw string body",
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data body as non-map type", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: 12345,
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle text/plain body as non-string", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "text/plain",
+ },
+ Body: 98765,
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle text/html body as non-string", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "text/html",
+ },
+ Body: 54321,
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle unknown content-type with non-map body", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/octet-stream",
+ },
+ Body: 99999,
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle empty response body", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "GET",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle JSON body as string for application/json", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ Body: `{"test": "value"}`,
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle JSON body as non-map and non-string (integer)", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ Body: 12345,
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle URL-encoded body with non-map type", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ Body: "not a map",
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle XML body with unsupported type (not string or map)", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/xml",
+ },
+ Body: 12345,
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(response).To(BeNil())
+ Expect(err).ToNot(BeNil())
+ Expect(err.GetMessage()).To(ContainSubstring("Invalid XML format"))
+ })
+
+ It("should handle default method as POST when method is empty", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ Expect(r.Method).To(Equal("POST"))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ Body: map[string]interface{}{"test": "value"},
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle text/html body as map (converted to JSON)", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "text/html",
+ },
+ Body: map[string]interface{}{"html": "Title
"},
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle default content-type with map body", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/custom",
+ },
+ Body: map[string]interface{}{"key": "value"},
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle default content-type with string body", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "application/custom",
+ },
+ Body: "string body",
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with nil value in map", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "field1": "value1",
+ "field2": nil,
+ "field3": "value3",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ // Error handling tests for multipart/form-data
+ It("should handle multipart/form-data with complex nested map containing all valid types", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "nestedMap": map[string]interface{}{
+ "key1": "value1",
+ "key2": 123,
+ "key3": true,
+ "key4": 45.67,
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with array containing all valid types", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "arrayField": []interface{}{
+ "string",
+ 123,
+ true,
+ 45.67,
+ map[string]interface{}{"nested": "map"},
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with primitive string value", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "simpleString": "test value",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with primitive int value", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "simpleInt": 42,
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with primitive bool value", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "simpleBool": true,
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with primitive float value", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "simpleFloat": 3.14159,
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with map containing unsupported types that json.Marshal handles", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ // json.Marshal can handle most basic types, so this tests the success path
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "complexMap": map[string]interface{}{
+ "nullValue": nil,
+ "emptyString": "",
+ "zero": 0,
+ "negativInt": -42,
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with empty nested map", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "emptyMap": map[string]interface{}{},
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with empty array", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "emptyArray": []interface{}{},
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with deeply nested structure", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "level1": map[string]interface{}{
+ "level2": map[string]interface{}{
+ "level3": map[string]interface{}{
+ "data": "deep value",
+ },
+ },
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with array of arrays", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "matrix": []interface{}{
+ []interface{}{1, 2, 3},
+ []interface{}{4, 5, 6},
+ []interface{}{7, 8, 9},
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with mixed file and data fields", func() {
+ tmpFile, err := os.CreateTemp("", "test-mixed-*.txt")
+ Expect(err).To(BeNil())
+ defer os.Remove(tmpFile.Name())
+ _, _ = tmpFile.WriteString("file content")
+ tmpFile.Seek(0, 0)
+
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "file": tmpFile,
+ "name": "test file",
+ "metadata": map[string]interface{}{
+ "size": 12,
+ "type": "text",
+ },
+ "tags": []interface{}{"test", "sample"},
+ "count": 42,
+ "enabled": true,
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ // Error path tests for multipart/form-data operations
+ It("should handle error when file is closed before reading in multipart/form-data", func() {
+ tmpFile, err := os.CreateTemp("", "test-closed-*.txt")
+ Expect(err).To(BeNil())
+ fileName := tmpFile.Name()
+ _, _ = tmpFile.WriteString("file content")
+ // Close the file to trigger io.Copy error
+ tmpFile.Close()
+ defer os.Remove(fileName)
+
+ // Reopen file for deletion but create request with closed file handle
+ closedFile, _ := os.Open(fileName)
+ closedFile.Close() // Close immediately to trigger error
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "file": closedFile,
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ // Should return error due to closed file
+ Expect(err).ToNot(BeNil())
+ Expect(response).To(BeNil())
+ })
+
+ It("should handle multipart/form-data with types that json.Marshal can handle", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ // Test with all JSON-compatible types in nested map
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "complexData": map[string]interface{}{
+ "string": "value",
+ "number": 42,
+ "float": 3.14,
+ "bool": true,
+ "null": nil,
+ "array": []interface{}{1, 2, 3},
+ "nested": map[string]interface{}{"key": "val"},
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with arrays containing all JSON types", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "mixedArray": []interface{}{
+ "string",
+ 123,
+ 45.67,
+ true,
+ false,
+ nil,
+ map[string]interface{}{"nested": "object"},
+ []interface{}{1, 2, 3},
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with all primitive value types", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "stringField": "text value",
+ "intField": 42,
+ "floatField": 3.14159,
+ "boolField": true,
+ "zeroField": 0,
+ "emptyString": "",
+ "negativeInt": -100,
+ "negativeFloat": -99.99,
+ "falseField": false,
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with bytes.Reader as io.Reader", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ fileContent := []byte("This is file content from bytes.Reader")
+ reader := strings.NewReader(string(fileContent))
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "fileFromReader": reader,
+ "description": "File uploaded via io.Reader",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with multiple files of different types", func() {
+ // Create temp files
+ txtFile, _ := os.CreateTemp("", "test-*.txt")
+ _, _ = txtFile.WriteString("text content")
+ txtFile.Seek(0, 0)
+ defer os.Remove(txtFile.Name())
+
+ jsonFile, _ := os.CreateTemp("", "test-*.json")
+ _, _ = jsonFile.WriteString(`{"key": "value"}`)
+ jsonFile.Seek(0, 0)
+ defer os.Remove(jsonFile.Name())
+
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "textFile": txtFile,
+ "jsonFile": jsonFile,
+ "readerFile": strings.NewReader("reader content"),
+ "metadata": map[string]interface{}{"count": 2},
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with large nested structure", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "level1": map[string]interface{}{
+ "level2a": map[string]interface{}{
+ "level3": []interface{}{
+ map[string]interface{}{"id": 1, "name": "item1"},
+ map[string]interface{}{"id": 2, "name": "item2"},
+ },
+ },
+ "level2b": []interface{}{
+ []interface{}{1, 2, 3},
+ []interface{}{4, 5, 6},
+ },
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with special characters in primitive values", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "specialChars": "value with spaces & symbols !@#$%^&*()",
+ "unicode": "Hello δΈη π",
+ "quotes": `value with "quotes" and 'apostrophes'`,
+ "newlines": "line1\nline2\nline3",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ // Error path tests for FORMDATA case
+ It("should return error when io.Reader fails during io.Copy in multipart/form-data", func() {
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "failingReader": &errorReader{},
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ // Should return error due to failing reader
+ Expect(err).ToNot(BeNil())
+ Expect(response).To(BeNil())
+ })
+
+ It("should handle multipart/form-data with channel type causing json.Marshal to work with map", func() {
+ // Note: json.Marshal will handle most types, but channels, functions, and complex types cause issues
+ // However, since we're putting them in a map[string]interface{}, Go will handle the conversion
+ // This test verifies the happy path where json.Marshal succeeds even with edge case types
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "nestedData": map[string]interface{}{
+ "validString": "test",
+ "validNumber": 123,
+ "validBool": true,
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with array containing various valid types", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "arrayData": []interface{}{
+ "string",
+ 123,
+ 45.67,
+ true,
+ map[string]interface{}{"nested": "value"},
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with all primitive types as WriteField values", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "stringPrimitive": "text",
+ "intPrimitive": 42,
+ "floatPrimitive": 3.14,
+ "boolPrimitive": true,
+ "int64Primitive": int64(9223372036854775807),
+ "int32Primitive": int32(2147483647),
+ "float32Primitive": float32(3.14159),
+ "uint8Primitive": uint8(255),
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with empty string primitive", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "emptyString": "",
+ "whitespace": " ",
+ "tab": "\t",
+ "newline": "\n",
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with zero values for all numeric types", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "zeroInt": 0,
+ "zeroFloat": 0.0,
+ "zeroInt64": int64(0),
+ "zeroFloat32": float32(0.0),
+ "falseBool": false,
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with very long string primitive", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ longString := strings.Repeat("a", 10000)
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "longString": longString,
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with nested maps at multiple levels", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "nested1": map[string]interface{}{
+ "nested2": map[string]interface{}{
+ "nested3": map[string]interface{}{
+ "nested4": map[string]interface{}{
+ "value": "deeply nested",
+ },
+ },
+ },
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with arrays containing nested arrays", func() {
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "nestedArrays": []interface{}{
+ []interface{}{
+ []interface{}{
+ []interface{}{1, 2, 3},
+ },
+ },
+ },
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+
+ It("should handle multipart/form-data with combination of files, maps, arrays, and primitives", func() {
+ tmpFile, err := os.CreateTemp("", "combo-test-*.txt")
+ Expect(err).To(BeNil())
+ defer os.Remove(tmpFile.Name())
+ _, _ = tmpFile.WriteString("combo test content")
+ tmpFile.Seek(0, 0)
+
+ mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(`{"status": "ok"}`))
+ }))
+ defer mockServer.Close()
+ ctrl.Config.ConnectionUrl = mockServer.URL
+
+ request := InvokeConnectionRequest{
+ Method: "POST",
+ Headers: map[string]string{
+ "Content-Type": "multipart/form-data",
+ },
+ Body: map[string]interface{}{
+ "file1": tmpFile,
+ "file2": strings.NewReader("reader content"),
+ "map1": map[string]interface{}{"key": "value"},
+ "array1": []interface{}{1, 2, 3},
+ "string1": "text",
+ "int1": 42,
+ "bool1": true,
+ "float1": 3.14,
+ },
+ }
+ SetBearerTokenForConnectionControllerFunc = func(v *ConnectionController) *skyflowError.SkyflowError {
+ return nil
+ }
+ response, err := ctrl.Invoke(ctx, request)
+ Expect(err).To(BeNil())
+ Expect(response).ToNot(BeNil())
+ })
+ })
+
+})
+
+var _ = Describe("Connection Utility Functions", func() {
+ Describe("RUrlencode and renderKey", func() {
+ It("should handle int values", func() {
+ parents := make([]interface{}, 0)
+ pairs := make(map[string]string)
+ parents = append(parents, "count")
+ result := RUrlencode(parents, pairs, 42)
+ Expect(result["count"]).To(Equal("42"))
+ })
+
+ It("should handle float32 values", func() {
+ parents := make([]interface{}, 0)
+ pairs := make(map[string]string)
+ parents = append(parents, "price")
+ result := RUrlencode(parents, pairs, float32(19.99))
+ Expect(result["price"]).To(ContainSubstring("19.99"))
+ })
+
+ It("should handle float64 values", func() {
+ parents := make([]interface{}, 0)
+ pairs := make(map[string]string)
+ parents = append(parents, "amount")
+ result := RUrlencode(parents, pairs, float64(99.95))
+ Expect(result["amount"]).To(ContainSubstring("99.95"))
+ })
+
+ It("should handle bool values", func() {
+ parents := make([]interface{}, 0)
+ pairs := make(map[string]string)
+ parents = append(parents, "active")
+ result := RUrlencode(parents, pairs, true)
+ Expect(result["active"]).To(Equal("true"))
+ })
+
+ It("should handle string values", func() {
+ parents := make([]interface{}, 0)
+ pairs := make(map[string]string)
+ parents = append(parents, "name")
+ result := RUrlencode(parents, pairs, "John Doe")
+ Expect(result["name"]).To(Equal("John Doe"))
+ })
+
+ It("should handle nested map values", func() {
+ parents := make([]interface{}, 0)
+ pairs := make(map[string]string)
+ parents = append(parents, "user")
+ data := map[string]interface{}{
+ "name": "Alice",
+ "age": 30,
+ }
+ result := RUrlencode(parents, pairs, data)
+ Expect(result["user[name]"]).To(Equal("Alice"))
+ Expect(result["user[age]"]).To(Equal("30"))
+ })
+
+ It("should handle deeply nested map values", func() {
+ parents := make([]interface{}, 0)
+ pairs := make(map[string]string)
+ parents = append(parents, "user")
+ data := map[string]interface{}{
+ "profile": map[string]interface{}{
+ "firstName": "Bob",
+ "age": 25,
+ },
+ }
+ result := RUrlencode(parents, pairs, data)
+ Expect(result["user[profile][firstName]"]).To(Equal("Bob"))
+ Expect(result["user[profile][age]"]).To(Equal("25"))
+ })
+ })
+
+ // Describe("writeFormData", func() {
+ // It("should write form data with multiple types", func() {
+ // buffer := new(bytes.Buffer)
+ // writer := multipart.NewWriter(buffer)
+
+ // requestBody := map[string]interface{}{
+ // "string": "value",
+ // "number": 123,
+ // "bool": true,
+ // "nested": map[string]interface{}{
+ // "key": "nested_value",
+ // },
+ // }
+
+ // err := writeFormData(writer, requestBody)
+ // Expect(err).To(BeNil())
+ // writer.Close()
+
+ // content := buffer.String()
+ // Expect(content).To(ContainSubstring("string"))
+ // Expect(content).To(ContainSubstring("value"))
+ // Expect(content).To(ContainSubstring("number"))
+ // Expect(content).To(ContainSubstring("123"))
+ // Expect(content).To(ContainSubstring("bool"))
+ // Expect(content).To(ContainSubstring("true"))
+ // Expect(content).To(ContainSubstring("nested[key]"))
+ // Expect(content).To(ContainSubstring("nested_value"))
+ // })
+
+ // It("should handle float values in form data", func() {
+ // buffer := new(bytes.Buffer)
+ // writer := multipart.NewWriter(buffer)
+
+ // requestBody := map[string]interface{}{
+ // "price": float64(19.99),
+ // }
+
+ // err := writeFormData(writer, requestBody)
+ // Expect(err).To(BeNil())
+ // writer.Close()
+
+ // content := buffer.String()
+ // Expect(content).To(ContainSubstring("price"))
+ // Expect(content).To(ContainSubstring("19.99"))
+ // })
+ // })
+})
+
+})
+var _ = Describe("VaultController", func() {
+ var vaultController *VaultController
+
+ BeforeEach(func() {
+ vaultController = &VaultController{
+ Config: &VaultConfig{
+ Credentials: Credentials{
+ Path: "test/path",
+ },
+ },
+ }
+ })
+
+ Describe("GenerateToken", func() {
+ Context("tokenUri parameter handling", func() {
+ It("should handle tokenUri when provided with Token (no network call)", func() {
+ credentials := Credentials{
+ Token: "valid-token",
+ TokenURI: "https://custom-token-uri.com",
+ }
+ token, err := GenerateToken(credentials)
+ Expect(err).To(BeNil())
+ Expect(token).ToNot(BeNil())
+ Expect(*token).To(Equal("valid-token"))
+ })
+
+ It("should handle tokenUri when provided with ApiKey (no network call)", func() {
+ credentials := Credentials{
+ ApiKey: "sky-api-key-1234567890123456789012",
+ TokenURI: "https://custom-token-uri.com",
+ }
+ token, err := GenerateToken(credentials)
+ Expect(err).To(BeNil())
+ Expect(token).ToNot(BeNil())
+ Expect(*token).To(Equal("sky-api-key-1234567890123456789012"))
+ })
+
+ It("should pass empty tokenUri to BearerTokenOptions when not provided with Path", func() {
+ credentials := Credentials{
+ Path: "../../" + os.Getenv("CRED_FILE_PATH"),
+ TokenURI: "",
+ }
+ token, err := GenerateToken(credentials)
+ Expect(err).To(BeNil())
+ Expect(token).ToNot(BeNil())
+ })
+
+ It("should pass empty tokenUri to BearerTokenOptions when not provided with CredentialsString", func() {
+ credentials := Credentials{
+ CredentialsString: os.Getenv("VALID_CREDS_PVT_KEY"),
+ TokenURI: "",
+ }
+ token, err := GenerateToken(credentials)
+ Expect(err).To(BeNil())
+ Expect(token).ToNot(BeNil())
+ })
+
+ It("should pass roles and context to BearerTokenOptions along with Token and tokenUri", func() {
+ credentials := Credentials{
+ Token: "valid-token",
+ Roles: []string{"admin", "user"},
+ Context: "test-context",
+ TokenURI: "https://custom-token-uri.com",
+ }
+ token, err := GenerateToken(credentials)
+ Expect(err).To(BeNil())
+ Expect(token).ToNot(BeNil())
+ Expect(*token).To(Equal("valid-token"))
+ })
+ })
})
Context("SetBearerTokenForVaultController", func() {
diff --git a/v2/internal/vault/controller/detect_controller.go b/v2/internal/vault/controller/detect_controller.go
index b19b19e..bf82662 100644
--- a/v2/internal/vault/controller/detect_controller.go
+++ b/v2/internal/vault/controller/detect_controller.go
@@ -211,7 +211,7 @@ func CreateReidentifyTextRequest(request common.ReidentifyTextRequest, config co
// RedactedEntities
if len(request.RedactedEntities) > 0 {
- redactedEntities := CreateEntityTypes(request.RedactedEntities, "redacted").([]vaultapis.FormatRedactedItem)
+ redactedEntities := CreateEntityTypes(request.RedactedEntities, constants.DETECT_REDACTION_TYPE_REDACTED).([]vaultapis.FormatRedactedItem)
if len(redactedEntities) > 0 {
payload.Format.Redacted = redactedEntities
}
@@ -219,7 +219,7 @@ func CreateReidentifyTextRequest(request common.ReidentifyTextRequest, config co
// MaskedEntities
if len(request.MaskedEntities) > 0 {
- maskedEntities := CreateEntityTypes(request.MaskedEntities, "masked").([]vaultapis.FormatMaskedItem)
+ maskedEntities := CreateEntityTypes(request.MaskedEntities, constants.DETECT_REDACTION_TYPE_MASKED).([]vaultapis.FormatMaskedItem)
if len(maskedEntities) > 0 {
payload.Format.Masked = maskedEntities
}
@@ -227,7 +227,7 @@ func CreateReidentifyTextRequest(request common.ReidentifyTextRequest, config co
// PlainTextEntities
if len(request.PlainTextEntities) > 0 {
- plainTextEntities := CreateEntityTypes(request.PlainTextEntities, "plaintext").([]vaultapis.FormatPlaintextItem)
+ plainTextEntities := CreateEntityTypes(request.PlainTextEntities, constants.DETECT_REDACTION_TYPE_PLAINTEXT).([]vaultapis.FormatPlaintextItem)
if len(plainTextEntities) > 0 {
payload.Format.Plaintext = plainTextEntities
}
@@ -238,7 +238,7 @@ func CreateReidentifyTextRequest(request common.ReidentifyTextRequest, config co
func CreateTextFileRequest(request *common.DeidentifyFileRequest, base64Content, vaultID string) *vaultapis.DeidentifyFileRequestDeidentifyText {
var entityTypes []vaultapis.DeidentifyFileRequestDeidentifyTextEntityTypesItem
- if result := CreateEntityTypesRef(request.Entities, "text"); result != nil {
+ if result := CreateEntityTypesRef(request.Entities, constants.FILE_TYPE_TEXT); result != nil {
entityTypes = result.([]vaultapis.DeidentifyFileRequestDeidentifyTextEntityTypesItem)
}
return &vaultapis.DeidentifyFileRequestDeidentifyText{
@@ -256,7 +256,7 @@ func CreateTextFileRequest(request *common.DeidentifyFileRequest, base64Content,
func CreateImageRequest(request *common.DeidentifyFileRequest, base64Content, vaultId, fileExt string) *vaultapis.DeidentifyFileImageRequestDeidentifyImage {
var entityTypes []vaultapis.DeidentifyFileImageRequestDeidentifyImageEntityTypesItem
- if result := CreateEntityTypesRef(request.Entities, "image"); result != nil {
+ if result := CreateEntityTypesRef(request.Entities, constants.FILE_TYPE_IMAGE); result != nil {
entityTypes = result.([]vaultapis.DeidentifyFileImageRequestDeidentifyImageEntityTypesItem)
}
return &vaultapis.DeidentifyFileImageRequestDeidentifyImage{
@@ -277,7 +277,7 @@ func CreateImageRequest(request *common.DeidentifyFileRequest, base64Content, va
func CreatePdfRequest(request *common.DeidentifyFileRequest, base64Content, vaultID string) *vaultapis.DeidentifyFileDocumentPdfRequestDeidentifyPdf {
var entityTypes []vaultapis.DeidentifyFileDocumentPdfRequestDeidentifyPdfEntityTypesItem
- if result := CreateEntityTypesRef(request.Entities, "pdf"); result != nil {
+ if result := CreateEntityTypesRef(request.Entities, constants.FILE_TYPE_PDF); result != nil {
entityTypes = result.([]vaultapis.DeidentifyFileDocumentPdfRequestDeidentifyPdfEntityTypesItem)
}
return &vaultapis.DeidentifyFileDocumentPdfRequestDeidentifyPdf{
@@ -296,7 +296,7 @@ func CreatePdfRequest(request *common.DeidentifyFileRequest, base64Content, vaul
func CreatePresentationRequest(request *common.DeidentifyFileRequest, base64Content, vaultID, fileExt string) *vaultapis.DeidentifyFileRequestDeidentifyPresentation {
var entityTypes []vaultapis.DeidentifyFileRequestDeidentifyPresentationEntityTypesItem
- if result := CreateEntityTypesRef(request.Entities, "ppt"); result != nil {
+ if result := CreateEntityTypesRef(request.Entities, constants.FILE_TYPE_PPT); result != nil {
entityTypes = result.([]vaultapis.DeidentifyFileRequestDeidentifyPresentationEntityTypesItem)
}
return &vaultapis.DeidentifyFileRequestDeidentifyPresentation{
@@ -314,7 +314,7 @@ func CreatePresentationRequest(request *common.DeidentifyFileRequest, base64Cont
func CreateSpreadsheetRequest(request *common.DeidentifyFileRequest, base64Content, vaultID, fileExt string) *vaultapis.DeidentifyFileRequestDeidentifySpreadsheet {
var entityTypes []vaultapis.DeidentifyFileRequestDeidentifySpreadsheetEntityTypesItem
- if result := CreateEntityTypesRef(request.Entities, "spread").([]vaultapis.DeidentifyFileRequestDeidentifySpreadsheetEntityTypesItem); result != nil {
+ if result := CreateEntityTypesRef(request.Entities, constants.FILE_TYPE_SPREAD).([]vaultapis.DeidentifyFileRequestDeidentifySpreadsheetEntityTypesItem); result != nil {
entityTypes = result
}
return &vaultapis.DeidentifyFileRequestDeidentifySpreadsheet{
@@ -332,7 +332,7 @@ func CreateSpreadsheetRequest(request *common.DeidentifyFileRequest, base64Conte
func CreateDocumentRequest(request *common.DeidentifyFileRequest, base64Content, vaultID, fileExt string) *vaultapis.DeidentifyFileRequestDeidentifyDocument {
var entityTypes []vaultapis.DeidentifyFileRequestDeidentifyDocumentEntityTypesItem
- if result := CreateEntityTypesRef(request.Entities, "document").([]vaultapis.DeidentifyFileRequestDeidentifyDocumentEntityTypesItem); result != nil {
+ if result := CreateEntityTypesRef(request.Entities, constants.FILE_TYPE_DOCUMENT).([]vaultapis.DeidentifyFileRequestDeidentifyDocumentEntityTypesItem); result != nil {
entityTypes = result
}
return &vaultapis.DeidentifyFileRequestDeidentifyDocument{
@@ -350,7 +350,7 @@ func CreateDocumentRequest(request *common.DeidentifyFileRequest, base64Content,
func CreateStructuredTextRequest(request *common.DeidentifyFileRequest, base64Content, vaultID, fileExt string) *vaultapis.DeidentifyFileRequestDeidentifyStructuredText {
var entityTypes []vaultapis.DeidentifyFileRequestDeidentifyStructuredTextEntityTypesItem
- if result := CreateEntityTypesRef(request.Entities, "structured").([]vaultapis.DeidentifyFileRequestDeidentifyStructuredTextEntityTypesItem); result != nil {
+ if result := CreateEntityTypesRef(request.Entities, constants.FILE_TYPE_STRUCTURED).([]vaultapis.DeidentifyFileRequestDeidentifyStructuredTextEntityTypesItem); result != nil {
entityTypes = result
}
return &vaultapis.DeidentifyFileRequestDeidentifyStructuredText{
@@ -369,7 +369,7 @@ func CreateStructuredTextRequest(request *common.DeidentifyFileRequest, base64Co
func CreateAudioRequest(request *common.DeidentifyFileRequest, base64Content, vaultID, fileExt string) *vaultapis.DeidentifyFileAudioRequestDeidentifyAudio {
var entityTypes []vaultapis.DeidentifyFileAudioRequestDeidentifyAudioEntityTypesItem
- if result := CreateEntityTypesRef(request.Entities, "audio").([]vaultapis.DeidentifyFileAudioRequestDeidentifyAudioEntityTypesItem); result != nil {
+ if result := CreateEntityTypesRef(request.Entities, constants.FILE_TYPE_AUDIO).([]vaultapis.DeidentifyFileAudioRequestDeidentifyAudioEntityTypesItem); result != nil {
entityTypes = result
}
req := &vaultapis.DeidentifyFileAudioRequestDeidentifyAudio{
@@ -400,7 +400,7 @@ func CreateAudioRequest(request *common.DeidentifyFileRequest, base64Content, va
func CreateGenericFileRequest(request *common.DeidentifyFileRequest, base64Content, vaultID, fileExtension string) *vaultapis.DeidentifyFileRequest {
var entityTypes []vaultapis.DeidentifyFileRequestEntityTypesItem
- if result := CreateEntityTypesRef(request.Entities, "generic").([]vaultapis.DeidentifyFileRequestEntityTypesItem); result != nil {
+ if result := CreateEntityTypesRef(request.Entities, constants.FILE_TYPE_GENERIC).([]vaultapis.DeidentifyFileRequestEntityTypesItem); result != nil {
entityTypes = result
}
return &vaultapis.DeidentifyFileRequest{
@@ -424,63 +424,63 @@ func CreateEntityTypesRef(entities []common.DetectEntities, dataType string) any
switch strings.ToLower(dataType) {
- case "text":
+ case constants.FILE_TYPE_TEXT:
entityTypes := make([]vaultapis.DeidentifyFileRequestDeidentifyTextEntityTypesItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.DeidentifyFileRequestDeidentifyTextEntityTypesItem(e)
}
return entityTypes
- case "image":
+ case constants.FILE_TYPE_IMAGE:
entityTypes := make([]vaultapis.DeidentifyFileImageRequestDeidentifyImageEntityTypesItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.DeidentifyFileImageRequestDeidentifyImageEntityTypesItem(e)
}
return entityTypes
- case "pdf":
+ case constants.FILE_TYPE_PDF:
entityTypes := make([]vaultapis.DeidentifyFileDocumentPdfRequestDeidentifyPdfEntityTypesItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.DeidentifyFileDocumentPdfRequestDeidentifyPdfEntityTypesItem(e)
}
return entityTypes
- case "ppt":
+ case constants.FILE_TYPE_PPT:
entityTypes := make([]vaultapis.DeidentifyFileRequestDeidentifyPresentationEntityTypesItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.DeidentifyFileRequestDeidentifyPresentationEntityTypesItem(e)
}
return entityTypes
- case "spread":
+ case constants.FILE_TYPE_SPREAD:
entityTypes := make([]vaultapis.DeidentifyFileRequestDeidentifySpreadsheetEntityTypesItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.DeidentifyFileRequestDeidentifySpreadsheetEntityTypesItem(e)
}
return entityTypes
- case "document":
+ case constants.FILE_TYPE_DOCUMENT:
entityTypes := make([]vaultapis.DeidentifyFileRequestDeidentifyDocumentEntityTypesItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.DeidentifyFileRequestDeidentifyDocumentEntityTypesItem(e)
}
return entityTypes
- case "structured":
+ case constants.FILE_TYPE_STRUCTURED:
entityTypes := make([]vaultapis.DeidentifyFileRequestDeidentifyStructuredTextEntityTypesItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.DeidentifyFileRequestDeidentifyStructuredTextEntityTypesItem(e)
}
return entityTypes
- case "audio":
+ case constants.FILE_TYPE_AUDIO:
entityTypes := make([]vaultapis.DeidentifyFileAudioRequestDeidentifyAudioEntityTypesItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.DeidentifyFileAudioRequestDeidentifyAudioEntityTypesItem(e)
}
return entityTypes
- case "generic":
+ case constants.FILE_TYPE_GENERIC:
entityTypes := make([]vaultapis.DeidentifyFileRequestEntityTypesItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.DeidentifyFileRequestEntityTypesItem(e)
@@ -498,21 +498,21 @@ func CreateEntityTypes(entities []common.DetectEntities, entityType string) any
}
switch strings.ToLower(entityType) {
- case "redacted":
+ case constants.ENTITY_TYPE_REDACTED:
entityTypes := make([]vaultapis.FormatRedactedItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.FormatRedactedItem(e)
}
return entityTypes
- case "masked":
+ case constants.ENTITY_TYPE_MASKED:
entityTypes := make([]vaultapis.FormatMaskedItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.FormatMaskedItem(e)
}
return entityTypes
- case "plaintext":
+ case constants.DETECT_REDACTION_TYPE_PLAINTEXT:
entityTypes := make([]vaultapis.FormatPlaintextItem, len(entities))
for i, e := range entities {
entityTypes[i] = vaultapis.FormatPlaintextItem(e)
@@ -833,21 +833,21 @@ func (d *DetectController) processFileByType(ctx context.Context, fileExtension,
var apiErr error
switch fileExtension {
- case "txt":
+ case constants.FILE_EXTENSION_TXT:
apiResponse, apiErr = d.FilesApiClient.DeidentifyText(ctx, CreateTextFileRequest(request, base64Content, d.Config.VaultId))
- case "mp3", "wav":
+ case constants.FILE_EXTENSION_MP3, constants.FILE_EXTENSION_WAV:
apiResponse, apiErr = d.FilesApiClient.DeidentifyAudio(ctx, CreateAudioRequest(request, base64Content, d.Config.VaultId, fileExtension))
- case "pdf":
+ case constants.FILE_EXTENSION_PDF:
apiResponse, apiErr = d.FilesApiClient.DeidentifyPdf(ctx, CreatePdfRequest(request, base64Content, d.Config.VaultId))
- case "jpg", "jpeg", "png", "bmp", "tif", "tiff":
+ case constants.FILE_EXTENSION_JPG, constants.FILE_EXTENSION_JPEG, constants.FILE_EXTENSION_PNG, constants.FILE_EXTENSION_BMP, constants.FILE_EXTENSION_TIF, constants.FILE_EXTENSION_TIFF:
apiResponse, apiErr = d.FilesApiClient.DeidentifyImage(ctx, CreateImageRequest(request, base64Content, d.Config.VaultId, fileExtension))
- case "ppt", "pptx":
+ case constants.FILE_EXTENSION_PPT, constants.FILE_EXTENSION_PPTX:
apiResponse, apiErr = d.FilesApiClient.DeidentifyPresentation(ctx, CreatePresentationRequest(request, base64Content, d.Config.VaultId, fileExtension))
- case "csv", "xls", "xlsx":
+ case constants.FILE_EXTENSION_CSV, constants.FILE_EXTENSION_XLS, constants.FILE_EXTENSION_XLSX:
apiResponse, apiErr = d.FilesApiClient.DeidentifySpreadsheet(ctx, CreateSpreadsheetRequest(request, base64Content, d.Config.VaultId, fileExtension))
- case "doc", "docx":
+ case constants.FILE_EXTENSION_DOC, constants.FILE_EXTENSION_DOCX:
apiResponse, apiErr = d.FilesApiClient.DeidentifyDocument(ctx, CreateDocumentRequest(request, base64Content, d.Config.VaultId, fileExtension))
- case "json", "xml":
+ case constants.FILE_EXTENSION_JSON, constants.FILE_EXTENSION_XML:
apiResponse, apiErr = d.FilesApiClient.DeidentifyStructuredText(ctx, CreateStructuredTextRequest(request, base64Content, d.Config.VaultId, fileExtension))
default:
apiResponse, apiErr = d.FilesApiClient.DeidentifyFile(ctx, CreateGenericFileRequest(request, base64Content, d.Config.VaultId, fileExtension))
@@ -918,7 +918,7 @@ func processDeidentifyFileResponse(data *vaultapis.DetectRunsResponse, outputDir
return nil
}
- deidentifyFilePrefix := "processed-"
+ deidentifyFilePrefix := constants.PROCESSED_PREFIX
processedFile := data.Output[0].ProcessedFile
if processedFile != nil {
decodedBytes, err := base64.StdEncoding.DecodeString(string(*processedFile))
@@ -980,9 +980,9 @@ func parseDeidentifyFileResponse(response *vaultapis.DetectRunsResponse, runID s
return nil, errors.New(string(skyflowError.SERVER) + logs.FAILED_TO_DECODE_PROCESSED_FILE)
}
fileResponse.File = common.FileInfo{
- Name: "deidentified." + string(*firstOutput.ProcessedFileExtension),
+ Name: constants.DEIDENTIFIED_FILE_PREFIX + string(*firstOutput.ProcessedFileExtension),
Size: int64(len(decodedBytes)),
- Type: "redacted_file",
+ Type: constants.FILE_OUTPUT_TYPE_REDACTED_FILE,
LastModified: time.Now().UnixMilli(),
}
fileResponse.FileBase64 = *firstOutput.ProcessedFile
@@ -992,7 +992,7 @@ func parseDeidentifyFileResponse(response *vaultapis.DetectRunsResponse, runID s
if firstOutput.ProcessedFileType != nil {
fileResponse.Type = string(*firstOutput.ProcessedFileType)
} else {
- fileResponse.Type = "UNKNOWN"
+ fileResponse.Type = constants.UNKNOWN_STATUS
}
if firstOutput.ProcessedFileExtension != nil {
diff --git a/v2/internal/vault/controller/vault_controller.go b/v2/internal/vault/controller/vault_controller.go
index b1f77b5..2b04f22 100644
--- a/v2/internal/vault/controller/vault_controller.go
+++ b/v2/internal/vault/controller/vault_controller.go
@@ -42,6 +42,9 @@ func GenerateToken(credentials common.Credentials) (*string, *skyflowError.Skyfl
if credentials.Context != "" {
options.Ctx = credentials.Context
}
+ if credentials.TokenURI != "" {
+ options.TokenURI = credentials.TokenURI
+ }
switch {
case credentials.Token != "":
bearerToken = credentials.Token
@@ -143,8 +146,8 @@ func setVaultCredentials(config *common.VaultConfig, builderCreds *common.Creden
// here if builder credentials are available
if builderCreds != nil && !isCredentialsEmpty(*builderCreds) {
creds = *builderCreds
- } else if envCreds := os.Getenv("SKYFLOW_CREDENTIALS"); envCreds != "" {
- creds.CredentialsString = os.Getenv("SKYFLOW_CREDENTIALS")
+ } else if envCreds := os.Getenv(constants.SKYFLOW_CREDENTIALS_ENV); envCreds != "" {
+ creds.CredentialsString = os.Getenv(constants.SKYFLOW_CREDENTIALS_ENV)
} else {
return nil, skyflowError.NewSkyflowError(skyflowError.ErrorCodesEnum(skyflowError.INVALID_INPUT_CODE), skyflowError.EMPTY_CREDENTIALS)
}
@@ -220,11 +223,11 @@ func (v *VaultController) Insert(ctx context.Context, request common.InsertReque
if parseErr != nil {
return nil, parseErr
}
- if formattedRecord["skyflow_id"] != nil {
+ if formattedRecord[constants.SKYFLOW_ID] != nil {
insertedFields = append(insertedFields, formattedRecord)
} else {
- formattedRecord["RequestId"] = header.Get(constants.REQUEST_KEY)
- formattedRecord["HttpCode"] = skyflowError.INVALID_INPUT_CODE
+ formattedRecord[constants.RESPONSE_KEY_REQUEST_ID] = header.Get(constants.REQUEST_KEY)
+ formattedRecord[constants.RESPONSE_KEY_HTTP_CODE] = skyflowError.INVALID_INPUT_CODE
errors = append(errors, formattedRecord)
}
}
@@ -504,7 +507,7 @@ func (v *VaultController) Update(ctx context.Context, request common.UpdateReque
var updatedField map[string]interface{}
updatedField = make(map[string]interface{})
updatedField = res
- updatedField["skyflowId"] = *id
+ updatedField[constants.RESPONSE_KEY_SKYFLOW_ID] = *id
return &common.UpdateResponse{
UpdatedField: updatedField,
Errors: nil,
diff --git a/v2/utils/common/common.go b/v2/utils/common/common.go
index c94d243..fb530a8 100644
--- a/v2/utils/common/common.go
+++ b/v2/utils/common/common.go
@@ -167,6 +167,7 @@ type BearerTokenOptions struct {
Ctx string
RoleIDs []string
LogLevel logger.LogLevel
+ TokenURI string
}
type SignedDataTokensOptions struct {
@@ -174,6 +175,7 @@ type SignedDataTokensOptions struct {
TimeToLive int
Ctx string
LogLevel logger.LogLevel
+ TokenURI string
}
type SignedDataTokensResponse struct {
@@ -196,6 +198,7 @@ type Credentials struct {
CredentialsString string
Token string
ApiKey string
+ TokenURI string
}
type ConnectionConfig struct {
ConnectionId string
@@ -218,7 +221,7 @@ const (
)
type InvokeConnectionResponse struct {
- Data map[string]interface{}
+ Data interface{}
Metadata map[string]interface{}
Errors map[string]interface{}
}
@@ -346,7 +349,7 @@ type InvokeConnectionRequest struct {
Method RequestMethod
QueryParams map[string]interface{}
PathParams map[string]string
- Body map[string]interface{}
+ Body interface{}
Headers map[string]string
}
type ContentType string
@@ -357,6 +360,8 @@ const (
FORMURLENCODED ContentType = "application/x-www-form-urlencoded"
FORMDATA ContentType = "multipart/form-data"
TEXTORXML ContentType = "text/xml"
+ APPLICATIONXML ContentType = "application/xml"
+ TEXTHTML ContentType = "text/html"
)
type OrderByEnum string
diff --git a/v2/utils/error/message.go b/v2/utils/error/message.go
index 6a1bd12..cdd8c24 100644
--- a/v2/utils/error/message.go
+++ b/v2/utils/error/message.go
@@ -1,10 +1,11 @@
package errors
-import "github.com/skyflowapi/skyflow-go/v2/internal/constants"
+import internal "github.com/skyflowapi/skyflow-go/v2/internal/constants"
// TO DO
const (
// config
+ INVALID_XML_FORMAT string = internal.SDK_PREFIX + " Validation error. Invalid XML format. Specify a valid XML format as string."
VAULT_ID_ALREADY_IN_CONFIG_LIST string = internal.SDK_PREFIX + " Validation error. VaultId is present in an existing config. Specify a new vaultId in config."
VAULT_ID_NOT_IN_CONFIG_LIST string = internal.SDK_PREFIX + " Validation error. VaultId is missing from the config. Specify the vaultIds from configs."
CONNECTION_ID_NOT_IN_CONFIG_LIST string = internal.SDK_PREFIX + " Validation error. ConnectionId is missing from the config. Specify the connectionIds from configs."
@@ -35,7 +36,7 @@ const (
MISSING_CLIENT_ID string = internal.SDK_PREFIX + " Initialization failed. Unable to read client ID in credentials. Verify your client ID."
MISSING_KEY_ID string = internal.SDK_PREFIX + " Initialization failed. Unable to read key ID in credentials. Verify your key ID."
MISSING_TOKEN_URI string = internal.SDK_PREFIX + " Initialization failed. Unable to read token URI in credentials. Verify your token URI."
- INVALID_TOKEN_URI string = internal.SDK_PREFIX + " Initialization failed. Token URI in not a valid URL in credentials. Verify your token URI."
+ INVALID_TOKEN_URI string = internal.SDK_PREFIX + " Initialization failed. Invalid Skyflow credentials. The token URI must be a string and a valid URL."
JWT_INVALID_FORMAT string = internal.SDK_PREFIX + " Initialization failed. Invalid private key format. Verify your credentials."
INVALID_ALGORITHM string = internal.SDK_PREFIX + " Initialization failed. Invalid algorithm to parse private key. Specify valid algorithm."
INVALID_KEY_SPEC string = internal.SDK_PREFIX + " Initialization failed. Unable to parse RSA private key. Verify your credentials."
@@ -120,6 +121,7 @@ const (
UNKNOWN_ERROR string = internal.SDK_PREFIX + " Error occurred. %s"
INVALID_BYOT string = internal.SDK_PREFIX + " Validation error. Invalid BYOT."
SKYFLOW_ID_KEY_ERROR string = internal.SDK_PREFIX + " Validation error. 'skyflow_id' is missing from the data payload. Specify a 'skyflow_id'."
+ FAILED_TO_UNMARSHAL_ERROR string = internal.SDK_PREFIX + "Failed to unmarshal error"
COLUMN_NAME_KEY_ERROR_FILE_UPLOAD = internal.SDK_PREFIX + " Validation error. columnName is missing from the payload. Specify a columnName key."
MISSING_FILE_SOURCE_IN_UPLOAD_FILE = internal.SDK_PREFIX + " Validation error. Provide exactly one of filePath, base64, or fileObject."
FILE_NAME_MUST_BE_PROVIDED_WITH_FILE_OBJECT = internal.SDK_PREFIX + " Validation error. fileName must be provided when using fileObject."
diff --git a/v2/utils/error/skyflow_exception.go b/v2/utils/error/skyflow_exception.go
index 8894094..15bd6ce 100644
--- a/v2/utils/error/skyflow_exception.go
+++ b/v2/utils/error/skyflow_exception.go
@@ -27,10 +27,10 @@ func (se *SkyflowError) Error() string {
if se.originalError != nil {
return fmt.Sprintf("Message: %s, Original Error (if any): %s", se.message, se.originalError.Error())
}
- return fmt.Sprintf("Message: %s", se.message)
+ return fmt.Sprintf("Message: %s", se.message) //nolint:revive
}
func (se *SkyflowError) GetMessage() string {
- return fmt.Sprintf("Message: %s", se.message)
+ return fmt.Sprintf("Message: %s", se.message) //nolint:revive
}
func (se *SkyflowError) GetCode() string {
return fmt.Sprintf("Code: %s", se.httpCode)
@@ -54,62 +54,62 @@ func NewSkyflowError(code ErrorCodesEnum, message string) *SkyflowError {
return &SkyflowError{
httpCode: string(code),
message: message,
- httpStatusCode: string("Bad Request"),
+ httpStatusCode: constants.HTTP_STATUS_BAD_REQUEST,
}
}
func SkyflowApiError(responseHeaders http.Response) *SkyflowError {
skyflowError := SkyflowError{
requestId: responseHeaders.Header.Get(constants.REQUEST_KEY),
}
- if responseHeaders.Header.Get("Content-Type") == "application/json" {
+ if responseHeaders.Header.Get(constants.HEADER_CONTENT_TYPE_CAPITAL) == constants.CONTENT_TYPE_JSON {
bodyBytes, _ := io.ReadAll(responseHeaders.Body)
// Parse JSON into a struct
var apiError map[string]interface{}
if err := json.Unmarshal(bodyBytes, &apiError); err != nil {
- return NewSkyflowError(INVALID_INPUT_CODE, "Failed to unmarhsal error")
+ return NewSkyflowError(INVALID_INPUT_CODE, FAILED_TO_UNMARSHAL_ERROR)
}
- if errorBody, ok := apiError["error"].(map[string]interface{}); ok {
- if httpCode, exists := errorBody["http_code"].(float64); exists {
+ if errorBody, ok := apiError[constants.ERROR_KEY_ERROR].(map[string]interface{}); ok {
+ if httpCode, exists := errorBody[constants.ERROR_KEY_HTTP_CODE].(float64); exists {
skyflowError.httpCode = strconv.FormatFloat(httpCode, 'f', 0, 64)
} else {
skyflowError.httpCode = strconv.Itoa(responseHeaders.StatusCode)
}
- if message, exists := errorBody["message"].(string); exists {
+ if message, exists := errorBody[constants.ERROR_KEY_MESSAGE].(string); exists {
skyflowError.message = message
} else {
- skyflowError.message = "Unknown error"
+ skyflowError.message = constants.UNKNOWN_ERROR
}
- if grpcCode, exists := errorBody["grpc_code"].(float64); exists {
+ if grpcCode, exists := errorBody[constants.ERROR_KEY_GRPC_CODE].(float64); exists {
skyflowError.grpcCode = strconv.FormatFloat(grpcCode, 'f', 0, 64)
}
- if httpStatus, exists := errorBody["http_status"].(string); exists {
+ if httpStatus, exists := errorBody[constants.ERROR_KEY_HTTP_STATUS].(string); exists {
skyflowError.httpStatusCode = httpStatus
}
- if details, exists := errorBody["details"].([]interface{}); exists {
+ if details, exists := errorBody[constants.ERROR_KEY_DETAILS].([]interface{}); exists {
// initalize details if nil
if skyflowError.details == nil {
skyflowError.details = make([]interface{}, 0)
}
skyflowError.details = details
}
- } else if errBody, ok := apiError["error"].(string); ok {
+ } else if errBody, ok := apiError[constants.ERROR_KEY_ERROR].(string); ok {
skyflowError.message = errBody
} else {
skyflowError.message = string(bodyBytes)
}
- } else if responseHeaders.Header.Get("Content-Type") == "text/plain" {
+ } else if responseHeaders.Header.Get(constants.HEADER_CONTENT_TYPE_CAPITAL) == constants.CONTENT_TYPE_TEXT_PLAIN {
bodyBytes, err := io.ReadAll(responseHeaders.Body)
if err != nil {
- return NewSkyflowError(INVALID_INPUT_CODE, "Failed to read error")
+ return NewSkyflowError(INVALID_INPUT_CODE, constants.ERROR_FAILED_TO_READ)
} else {
skyflowError.message = string(bodyBytes)
skyflowError.httpStatusCode = responseHeaders.Status
}
- } else if responseHeaders.Header.Get("Content-Type") == "text/plain; charset=utf-8" {
+ } else if responseHeaders.Header.Get(constants.HEADER_CONTENT_TYPE_CAPITAL) == constants.CONTENT_TYPE_TEXT_CHARSET {
bodyBytes, errs := io.ReadAll(responseHeaders.Body)
if errs != nil {
- return NewSkyflowError(INVALID_INPUT_CODE, "Failed to read error")
+ return NewSkyflowError(INVALID_INPUT_CODE, constants.ERROR_FAILED_TO_READ)
}
// Parse JSON into a struct
var apiError map[string]interface{}
@@ -118,30 +118,30 @@ func SkyflowApiError(responseHeaders http.Response) *SkyflowError {
skyflowError.httpStatusCode = responseHeaders.Status
}
if apiError != nil {
- if errorBody, ok := apiError["error"].(map[string]interface{}); ok {
- if httpCode, exists := errorBody["http_code"].(float64); exists {
+ if errorBody, ok := apiError[constants.ERROR_KEY_ERROR].(map[string]interface{}); ok {
+ if httpCode, exists := errorBody[constants.ERROR_KEY_HTTP_CODE].(float64); exists {
skyflowError.httpCode = strconv.FormatFloat(httpCode, 'f', 0, 64)
} else {
skyflowError.httpCode = strconv.Itoa(responseHeaders.StatusCode)
}
- if message, exists := errorBody["message"].(string); exists {
- skyflowError.message = message
- } else {
- skyflowError.message = "Unknown error"
+ if message, exists := errorBody[constants.ERROR_KEY_MESSAGE].(string); exists {
+ skyflowError.message = message
+ } else {
+ skyflowError.message = constants.UNKNOWN_ERROR
}
- if grpcCode, exists := errorBody["grpc_code"].(float64); exists {
+ if grpcCode, exists := errorBody[constants.ERROR_KEY_GRPC_CODE].(float64); exists {
skyflowError.grpcCode = strconv.FormatFloat(grpcCode, 'f', 0, 64)
}
- if httpStatus, exists := errorBody["http_status"].(string); exists {
+ if httpStatus, exists := errorBody[constants.ERROR_KEY_HTTP_STATUS].(string); exists {
skyflowError.httpStatusCode = httpStatus
}
- if details, exists := errorBody["details"].([]interface{}); exists {
+ if details, exists := errorBody[constants.ERROR_KEY_DETAILS].([]interface{}); exists {
if skyflowError.details == nil {
skyflowError.details = make([]interface{}, 0)
}
skyflowError.details = details
}
- } else if errBody, ok := apiError["error"].(string); ok {
+ } else if errBody, ok := apiError[constants.ERROR_KEY_ERROR].(string); ok {
skyflowError.message = errBody
skyflowError.httpStatusCode = responseHeaders.Status
} else {
@@ -166,7 +166,7 @@ func SkyflowApiError(responseHeaders http.Response) *SkyflowError {
boolValue = false
}
// set the error detail
- errorDetail["errorFromClient"] = boolValue
+ errorDetail[constants.ERROR_KEY_FROM_CLIENT] = boolValue
skyflowError.details = append(skyflowError.details, errorDetail)
}
return &skyflowError
@@ -186,28 +186,28 @@ func SkyflowErrorApi(error error, header http.Header) *SkyflowError {
if err != nil {
return NewSkyflowError(INVALID_INPUT_CODE, error.Error())
}
- if errorBody, ok := apiError["error"].(map[string]interface{}); ok {
- if httpCode, exists := errorBody["http_code"].(float64); exists {
+ if errorBody, ok := apiError[constants.ERROR_KEY_ERROR].(map[string]interface{}); ok {
+ if httpCode, exists := errorBody[constants.ERROR_KEY_HTTP_CODE].(float64); exists {
skyflowError.httpCode = strconv.FormatFloat(httpCode, 'f', 0, 64)
}
- if message, exists := errorBody["message"].(string); exists {
+ if message, exists := errorBody[constants.ERROR_KEY_MESSAGE].(string); exists {
skyflowError.message = message
} else {
- skyflowError.message = "Unknown error"
+ skyflowError.message = constants.UNKNOWN_ERROR
}
- if grpcCode, exists := errorBody["grpc_code"].(float64); exists {
+ if grpcCode, exists := errorBody[constants.ERROR_KEY_GRPC_CODE].(float64); exists {
skyflowError.grpcCode = strconv.FormatFloat(grpcCode, 'f', 0, 64)
}
- if httpStatus, exists := errorBody["http_status"].(string); exists {
+ if httpStatus, exists := errorBody[constants.ERROR_KEY_HTTP_STATUS].(string); exists {
skyflowError.httpStatusCode = httpStatus
}
- if details, exists := errorBody["details"].([]interface{}); exists {
+ if details, exists := errorBody[constants.ERROR_KEY_DETAILS].([]interface{}); exists {
if skyflowError.details == nil {
skyflowError.details = make([]interface{}, 0)
}
skyflowError.details = details
}
- } else if errBody, ok := apiError["error"].(string); ok {
+ } else if errBody, ok := apiError[constants.ERROR_KEY_ERROR].(string); ok {
skyflowError.message = errBody
} else {
skyflowError.message = error.Error()
diff --git a/v2/utils/messages/error_logs.go b/v2/utils/messages/error_logs.go
index 7865828..c493844 100644
--- a/v2/utils/messages/error_logs.go
+++ b/v2/utils/messages/error_logs.go
@@ -3,6 +3,7 @@ package logs
import . "github.com/skyflowapi/skyflow-go/v2/internal/constants"
const (
+ INVALID_XML_FORMAT = SDK_LOG_PREFIX + " Validation error. Invalid XML format. Specify a valid XML format as string."
CLIENT_ID_NOT_FOUND = SDK_LOG_PREFIX + "Invalid credentials. Client ID cannot be empty."
TOKEN_URI_NOT_FOUND = SDK_LOG_PREFIX + "Invalid credentials. Token URI cannot be empty."
KEY_ID_NOT_FOUND = SDK_LOG_PREFIX + "Invalid credentials. Key ID cannot be empty."
@@ -67,6 +68,9 @@ const (
BEARER_TOKEN_REJECTED = SDK_LOG_PREFIX + "Bearer token request resulted in failure."
PRIVATE_KEY_TYPE = SDK_LOG_PREFIX + "RSA private key is of the wrong type Pem Type: %s"
PARSE_JWT_PAYLOAD = SDK_LOG_PREFIX + "Unable to parse jwt payload"
+ FAILED_TO_MARSHALL_JSON_METADATA = SDK_LOG_PREFIX + "Failed to marshal json data in createJSONMetadata()."
+ FAILED_TO_DECODE_BASE64 = SDK_LOG_PREFIX + "Failed to decode base64: %v"
+ FAILED_TO_CREATE_FILE = SDK_LOG_PREFIX + "Failed to create file: %v"
EMPTY_REQUEST_HEADERS = SDK_LOG_PREFIX + "Invalid %s request. Request headers can not be empty."
INVALID_REQUEST_HEADERS = SDK_LOG_PREFIX + "Invalid %s request. Request header can not be nil or empty in request headers."
EMPTY_PATH_PARAMS = SDK_LOG_PREFIX + "Invalid %s request. Path params can not be empty."
diff --git a/v2/utils/messages/info_logs.go b/v2/utils/messages/info_logs.go
index 2a67223..fb1ebfc 100644
--- a/v2/utils/messages/info_logs.go
+++ b/v2/utils/messages/info_logs.go
@@ -6,7 +6,7 @@ import (
const (
EMPTY_BEARER_TOKEN = SDK_LOG_PREFIX + "BearerToken is Empty"
- BEARER_TOKEN_EXPIRED = SDK_LOG_PREFIX + "BearerToken is expired"
+ BEARER_TOKEN_EXPIRED = SDK_LOG_PREFIX + "Bearer Token provided is either invalid or has expired."
GENERATE_BEARER_TOKEN_TRIGGERED = SDK_LOG_PREFIX + "GenerateBearerToken is triggered"
GENERATE_BEARER_TOKEN_SUCCESS = SDK_LOG_PREFIX + "BearerToken is generated"
GENERATE_SIGNED_DATA_TOKEN_SUCCESS = SDK_LOG_PREFIX + "Signed Data tokens are generated"