diff --git a/.gitignore b/.gitignore index 51eb33d..c67b66b 100644 --- a/.gitignore +++ b/.gitignore @@ -89,11 +89,7 @@ out/ # JIRA plugin atlassian-ide-plugin.xml -# Cursive Clojure plugin -.idea/replstate.xml - -# SonarLint plugin -.idea/sonarlint/ +.idea/ # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml diff --git a/internal/client/client.go b/internal/client/client.go index 331e26a..dce0582 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -3,7 +3,9 @@ package client import ( "bytes" "encoding/json" + "errors" "fmt" + "github.com/BESTSELLER/terraform-provider-servicenow-data/internal/models" "io" "net/http" "sync" @@ -39,41 +41,69 @@ func NewClient(url, user, pass string) *Client { } } -func (Client *Client) sendRequest(method string, path string, payload interface{}, statusCode int) (value string, err error) { - url := Client.url + path +func (client *Client) GetTableRow(tableName, sysID string) (*map[string]string, error) { + rowPath := fmt.Sprintf("/table/%s/%s", tableName, sysID) + rawData, err := client.sendRequest(http.MethodGet, rowPath, nil, 200) + if err != nil { + return nil, err + } + var objMap map[string]json.RawMessage + err = json.Unmarshal(*rawData, &objMap) + if err == nil { + return nil, err + } + rowData := make(map[string]string, len(objMap)) + + for s, message := range objMap { + var str string + err = json.Unmarshal(message, &str) + if err == nil { + rowData[s] = str + } else { + var ai models.ApprovalItem + err = json.Unmarshal(message, &ai) + if err != nil { + return nil, errors.New(fmt.Sprintf("Unmarshal exploded for result.%s.%v", s, message)) + } + rowData[s] = ai.Value + } + } + return &rowData, nil +} + +func (client *Client) sendRequest(method, path string, payload interface{}, statusCode int) (value *[]byte, err error) { + url := client.url + "/api/now" + path b := new(bytes.Buffer) err = json.NewEncoder(b).Encode(payload) if err != nil { - return "", err + return nil, err } req, err := http.NewRequest(method, url, b) if err != nil { - return "", err + return nil, err } - req.SetBasicAuth(Client.user, Client.pass) + req.SetBasicAuth(client.user, client.pass) req.Header.Add("Content-Type", "application/json") resp, err := httpClient.Do(req) defer resp.Body.Close() if err != nil { - return "", err + return nil, err } body, err := io.ReadAll(resp.Body) if err != nil { - return "", err + return nil, err } - strbody := string(body) - if statusCode != 0 { if resp.StatusCode != statusCode { - return "", fmt.Errorf("[ERROR] unexpected status code got: %v expected: %v \n %v \n %v", resp.StatusCode, statusCode, strbody, url) + return nil, fmt.Errorf("[ERROR] unexpected status code got: %v expected: %v \n %v \n %v", resp.StatusCode, statusCode, string(body), url) } } - return strbody, nil + return &body, nil } diff --git a/internal/datasource/data_source_database_row.go b/internal/datasource/data_source_database_row.go new file mode 100644 index 0000000..9985565 --- /dev/null +++ b/internal/datasource/data_source_database_row.go @@ -0,0 +1,65 @@ +package datasource + +import ( + "context" + "fmt" + "github.com/BESTSELLER/terraform-provider-servicenow-data/internal/client" + "github.com/BESTSELLER/terraform-provider-servicenow-data/internal/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// TODO +func DatabaseRowDatasource() *schema.Resource { + return &schema.Resource{ + Schema: resource.RowSchema, + ReadContext: dataSourceDatabaseRowRead, + Description: "A row in a SN table", + UseJSONNumber: false, + } +} + +func dataSourceDatabaseRowRead(ctx context.Context, data *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + rowData := data.Get("row_data").([]interface{}) + + // Warning or errors can be collected in a slice type + var diags diag.Diagnostics + tableID := data.Get("table_id").(string) + if tableID == "" { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "tableID is mandatory", + }) + return diags + } + rowID := data.Get("row_data").(map[string]string)["sys_id"] + + if rowID == "" { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "sys_id is mandatory", + }) + return diags + } + + order, err := c.GetTableRow(tableID, rowID) + if err != nil { + return diag.FromErr(err) + } + + rowData = parseRowSchema(tableID, order) + if err := data.Set("row_data", rowData); err != nil { + return diag.FromErr(err) + } + + data.SetId(fmt.Sprintf("%s/%s", tableID, rowID)) + + return diags + +} + +func parseRowSchema(tableID string, row *map[string]string) []interface{} { + panic("parseRowSchema unimplemented") +} diff --git a/internal/models/sn_row.go b/internal/models/sn_row.go new file mode 100644 index 0000000..9c2a535 --- /dev/null +++ b/internal/models/sn_row.go @@ -0,0 +1,6 @@ +package models + +type ApprovalItem struct { + Link string `json:"link"` + Value string `json:"value"` +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 4553720..0464550 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -3,13 +3,13 @@ package provider import ( "context" "github.com/BESTSELLER/terraform-provider-servicenow-data/internal/client" + "github.com/BESTSELLER/terraform-provider-servicenow-data/internal/datasource" + "github.com/BESTSELLER/terraform-provider-servicenow-data/internal/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func ServiceNowDataProvider() *schema.Provider { - dataSources := make(map[string]*schema.Resource) - resources := make(map[string]*schema.Resource) p := &schema.Provider{ Schema: map[string]*schema.Schema{ "sn_api_user": { @@ -29,8 +29,12 @@ func ServiceNowDataProvider() *schema.Provider { Description: "The URL to the SN table using basic auth"}, }, - ResourcesMap: resources, - DataSourcesMap: dataSources, + ResourcesMap: map[string]*schema.Resource{ + resource.DatabaseRowResourceName: resource.DatabaseRowResource(), + }, + DataSourcesMap: map[string]*schema.Resource{ + resource.DatabaseRowResourceName: datasource.DatabaseRowDatasource(), + }, ConfigureContextFunc: providerConfigure, } return p diff --git a/internal/resource/resource_database_row.go b/internal/resource/resource_database_row.go index 4afb1ed..d3e2970 100644 --- a/internal/resource/resource_database_row.go +++ b/internal/resource/resource_database_row.go @@ -7,7 +7,6 @@ import ( const DatabaseRowResourceName = "sn_application" -// TODO func DatabaseRowResource() *schema.Resource { return &schema.Resource{ Schema: RowSchema, @@ -35,34 +34,46 @@ func DatabaseRowResource() *schema.Resource { } var RowSchema = map[string]*schema.Schema{ - "sys_id": { - Description: "The unique id of the row", - Type: schema.TypeString, - Required: false, - Computed: true}, - "sys_updated_by": { - Description: "User that made the last update", - Type: schema.TypeString, - Required: false, - Computed: true}, - "sys_created_by": { - Description: "Account that created the row", - Type: schema.TypeString, - Required: false, - Computed: true}, - "sys_created_on": { - Description: "Creation Time", - Type: schema.TypeString, - Required: false, - Computed: true}, - "sys_updated_on": { - Description: "Last update Time", - Type: schema.TypeString, - Required: false, - Computed: true}, - "custom_columns": { - Description: "Custom columns that are not references", - Type: schema.TypeMap, - Elem: &schema.Schema{ - Type: schema.TypeString}}, + "table_id": { + Type: schema.TypeString, + Required: true, + Computed: false, + }, + "row_data": { + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "sys_id": { + Description: "The unique id of the row", + Type: schema.TypeString, + Required: false, + Computed: true}, + "sys_updated_by": { + Description: "User that made the last update", + Type: schema.TypeString, + Required: false, + Computed: true}, + "sys_created_by": { + Description: "Account that created the row", + Type: schema.TypeString, + Required: false, + Computed: true}, + "sys_created_on": { + Description: "Creation Time", + Type: schema.TypeString, + Required: false, + Computed: true}, + "sys_updated_on": { + Description: "Last update Time", + Type: schema.TypeString, + Required: false, + Computed: true}, + "custom_columns": { + Description: "Custom columns that are not references", + Type: schema.TypeMap, + Elem: &schema.Schema{ + Type: schema.TypeString}, + }, + }, + }, + }, }