github.com/uvalib/orcid-access-ws@v0.0.0-20250612130209-7d062dbabf9d/orcidaccessws/orcid/client.go (about)

     1  package orcid
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/parnurzeal/gorequest"
    13  	"github.com/pkg/errors"
    14  	"github.com/uvalib/orcid-access-ws/orcidaccessws/api"
    15  	"github.com/uvalib/orcid-access-ws/orcidaccessws/config"
    16  	"github.com/uvalib/orcid-access-ws/orcidaccessws/logger"
    17  )
    18  
    19  var emptyUpdateCode = ""
    20  var emptyAccessToken = ""
    21  var currentAccessToken = ""
    22  
    23  // we want to handle this differently from other requests because it is used as part of healthchecking
    24  var authTimeout = 5 * time.Second
    25  
    26  // UpdateOrcidActivity -- update the user activity
    27  func UpdateOrcidActivity(orcid string, oauthToken string, activity api.ActivityUpdate) (string, int, error) {
    28  
    29  	logActivityUpdateRequest(activity)
    30  
    31  	// determine if we are creating a new activity or updating an existing one
    32  	existingActivity := len(activity.UpdateCode) != 0
    33  
    34  	// construct target URL
    35  	url := fmt.Sprintf("%s/%s/work", config.Configuration.OrcidSecureURL, orcid)
    36  	if existingActivity == true {
    37  		url = fmt.Sprintf("%s/%s", url, activity.UpdateCode)
    38  	}
    39  	//fmt.Printf( "%s\n", url )
    40  
    41  	// build the request body
    42  	requestBody, err := makeUpdateActivityBody(activity)
    43  
    44  	// check for errors
    45  	if err != nil {
    46  		logger.Log(fmt.Sprintf("ERROR: creating service payload %s", err))
    47  		return emptyUpdateCode, http.StatusBadRequest, err
    48  	}
    49  
    50  	// construct the auth field
    51  	auth := fmt.Sprintf("Bearer %s", oauthToken)
    52  
    53  	// issue the request
    54  	start := time.Now()
    55  	var resp gorequest.Response
    56  	var body string
    57  	var errs []error
    58  	if existingActivity == true {
    59  		resp, body, errs = gorequest.New().
    60  			SetDebug(config.Configuration.Debug).
    61  			Put(url).
    62  			Set("Accept", "application/json").
    63  			Set("Content-Type", "application/xml").
    64  			Set("Authorization", auth).
    65  			Send(requestBody).
    66  			Timeout(time.Duration(config.Configuration.ServiceTimeout) * time.Second).
    67  			End()
    68  	} else {
    69  		resp, body, errs = gorequest.New().
    70  			SetDebug(config.Configuration.Debug).
    71  			Post(url).
    72  			Set("Accept", "application/json").
    73  			Set("Content-Type", "application/xml").
    74  			Set("Authorization", auth).
    75  			Send(requestBody).
    76  			Timeout(time.Duration(config.Configuration.ServiceTimeout) * time.Second).
    77  			End()
    78  	}
    79  	duration := time.Since(start)
    80  
    81  	// check for errors
    82  	if errs != nil {
    83  		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s in %s", url, errs, duration))
    84  		httpStatus := mapErrorResponseToStatus(errs[0])
    85  		return emptyUpdateCode, httpStatus, errs[0]
    86  	}
    87  
    88  	defer io.Copy(ioutil.Discard, resp.Body)
    89  	defer resp.Body.Close()
    90  
    91  	logger.Log(fmt.Sprintf("Service (%s) returns http %d in %s", url, resp.StatusCode, duration))
    92  
    93  	// the happy update path; just return the original update code
    94  	if existingActivity == true && resp.StatusCode == http.StatusOK {
    95  		return activity.UpdateCode, http.StatusOK, nil
    96  	}
    97  
    98  	// the happy create path, pull the push code from the location header
    99  	if existingActivity == false && resp.StatusCode == http.StatusCreated {
   100  		tokens := strings.Split(resp.Header.Get("Location"), "/")
   101  		if len(tokens) != 0 {
   102  			return tokens[len(tokens)-1], http.StatusOK, nil
   103  		}
   104  
   105  		// unexpected, return an error
   106  		return emptyUpdateCode, http.StatusInternalServerError, errors.New("Unexpected/missing location header in response")
   107  	}
   108  
   109  	//
   110  	// something unexpected happened and we did not get an error report (handled above)
   111  	//
   112  
   113  	// check for a 500 error and return if we find it
   114  	if resp.StatusCode == http.StatusInternalServerError {
   115  		return emptyUpdateCode, http.StatusInternalServerError, errors.New("Server reports Internal Server Error")
   116  	}
   117  
   118  	// otherwise, attempt to decode the response
   119  	aur := activityUpdateResponse{}
   120  	err = json.Unmarshal([]byte(body), &aur)
   121  	if err != nil {
   122  		logger.Log(fmt.Sprintf("ERROR: json unmarshal: %s", err))
   123  		httpStatus := mapErrorResponseToStatus(err)
   124  		return emptyUpdateCode, httpStatus, err
   125  	}
   126  
   127  	//
   128  	// ORCID reported an error
   129  	//
   130  	if len(aur.Error) != 0 {
   131  		logger.Log(fmt.Sprintf("ERROR: service reports %s (%s)", aur.Error, aur.ErrorDescription))
   132  		return emptyUpdateCode, resp.StatusCode, errors.New(aur.ErrorDescription)
   133  	}
   134  
   135  	if aur.ResponseCode != http.StatusOK {
   136  		logger.Log(fmt.Sprintf("ERROR: service reports: %d (%s)", aur.ResponseCode, aur.DeveloperMessage))
   137  		return emptyUpdateCode, aur.ResponseCode, errors.New(aur.DeveloperMessage)
   138  	}
   139  
   140  	// unclear why we are here but an error occurred
   141  	return emptyUpdateCode, http.StatusInternalServerError, errors.New("Unhandled error case")
   142  }
   143  
   144  var employmentXML = `<?xml version="1.0" encoding="UTF-8"?>
   145  <employment:employment
   146  	xmlns:employment="http://www.orcid.org/ns/employment" xmlns:common="http://www.orcid.org/ns/common"
   147  	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   148  	xsi:schemaLocation="http://www.orcid.org/ns/employment ../employment-3.0.xsd ">
   149  	<common:organization>
   150  		<common:name>University of Virginia</common:name>
   151  		<common:address>
   152  			<common:city>Charlottesville</common:city>
   153  			<common:region>VA</common:region>
   154  			<common:country>US</common:country>
   155  		</common:address>
   156  		<common:disambiguated-organization>
   157  				<common:disambiguated-organization-identifier>https://ror.org/0153tk833</common:disambiguated-organization-identifier>
   158  				<common:disambiguation-source>ROR</common:disambiguation-source>
   159  			</common:disambiguated-organization>
   160  	</common:organization>
   161  </employment:employment>`
   162  
   163  // SendEmployment updates the user's ORCID with UVA Employent info
   164  func SendEmployment(attributes api.OrcidAttributes) {
   165  
   166  	// First check for existing UVA Employment
   167  	if hasEmp, err := hasExistingEmployment(attributes); err != nil || hasEmp {
   168  		return
   169  	}
   170  
   171  	url := fmt.Sprintf("%s/%s/employment", config.Configuration.OrcidSecureURL, attributes.Orcid)
   172  	// construct the auth field
   173  	auth := fmt.Sprintf("Bearer %s", attributes.OauthAccessToken)
   174  	start := time.Now()
   175  	resp, _, errs := gorequest.New().
   176  		SetDebug(config.Configuration.Debug).
   177  		Post(url).
   178  		Set("Accept", "application/xml").
   179  		Set("Content-Type", "application/xml").
   180  		Set("Authorization", auth).
   181  		Send(employmentXML).
   182  		Timeout(time.Duration(config.Configuration.ServiceTimeout) * time.Second).
   183  		End()
   184  	duration := time.Since(start)
   185  
   186  	defer io.Copy(ioutil.Discard, resp.Body)
   187  	defer resp.Body.Close()
   188  	// check for errors
   189  	if errs != nil || resp.StatusCode != http.StatusCreated {
   190  		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s %s in %s \n %s", url, resp.Status, errs, duration, resp.Body))
   191  		return
   192  	}
   193  	logger.Log(fmt.Sprintf("Employment created for %s", attributes.Orcid))
   194  
   195  	return
   196  }
   197  
   198  // hasExistingEmployment checks for existing UVA Employment matching our OrcidClientID
   199  func hasExistingEmployment(attributes api.OrcidAttributes) (bool, []error) {
   200  	hasEmployment := false
   201  	// Only need to know employment here
   202  	var employmentStruct struct {
   203  		AffiliationGroup []struct {
   204  			Summaries []struct {
   205  				Employment struct {
   206  					Source struct {
   207  						SourceClientID struct {
   208  							Path string `json:"path"`
   209  						} `json:"source-client-id"`
   210  					} `json:"source"`
   211  				} `json:"employment-summary"`
   212  			} `json:"summaries"`
   213  		} `json:"affiliation-group"`
   214  	}
   215  	url := fmt.Sprintf("%s/%s/employments", config.Configuration.OrcidSecureURL, attributes.Orcid)
   216  	// construct the auth field
   217  	auth := fmt.Sprintf("Bearer %s", attributes.OauthAccessToken)
   218  	start := time.Now()
   219  	resp, body, errs := gorequest.New().
   220  		SetDebug(config.Configuration.Debug).
   221  		Get(url).
   222  		Set("Accept", "application/json").
   223  		Set("Content-Type", "application/json").
   224  		Set("Authorization", auth).
   225  		Timeout(time.Duration(config.Configuration.ServiceTimeout) * time.Second).
   226  		End()
   227  	duration := time.Since(start)
   228  
   229  	defer io.Copy(ioutil.Discard, resp.Body)
   230  	defer resp.Body.Close()
   231  	// check for errors
   232  	if errs != nil || resp.StatusCode != http.StatusOK {
   233  		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s %s in %s \n %s", url, resp.Status, errs, duration, resp.Body))
   234  		return false, errs
   235  	}
   236  
   237  	if err := json.Unmarshal([]byte(body), &employmentStruct); err != nil {
   238  		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s in %s", url, errs, duration))
   239  		return false, errs
   240  	}
   241  
   242  	// Check the nested json
   243  	for _, g := range employmentStruct.AffiliationGroup {
   244  		for _, s := range g.Summaries {
   245  			// Check if existing employments match our client ID
   246  			if s.Employment.Source.SourceClientID.Path == config.Configuration.OrcidClientID {
   247  				hasEmployment = true
   248  			}
   249  		}
   250  	}
   251  	logger.Log(fmt.Sprintf("INFO: %s has UVA Employment: %t", attributes.Orcid, hasEmployment))
   252  	return hasEmployment, nil
   253  }
   254  
   255  var educationXML = `<?xml version="1.0" encoding="UTF-8"?>
   256  <education:education
   257  	xmlns:common="http://www.orcid.org/ns/common" xmlns:education="http://www.orcid.org/ns/education"
   258  	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   259  	xsi:schemaLocation="http://www.orcid.org/ns/education ../education-3.0.xsd ">
   260  	<common:organization>
   261  		<common:name>University of Virginia</common:name>
   262  		<common:address>
   263  			<common:city>Charlottesville</common:city>
   264  			<common:region>VA</common:region>
   265  			<common:country>US</common:country>
   266  		</common:address>
   267  		<common:disambiguated-organization>
   268  				<common:disambiguated-organization-identifier>https://ror.org/0153tk833</common:disambiguated-organization-identifier>
   269  				<common:disambiguation-source>ROR</common:disambiguation-source>
   270  			</common:disambiguated-organization>
   271  	</common:organization>
   272  </education:education>`
   273  
   274  // SendEmployment updates the user's ORCID with UVA Employent info
   275  func SendEducation(attributes api.OrcidAttributes) {
   276  
   277  	// First check for existing UVA Employment
   278  	if hasEmp, err := hasExistingEducation(attributes); err != nil || hasEmp {
   279  		return
   280  	}
   281  
   282  	url := fmt.Sprintf("%s/%s/education", config.Configuration.OrcidSecureURL, attributes.Orcid)
   283  	// construct the auth field
   284  	auth := fmt.Sprintf("Bearer %s", attributes.OauthAccessToken)
   285  	start := time.Now()
   286  	resp, _, errs := gorequest.New().
   287  		SetDebug(config.Configuration.Debug).
   288  		Post(url).
   289  		Set("Accept", "application/xml").
   290  		Set("Content-Type", "application/xml").
   291  		Set("Authorization", auth).
   292  		Send(educationXML).
   293  		Timeout(time.Duration(config.Configuration.ServiceTimeout) * time.Second).
   294  		End()
   295  	duration := time.Since(start)
   296  
   297  	defer io.Copy(ioutil.Discard, resp.Body)
   298  	defer resp.Body.Close()
   299  	// check for errors
   300  	if errs != nil || resp.StatusCode != http.StatusCreated {
   301  		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s %s in %s \n %s", url, resp.Status, errs, duration, resp.Body))
   302  		return
   303  	}
   304  	logger.Log(fmt.Sprintf("INFO: UVA Education created for %s", attributes.Orcid))
   305  
   306  	return
   307  }
   308  
   309  // hasExistingEmployment checks for existing UVA Employment matching our OrcidClientID
   310  func hasExistingEducation(attributes api.OrcidAttributes) (bool, error) {
   311  	hasUVAEducation := false
   312  	// Only need to know employment here
   313  	var educationStruct struct {
   314  		AffiliationGroup []struct {
   315  			Summaries []struct {
   316  				Education struct {
   317  					Source struct {
   318  						SourceClientID struct {
   319  							Path string `json:"path"`
   320  						} `json:"source-client-id"`
   321  					} `json:"source"`
   322  				} `json:"education-summary"`
   323  			} `json:"summaries"`
   324  		} `json:"affiliation-group"`
   325  	}
   326  	url := fmt.Sprintf("%s/%s/educations", config.Configuration.OrcidSecureURL, attributes.Orcid)
   327  	// construct the auth field
   328  	auth := fmt.Sprintf("Bearer %s", attributes.OauthAccessToken)
   329  	start := time.Now()
   330  	resp, body, errs := gorequest.New().
   331  		SetDebug(config.Configuration.Debug).
   332  		Get(url).
   333  		Set("Accept", "application/json").
   334  		Set("Content-Type", "application/json").
   335  		Set("Authorization", auth).
   336  		Timeout(time.Duration(config.Configuration.ServiceTimeout) * time.Second).
   337  		End()
   338  	duration := time.Since(start)
   339  
   340  	defer io.Copy(ioutil.Discard, resp.Body)
   341  	defer resp.Body.Close()
   342  	// check for errors
   343  	if errs != nil {
   344  		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s in %s", url, errs, duration))
   345  		return false, errs[0]
   346  	}
   347  
   348  	if err := json.Unmarshal([]byte(body), &educationStruct); err != nil {
   349  		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s in %s", url, errs, duration))
   350  		return false, errs[0]
   351  	}
   352  
   353  	// Check the nested json
   354  	for _, g := range educationStruct.AffiliationGroup {
   355  		for _, s := range g.Summaries {
   356  			// Check if existing employments match our client ID
   357  			if s.Education.Source.SourceClientID.Path == config.Configuration.OrcidClientID {
   358  				hasUVAEducation = true
   359  			}
   360  		}
   361  	}
   362  	//logger.Log(fmt.Sprintf("hasEducation: %s ", resp.Body))
   363  	logger.Log(fmt.Sprintf("INFO: %s has UVA education: %t", attributes.Orcid, hasUVAEducation))
   364  	return hasUVAEducation, nil
   365  }
   366  
   367  func getOauthToken() (string, int, error) {
   368  
   369  	// construct target URL
   370  	url := fmt.Sprintf("%s/oauth/token", config.Configuration.OrcidOauthURL)
   371  	//fmt.Printf("%s\n", url)
   372  
   373  	// create the request payload
   374  	pl := oauthRequest{
   375  		ClientID:     config.Configuration.OrcidClientID,
   376  		ClientSecret: config.Configuration.OrcidClientSecret,
   377  		Scope:        "/read-public",
   378  		GrantType:    "client_credentials"}
   379  
   380  	// issue the request
   381  	start := time.Now()
   382  	resp, body, errs := gorequest.New().
   383  		SetDebug(config.Configuration.Debug).
   384  		Post(url).
   385  		Send(pl).
   386  		Set("Accept", "application/json").
   387  		Set("Content-Type", "application/x-www-form-urlencoded").
   388  		Timeout(authTimeout).
   389  		End()
   390  	duration := time.Since(start)
   391  
   392  	// check for errors
   393  	if errs != nil {
   394  		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s in %s", url, errs, duration))
   395  		httpStatus := mapErrorResponseToStatus(errs[0])
   396  		return emptyAccessToken, httpStatus, errs[0]
   397  	}
   398  
   399  	defer io.Copy(ioutil.Discard, resp.Body)
   400  	defer resp.Body.Close()
   401  
   402  	logger.Log(fmt.Sprintf("Service (%s) returns http %d in %s", url, resp.StatusCode, duration))
   403  
   404  	// check for a non-success status and return if we find it
   405  	if resp.StatusCode != http.StatusOK {
   406  		return emptyAccessToken, resp.StatusCode, fmt.Errorf("Service (%s) returns http %d", url, resp.StatusCode)
   407  	}
   408  
   409  	// otherwise, attempt to decode the response
   410  	oar := oauthResponse{}
   411  	err := json.Unmarshal([]byte(body), &oar)
   412  	if err != nil {
   413  		logger.Log(fmt.Sprintf("ERROR: json unmarshal: %s", err))
   414  		httpStatus := mapErrorResponseToStatus(err)
   415  		return emptyAccessToken, httpStatus, err
   416  	}
   417  
   418  	return oar.AccessToken, http.StatusOK, nil
   419  }
   420  
   421  // RenewAccessToken -- renew the access token
   422  func RenewAccessToken(staleToken string) (string, string, int, error) {
   423  
   424  	// construct target URL
   425  	url := fmt.Sprintf("%s/oauth/token", config.Configuration.OrcidOauthURL)
   426  	//fmt.Printf("%s\n", url)
   427  
   428  	// issue the request
   429  	start := time.Now()
   430  	resp, _, errs := gorequest.New().
   431  		SetDebug(config.Configuration.Debug).
   432  		Get(url).
   433  		Set("Accept", "application/json").
   434  		Set("refresh_token", staleToken).
   435  		Set("grant_type", "refresh_token").
   436  		Set("client_id", config.Configuration.OrcidClientID).
   437  		Set("client_secret", config.Configuration.OrcidClientSecret).
   438  		Timeout(time.Duration(config.Configuration.ServiceTimeout) * time.Second).
   439  		End()
   440  	duration := time.Since(start)
   441  
   442  	// check for errors
   443  	if errs != nil {
   444  		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s in %s", url, errs, duration))
   445  		httpStatus := mapErrorResponseToStatus(errs[0])
   446  		return emptyUpdateCode, emptyUpdateCode, httpStatus, errs[0]
   447  	}
   448  
   449  	defer io.Copy(ioutil.Discard, resp.Body)
   450  	defer resp.Body.Close()
   451  
   452  	logger.Log(fmt.Sprintf("Service (%s) returns http %d in %s", url, resp.StatusCode, duration))
   453  
   454  	//logger.Log(fmt.Sprintf("BODY [%s]", body ) )
   455  
   456  	return emptyUpdateCode, emptyUpdateCode, http.StatusInternalServerError, errors.New("Not implemented")
   457  }
   458  
   459  // GetOrcidDetails -- get details for the specified ORCID
   460  func GetOrcidDetails(orcid string) (*api.OrcidDetails, int, error) {
   461  
   462  	// construct target URL
   463  	url := fmt.Sprintf("%s/%s/person", config.Configuration.OrcidPublicURL, orcid)
   464  	//fmt.Printf( "%s\n", url )
   465  
   466  	// get an access token
   467  	token, err := getAccessToken()
   468  	if err != nil {
   469  		return nil, http.StatusInternalServerError, err
   470  	}
   471  
   472  	// issue the request
   473  	start := time.Now()
   474  	resp, body, errs := gorequest.New().
   475  		SetDebug(config.Configuration.Debug).
   476  		Get(url).
   477  		Set("Authorization", token).
   478  		Set("Accept", "application/json").
   479  		Timeout(time.Duration(config.Configuration.ServiceTimeout) * time.Second).
   480  		End()
   481  	duration := time.Since(start)
   482  
   483  	// check for errors
   484  	if errs != nil {
   485  		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s in %s", url, errs, duration))
   486  		httpStatus := mapErrorResponseToStatus(errs[0])
   487  		return nil, httpStatus, errs[0]
   488  	}
   489  
   490  	defer io.Copy(ioutil.Discard, resp.Body)
   491  	defer resp.Body.Close()
   492  
   493  	logger.Log(fmt.Sprintf("Service (%s) returns http %d in %s", url, resp.StatusCode, duration))
   494  
   495  	// check for an http status
   496  	if resp.StatusCode != http.StatusOK {
   497  		return nil, resp.StatusCode, errors.New(orcid)
   498  	}
   499  
   500  	pr := orcidPersonResponse{}
   501  	err = json.Unmarshal([]byte(body), &pr)
   502  	if err != nil {
   503  		logger.Log(fmt.Sprintf("ERROR: json unmarshal: %s", err))
   504  		return nil, http.StatusInternalServerError, err
   505  	}
   506  
   507  	return transformDetailsResponse(&pr), http.StatusOK, nil
   508  }
   509  
   510  //
   511  // SearchOrcid -- search ORCID given the supplied parameters and return the set of ORCID details that match
   512  //
   513  //func SearchOrcid(search string, startIx string, maxResults string) ([]*api.OrcidDetails, int, int, error) {
   514  //
   515  //	// construct target URL
   516  //	url := fmt.Sprintf("%s/search?q=%s&start=%s&rows=%s", config.Configuration.OrcidPublicURL,
   517  //		htmlEncodeString(search), startIx, maxResults)
   518  //	fmt.Printf("%s\n", url)
   519  //
   520  //	// issue the request
   521  //	start := time.Now()
   522  //	resp, body, errs := gorequest.New().
   523  //		SetDebug(config.Configuration.Debug).
   524  //		Get(url).
   525  //		Set("Accept", "application/json").
   526  //		Timeout(time.Duration(config.Configuration.ServiceTimeout) * time.Second).
   527  //		End()
   528  //	duration := time.Since(start)
   529  //
   530  //	// check for errors
   531  //	if errs != nil {
   532  //		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s in %s", url, errs, duration))
   533  //		httpStatus := mapErrorResponseToStatus(errs[0])
   534  //		return nil, 0, httpStatus, errs[0]
   535  //	}
   536  //
   537  //	defer io.Copy(ioutil.Discard, resp.Body)
   538  //	defer resp.Body.Close()
   539  //
   540  //	logger.Log(fmt.Sprintf("INFO: Service (%s) returns http %d in %s", url, resp.StatusCode, duration))
   541  //
   542  //	// check the common response elements
   543  //	status, err := checkCommonResponse(body)
   544  //	if err != nil {
   545  //		httpStatus := mapErrorResponseToStatus(err)
   546  //		return nil, 0, httpStatus, err
   547  //	}
   548  //
   549  //	if status != http.StatusOK {
   550  //		return nil, 0, status, err
   551  //	}
   552  //
   553  //	sr := orcidSearchResponse{}
   554  //	err = json.Unmarshal([]byte(body), &sr)
   555  //	if err != nil {
   556  //		logger.Log(fmt.Sprintf("ERROR: json unmarshal: %s", err))
   557  //		httpStatus := mapErrorResponseToStatus(err)
   558  //		return nil, 0, httpStatus, err
   559  //	}
   560  //
   561  //	//if sr.SearchResults.Results == nil || len( sr.SearchResults.Results ) == 0 {
   562  //	//    return nil, sr.SearchResults.TotalFound, http.StatusNotFound, nil
   563  //	//}
   564  //
   565  //	return transformSearchResponse(sr.SearchResults), sr.SearchResults.TotalFound, http.StatusOK, nil
   566  //}
   567  
   568  // GetPublicEndpointStatus -- get the public endpoint status
   569  func GetPublicEndpointStatus() error {
   570  
   571  	// construct target URL
   572  	url := fmt.Sprintf("%s/status", config.Configuration.OrcidPublicURL)
   573  	//fmt.Printf( "%s\n", url )
   574  
   575  	// get an access token
   576  	token, err := getAccessToken()
   577  	if err != nil {
   578  		return err
   579  	}
   580  
   581  	return issueAuthorizedGet(url, "application/json", token)
   582  }
   583  
   584  // GetSecureEndpointStatus -- get the secure endpoint status
   585  func GetSecureEndpointStatus() error {
   586  
   587  	// construct target URL
   588  	url := fmt.Sprintf("%s/status", config.Configuration.OrcidSecureURL)
   589  	//fmt.Printf( "%s\n", url )
   590  
   591  	// get the access token
   592  	token, err := getAccessToken()
   593  	if err != nil {
   594  		return err
   595  	}
   596  	return issueAuthorizedGet(url, "application/json", token)
   597  }
   598  
   599  // issue a GET to the specified URL and use a bearer token for aurthorization
   600  // ignore the response payload and return an error if we get a non-200 response
   601  func issueAuthorizedGet(url string, accept string, authToken string) error {
   602  
   603  	// construct the auth field
   604  	auth := fmt.Sprintf("Bearer %s", authToken)
   605  
   606  	// issue the request
   607  	start := time.Now()
   608  	resp, _, errs := gorequest.New().
   609  		SetDebug(config.Configuration.Debug).
   610  		Get(url).
   611  		Set("Accept", accept).
   612  		Set("Authorization", auth).
   613  		Timeout(authTimeout).
   614  		End()
   615  	duration := time.Since(start)
   616  
   617  	// check for errors
   618  	if errs != nil {
   619  		logger.Log(fmt.Sprintf("ERROR: service (%s) returns %s in %s", url, errs, duration))
   620  		return errs[0]
   621  	}
   622  
   623  	defer io.Copy(ioutil.Discard, resp.Body)
   624  	defer resp.Body.Close()
   625  
   626  	logger.Log(fmt.Sprintf("Service (%s) returns http %d in %s", url, resp.StatusCode, duration))
   627  
   628  	// check for a non-success status and return if we find it
   629  	if resp.StatusCode != http.StatusOK {
   630  		return fmt.Errorf("Service (%s) returns http %d", url, resp.StatusCode)
   631  	}
   632  
   633  	return nil
   634  }
   635  
   636  // singleton type access to the access token
   637  func getAccessToken() (string, error) {
   638  
   639  	// do we need an access token
   640  	if len(currentAccessToken) == 0 {
   641  		token, status, err := getOauthToken()
   642  		if status != http.StatusOK {
   643  			return emptyAccessToken, err
   644  		}
   645  		currentAccessToken = token
   646  	}
   647  	return currentAccessToken, nil
   648  }
   649  
   650  //
   651  // end of file
   652  //