diff --git a/cmd/datasetPublishDataRetrieve/main.go b/cmd/datasetPublishDataRetrieve/main.go index 34d4bf8..aba671c 100644 --- a/cmd/datasetPublishDataRetrieve/main.go +++ b/cmd/datasetPublishDataRetrieve/main.go @@ -118,7 +118,11 @@ func main() { color.Unset() } else { // create retrieve Job - jobId := datasetUtils.CreateRetrieveJob(client, APIServer, user, datasetList) - fmt.Println(jobId) + jobId, err := datasetUtils.CreateRetrieveJob(client, APIServer, user, datasetList) + if err != nil { + log.Fatal(err) + } else{ + fmt.Println(jobId) + } } } diff --git a/datasetUtils/createRetrieveJob.go b/datasetUtils/createRetrieveJob.go index 245c9f0..7acef53 100644 --- a/datasetUtils/createRetrieveJob.go +++ b/datasetUtils/createRetrieveJob.go @@ -6,12 +6,10 @@ import ( "log" "net/http" "time" + "fmt" ) -func CreateRetrieveJob(client *http.Client, APIServer string, user map[string]string, datasetList []string) (jobId string) { - // important: define field with capital names and rename fields via 'json' constructs - // otherwise the marshaling will omit the fields ! - +func constructJobRequest(user map[string]string, datasetList []string) ([]byte, error) { type datasetStruct struct { Pid string `json:"pid"` Files []string `json:"files"` @@ -32,27 +30,31 @@ func CreateRetrieveJob(client *http.Client, APIServer string, user map[string]st emptyfiles := make([]string, 0) var dsMap []datasetStruct - for i := 0; i < len(datasetList); i++ { - dsMap = append(dsMap, datasetStruct{datasetList[i], emptyfiles}) + for _, dataset := range datasetList { + dsMap = append(dsMap, datasetStruct{dataset, emptyfiles}) } jobMap["datasetList"] = dsMap // marshal to JSON - var bmm []byte - bmm, _ = json.Marshal(jobMap) - // fmt.Printf("Marshalled job description : %s\n", string(bmm)) + return json.Marshal(jobMap) +} - // now send archive job request +func sendJobRequest(client *http.Client, APIServer string, user map[string]string, bmm []byte) (*http.Response, error) { myurl := APIServer + "/Jobs?access_token=" + user["accessToken"] req, err := http.NewRequest("POST", myurl, bytes.NewBuffer(bmm)) + if err != nil { + return nil, err + } req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { - log.Fatal(err) + return nil, err } - defer resp.Body.Close() + return resp, nil +} +func handleJobResponse(resp *http.Response, user map[string]string) (string, error) { if resp.StatusCode == 200 { log.Println("Job response Status: okay") log.Println("A confirmation email will be sent to", user["mail"]) @@ -60,11 +62,46 @@ func CreateRetrieveJob(client *http.Client, APIServer string, user map[string]st var j Job err := decoder.Decode(&j) if err != nil { - log.Fatal("Could not decode id from job:", err) + return "", fmt.Errorf("could not decode id from job: %v", err) } - return j.Id + return j.Id, nil } else { log.Println("Job response Status: there are problems:", resp.StatusCode) - return "" + return "", fmt.Errorf("Job response Status: there are problems: %d", resp.StatusCode) + } +} + +/* +CreateRetrieveJob creates a job to retrieve a dataset from an API server. + +Parameters: +- client: An *http.Client object that is used to send the HTTP request. +- APIServer: A string representing the URL of the API server. +- user: A map[string]string containing user information. It should have keys "mail", "username", and "accessToken". +- datasetList: A slice of strings representing the list of datasets to be retrieved. + +The function constructs a job request with the provided parameters and sends it to the API server. If the job is successfully created, it returns the job ID as a string. If the job creation fails, it returns an empty string. + +The function logs the status of the job creation and sends a confirmation email to the user if the job is successfully created. + +Note: The function will terminate the program if it encounters an error while sending the HTTP request or decoding the job ID from the response. +*/ +func CreateRetrieveJob(client *http.Client, APIServer string, user map[string]string, datasetList []string) (jobId string, err error) { + bmm, err := constructJobRequest(user, datasetList) + if err != nil { + return "", err + } + + resp, err := sendJobRequest(client, APIServer, user, bmm) + if err != nil { + return "", err + } + defer resp.Body.Close() + + jobId, err = handleJobResponse(resp, user) + if err != nil { + return "", err } + + return jobId, nil } diff --git a/datasetUtils/createRetrieveJob_test.go b/datasetUtils/createRetrieveJob_test.go new file mode 100644 index 0000000..125b36b --- /dev/null +++ b/datasetUtils/createRetrieveJob_test.go @@ -0,0 +1,187 @@ +package datasetUtils + +import ( + "net/http" + "net/http/httptest" + "testing" + "encoding/json" + "reflect" + "sort" +) + +// Checks if the function returns a job ID when it successfully creates a job. +func TestCreateRetrieveJob(t *testing.T) { + // Create a test server that always responds with a 200 status code and a job ID + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Write([]byte(`{"id": "12345"}`)) + })) + defer server.Close() + + // Create a test client that uses the test server + client := server.Client() + + // Define the parameters for the CreateRetrieveJob function + APIServer := server.URL + user := map[string]string{ + "mail": "test@example.com", + "username": "testuser", + "accessToken": "testtoken", + } + datasetList := []string{"dataset1", "dataset2"} + + // Call the CreateRetrieveJob function + jobId, _ := CreateRetrieveJob(client, APIServer, user, datasetList) + + // Check if the function returned a job ID + if jobId == "" { + t.Errorf("CreateRetrieveJob() returned an empty job ID, want non-empty") + } +} + +// checks if the function returns a valid JSON byte array and no error when it's called with valid parameters. +func TestConstructJobRequest(t *testing.T) { + // Define the parameters for the constructJobRequest function + user := map[string]string{ + "mail": "test@example.com", + "username": "testuser", + } + datasetList := []string{"dataset1", "dataset2"} + + // Call the constructJobRequest function + bmm, err := constructJobRequest(user, datasetList) + + // Check if the function returned an error + if err != nil { + t.Errorf("constructJobRequest() returned an error: %v", err) + } + + // Check if the function returned a valid JSON byte array + var data map[string]interface{} + if err := json.Unmarshal(bmm, &data); err != nil { + t.Errorf("constructJobRequest() returned invalid JSON: %v", err) + } + + // Remove the creationTime field from the actual JSON + delete(data, "creationTime") + + // Define the expected data + expectedData := map[string]interface{}{ + "emailJobInitiator": user["mail"], + "jobParams": map[string]interface{}{ + "username": user["username"], + "destinationPath": "/archive/retrieve", + }, + "datasetList": []interface{}{ + map[string]interface{}{"pid": datasetList[0], "files": []interface{}{}}, + map[string]interface{}{"pid": datasetList[1], "files": []interface{}{}}, + }, + "jobStatusMessage": "jobSubmitted", + "type": "retrieve", + } + + // Compare individual fields + for key, expectedValue := range expectedData { + if actualValue, ok := data[key]; ok { + if key == "datasetList" { + // Assert the underlying type of actualValue and expectedValue to []interface{} + actualList, ok1 := actualValue.([]interface{}) + expectedList, ok2 := expectedValue.([]interface{}) + if !ok1 || !ok2 { + t.Errorf("constructJobRequest() returned unexpected type for key %v: got %T want %T", key, actualValue, expectedValue) + continue + } + + // Sort the datasetList slice before comparing + sort.Slice(actualList, func(i, j int) bool { + return actualList[i].(map[string]interface{})["pid"].(string) < actualList[j].(map[string]interface{})["pid"].(string) + }) + sort.Slice(expectedList, func(i, j int) bool { + return expectedList[i].(map[string]interface{})["pid"].(string) < expectedList[j].(map[string]interface{})["pid"].(string) + }) + + actualValue = actualList + expectedValue = expectedList + } + if !reflect.DeepEqual(actualValue, expectedValue) { + t.Errorf("constructJobRequest() returned unexpected JSON for key %v: got %v want %v", key, actualValue, expectedValue) + } + } else { + t.Errorf("constructJobRequest() did not return expected key in JSON: %v", key) + } + } +} + +// Checks if the function returns a valid HTTP response and no error when it's called with valid parameters. +func TestSendJobRequest(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Write([]byte(`{"id": "12345"}`)) + })) + defer server.Close() + + client := server.Client() + APIServer := server.URL + user := map[string]string{ + "mail": "test@example.com", + "username": "testuser", + "accessToken": "testtoken", + } + bmm := []byte(`{"key": "value"}`) + + resp, err := sendJobRequest(client, APIServer, user, bmm) + + if err != nil { + t.Errorf("sendJobRequest() returned an error: %v", err) + } + + if resp.StatusCode != 200 { + t.Errorf("sendJobRequest() returned status code %v, want 200", resp.StatusCode) + } +} + +// Checks for a successful response, a response with a non-200 status code, and a response with invalid JSON. +func TestHandleJobResponse(t *testing.T) { + user := map[string]string{ + "mail": "test@example.com", + "username": "testuser", + } + + // Test successful response + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Write([]byte(`{"id": "12345"}`)) + })) + client := server.Client() + resp, _ := client.Get(server.URL) + jobId, err := handleJobResponse(resp, user) + if err != nil { + t.Errorf("handleJobResponse() returned an error: %v", err) + } + if jobId != "12345" { + t.Errorf("handleJobResponse() returned job ID %v, want 12345", jobId) + } + server.Close() + + // Test non-200 status code + server = httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusInternalServerError) + rw.Write([]byte(`{"id": "12345"}`)) + })) + client = server.Client() + resp, _ = client.Get(server.URL) + jobId, err = handleJobResponse(resp, user) + if err == nil { + t.Errorf("handleJobResponse() did not return an error for non-200 status code") + } + server.Close() + + // Test invalid JSON in response + server = httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Write([]byte(`invalid JSON`)) + })) + client = server.Client() + resp, _ = client.Get(server.URL) + jobId, err = handleJobResponse(resp, user) + if err == nil { + t.Errorf("handleJobResponse() did not return an error for invalid JSON") + } + server.Close() +}