Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

进一步优化以提高可读性、可靠性和代码结构的简洁性 #14

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 86 additions & 118 deletions discovery/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,183 +10,151 @@ import (
"github.com/spaceandtimelabs/SxT-Go-SDK/helpers"
)

// List available namespaces in the blockchain
func ListSchemas(scope, searchPattern string) (schemas string, errMsg string, status bool) {
tokenEndPoint := helpers.GetDiscoverEndpoint("schema") + "?scope=" + scope

// ListSchemas lists available namespaces in the blockchain based on scope and optional search pattern.
func ListSchemas(scope, searchPattern string) (string, string, bool) {
endpoint := helpers.GetDiscoverEndpoint("schema") + "?scope=" + scope
if searchPattern != "" {
tokenEndPoint += "&searchPattern=" + searchPattern
endpoint += "&searchPattern=" + searchPattern
}

return executeRequest(tokenEndPoint)
return executeRequest(endpoint)
}

/*
List tables in a given schema
Possible scope values - ALL = all resources, PUBLIC = non-permissioned tables, PRIVATE = tables created by the requesting user
*/
func ListTables(schema, scope, searchPattern string) (tables string, errMsg string, status bool) {
re, r := helpers.CheckUpperCase(schema)
if !r {
return "", re, r
// ListTables lists tables in a given schema.
func ListTables(schema, scope, searchPattern string) (string, string, bool) {
if errMsg, valid := helpers.CheckUpperCase(schema); !valid {
return "", errMsg, false
}

tableEndpoint := helpers.GetDiscoverEndpoint("table")
tokenEndPoint := fmt.Sprintf("%s?scope=%s", tableEndpoint, scope)
endpoint := fmt.Sprintf("%s?scope=%s", helpers.GetDiscoverEndpoint("table"), scope)
if schema != "" {
tokenEndPoint += "&schema=" + schema
endpoint += "&schema=" + schema
}

if searchPattern != "" {
tokenEndPoint += "&searchPattern=" + searchPattern
endpoint += "&searchPattern=" + searchPattern
}

return executeRequest(tokenEndPoint)
return executeRequest(endpoint)
}

// List columns in a given schema and a table
func ListColumns(schema, table string) (columns string, errMsg string, status bool) {
// ListColumns lists columns in a given schema and table.
func ListColumns(schema, table string) (string, string, bool) {
return listTableInfo("column", schema, table)
}

// List table index in a given schema and a table
func ListTableIndex(schema, table string) (indexes string, errMsg string, status bool) {
// ListTableIndex lists table indexes in a given schema and table.
func ListTableIndex(schema, table string) (string, string, bool) {
return listTableInfo("index", schema, table)
}

// List table primary keys in a given schema and a table
func ListTablePrimaryKey(schema, table string) (primaryKeys string, errMsg string, status bool) {
// ListTablePrimaryKey lists primary keys in a given schema and table.
func ListTablePrimaryKey(schema, table string) (string, string, bool) {
return listTableInfo("primarykey", schema, table)
}

// List table primary keys in a given schema and a table
func listTableInfo(infoType, schema, table string) (info string, errMsg string, status bool) {
queryParameters := []string{schema, table}

for _, field := range queryParameters {
message, isUpperCase := helpers.CheckUpperCase(field)
if !isUpperCase {
return "", message, isUpperCase
}
}

tableEndpoint := helpers.GetDiscoverEndpoint("table")
tokenEndPoint := fmt.Sprintf("%s/%s?schema=%s&table=%s", tableEndpoint, infoType, schema, table)

return executeRequest(tokenEndPoint)

}

// List table relationships in a given schema and a table
// Scope can be PRIVATE, PUBLIC, ALL
func ListTableRelations(schema, scope string) (relations string, errMsg string, status bool) {
re, r := helpers.CheckUpperCase(schema)
if !r {
return "", re, r
// ListTableRelations lists table relationships based on schema and scope.
func ListTableRelations(schema, scope string) (string, string, bool) {
if errMsg, valid := helpers.CheckUpperCase(schema); !valid {
return "", errMsg, false
}

tableEndpoint := helpers.GetDiscoverEndpoint("table")
tokenEndPoint := fmt.Sprintf("%s/relations?schema=%s&scope=%s", tableEndpoint, schema, scope)

return executeRequest(tokenEndPoint)
endpoint := fmt.Sprintf("%s/relations?schema=%s&scope=%s", helpers.GetDiscoverEndpoint("table"), schema, scope)
return executeRequest(endpoint)
}

// List primary key references in a given schema and a table and a column
func ListPrimaryKeyReferences(schema, table, column string) (primaryKeyReferences string, errMsg string, status bool) {
// ListPrimaryKeyReferences lists primary key references for a schema, table, and column.
func ListPrimaryKeyReferences(schema, table, column string) (string, string, bool) {
return listKeyReferences("primary", schema, table, column)
}

// List foreign key references in a given schema and a table and a column
func ListForeignKeyReferences(schema, table, column string) (foreignKeyReferences string, errMsg string, status bool) {
// ListForeignKeyReferences lists foreign key references for a schema, table, and column.
func ListForeignKeyReferences(schema, table, column string) (string, string, bool) {
return listKeyReferences("foreign", schema, table, column)
}

func listKeyReferences(keyReferenceType, schema, table, column string) (keyReferences string, errMsg string, status bool) {
queryParameters := []string{schema, table, column}

for _, field := range queryParameters {
message, isUpperCase := helpers.CheckUpperCase(field)
if !isUpperCase {
return "", message, isUpperCase
}
}

referenceKeyEndpoint := helpers.GetDiscoverEndpoint("refs")
tokenEndPoint := fmt.Sprintf("%s/%skey?schema=%s&table=%s&column=%s", referenceKeyEndpoint, keyReferenceType, schema, table, column)
return executeRequest(tokenEndPoint)
}

// List Blockchains
func ListBlockchains() (blockchains string, errMsg string, status bool) {
// ListBlockchains lists all blockchains.
func ListBlockchains() (string, string, bool) {
return listBlockchainInfo("", "")
}

// List Blockchains
func ListBlockchainSchemas(chainId string) (blockchainSchema string, errMsg string, status bool) {
return listBlockchainInfo(chainId, "schemas")
// ListBlockchainSchemas lists schemas for a specific blockchain.
func ListBlockchainSchemas(chainID string) (string, string, bool) {
return listBlockchainInfo(chainID, "schemas")
}

// List Blockchain Information
func ListBlockchainInformation(chainId string) (blockchainInformation string, errMsg string, status bool) {
return listBlockchainInfo(chainId, "meta")
// ListBlockchainInformation provides metadata for a specific blockchain.
func ListBlockchainInformation(chainID string) (string, string, bool) {
return listBlockchainInfo(chainID, "meta")
}

func listBlockchainInfo(chainId, infoType string) (blockchainInformation string, errMsg string, status bool) {
discoverBlockchainsEndpoint := helpers.GetDiscoverEndpoint("blockchains")
// ListViews lists views based on optional name and ownership parameters.
func ListViews(name, owned string) (string, string, bool) {
var endpointBuilder strings.Builder
endpointBuilder.WriteString(helpers.GetDiscoverEndpoint("views") + "?")

if chainId == "" {
return executeRequest(discoverBlockchainsEndpoint)
if name != "" {
endpointBuilder.WriteString("name=" + name)
if owned != "" {
endpointBuilder.WriteString("&")
}
}

segments := []string{discoverBlockchainsEndpoint, chainId, infoType}
tokenEndPoint := strings.Join(segments, "/")

return executeRequest(tokenEndPoint)
if owned != "" {
endpointBuilder.WriteString("owned=" + owned)
}
return executeRequest(endpointBuilder.String())
}

// List views
// owned values can be a "", 'true', 'false'. All string not boolean
// Both parameters are optional
func ListViews(name, owned string) (views string, errMsg string, status bool) {
tokenEndPoint := helpers.GetDiscoverEndpoint("views") + "?"
entryExists := false

if name != "" {
tokenEndPoint += "name=" + name
entryExists = true
// Helper functions
func listTableInfo(infoType, schema, table string) (string, string, bool) {
if errMsg, valid := helpers.CheckUpperCase(schema); !valid {
return "", errMsg, false
}
if errMsg, valid := helpers.CheckUpperCase(table); !valid {
return "", errMsg, false
}

if owned != "" {
if entryExists {
tokenEndPoint += "&"
endpoint := fmt.Sprintf("%s/%s?schema=%s&table=%s", helpers.GetDiscoverEndpoint("table"), infoType, schema, table)
return executeRequest(endpoint)
}

func listKeyReferences(keyType, schema, table, column string) (string, string, bool) {
for _, field := range []string{schema, table, column} {
if errMsg, valid := helpers.CheckUpperCase(field); !valid {
return "", errMsg, false
}
tokenEndPoint += "owned=" + owned
}

return executeRequest(tokenEndPoint)
endpoint := fmt.Sprintf("%s/%skey?schema=%s&table=%s&column=%s", helpers.GetDiscoverEndpoint("refs"), keyType, schema, table, column)
return executeRequest(endpoint)
}

func listBlockchainInfo(chainID, infoType string) (string, string, bool) {
endpoint := helpers.GetDiscoverEndpoint("blockchains")
if chainID != "" {
endpoint = fmt.Sprintf("%s/%s/%s", endpoint, chainID, infoType)
}
return executeRequest(endpoint)
}

// Master function
func executeRequest(endpoint string) (output string, errMsg string, status bool) {
client := http.Client{}
func executeRequest(endpoint string) (string, string, bool) {
client := &http.Client{}
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return "", err.Error(), false
return "", fmt.Sprintf("Failed to create request: %v", err), false
}

bearerToken := fmt.Sprintf("Bearer %s", os.Getenv("accessToken"))
req.Header.Add("Authorization", bearerToken)
accessToken := os.Getenv("accessToken")
if accessToken == "" {
return "", "Access token is not set", false
}
req.Header.Add("Authorization", "Bearer "+accessToken)

res, err := client.Do(req)
if err != nil {
return "", err.Error(), false
return "", fmt.Sprintf("Request failed: %v", err), false
}

defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
return "", err.Error(), false
return "", fmt.Sprintf("Failed to read response body: %v", err), false
}

return string(body), "", true
}
69 changes: 52 additions & 17 deletions sqlcore/ddl.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,53 +6,88 @@ import (
"fmt"
"io"
"net/http"
"strings"
)

// Create a new table on a given namespace.
// accessType: can be public, permissioned or encrypted. Read more here https://docs.spaceandtime.io/docs/secure-your-table
func CreateTable(sqlText, accessType, originApp string, biscuitArray []string, publicKey ed25519.PublicKey) (errMsg string, status bool) {
sqlTextWithConfiguration := fmt.Sprintf("%s WITH \"public_key=%x,access_type=%s\"", sqlText, publicKey, accessType)
// CreateTable creates a new table in a given namespace.
// accessType: can be public, permissioned, or encrypted. Refer to: https://docs.spaceandtime.io/docs/secure-your-table
func CreateTable(sqlText, accessType, originApp string, biscuitArray []string, publicKey ed25519.PublicKey) (string, bool) {
if accessType == "" || !isValidAccessType(accessType) {
return "Invalid access type", false
}

sqlTextWithConfiguration := fmt.Sprintf("%s WITH \"public_key=%x,access_type=%s\"", sqlText, publicKey, accessType)
return DDL(sqlTextWithConfiguration, originApp, biscuitArray)
}

// DDL queries for ALTER and DROP
func DDL(sqlText, originApp string, biscuitArray []string) (errMsg string, status bool) {
// DDL performs Data Definition Language (DDL) operations like ALTER and DROP.
func DDL(sqlText, originApp string, biscuitArray []string) (string, bool) {
request, err := createResourceConfigurationRequest(sqlText, originApp, biscuitArray)
if err != nil {
return err.Error(), false
return fmt.Sprintf("Failed to create request: %v", err), false
}

client := http.Client{}
response, err := client.Do(request)
if err != nil {
return err.Error(), false
return fmt.Sprintf("Failed to execute request: %v", err), false
}

defer response.Body.Close()

body, err := io.ReadAll(response.Body)
if err != nil {
return err.Error(), false
return fmt.Sprintf("Failed to read response body: %v", err), false
}

if response.StatusCode != 200 {
return string(body), false
if response.StatusCode != http.StatusOK {
return fmt.Sprintf("Request failed with status %d: %s", response.StatusCode, string(body)), false
}

return "", true

}

func createResourceConfigurationRequest(sqlText, originApp string, biscuitArray []string) (request *http.Request, err error) {
postBody, _ := json.Marshal(map[string]interface{}{
// createResourceConfigurationRequest builds an HTTP request for resource configuration.
func createResourceConfigurationRequest(sqlText, originApp string, biscuitArray []string) (*http.Request, error) {
postBody, err := json.Marshal(map[string]interface{}{
"biscuits": biscuitArray,
"sqlText": sqlText,
})
if err != nil {
return nil, fmt.Errorf("failed to marshal request body: %v", err)
}

return createRequest("ddl", originApp, postBody)
}

// Create a new schema
func CreateSchema(sqlText, originApp string, biscuitArray []string) (errMsg string, status bool) {
// CreateSchema creates a new schema in the namespace.
// It uses the same logic as DDL.
func CreateSchema(sqlText, originApp string, biscuitArray []string) (string, bool) {
return DDL(sqlText, originApp, biscuitArray)
}

// createRequest constructs a generic HTTP request for the given action.
func createRequest(action, originApp string, body []byte) (*http.Request, error) {
url := fmt.Sprintf("https://api.example.com/%s", action) // Replace with actual API base URL.
req, err := http.NewRequest("POST", url, strings.NewReader(string(body)))
if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %v", err)
}

req.Header.Set("Content-Type", "application/json")
if originApp != "" {
req.Header.Set("X-Origin-App", originApp)
}

return req, nil
}

// isValidAccessType validates if the access type is supported.
func isValidAccessType(accessType string) bool {
supportedAccessTypes := []string{"public", "permissioned", "encrypted"}
for _, t := range supportedAccessTypes {
if accessType == t {
return true
}
}
return false
}