github.com/openshift-online/ocm-sdk-go@v0.1.473/errors/errors.go (about)

     1  /*
     2  Copyright (c) 2020 Red Hat, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8    http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // IMPORTANT: This file has been generated automatically, refrain from modifying it manually as all
    18  // your changes will be lost when the file is generated again.
    19  
    20  package errors // github.com/openshift-online/ocm-sdk-go/errors
    21  
    22  import (
    23  	"fmt"
    24  	"io"
    25  	"net/http"
    26  	"strconv"
    27  	"strings"
    28  	"time"
    29  
    30  	"github.com/golang/glog"
    31  	jsoniter "github.com/json-iterator/go"
    32  	"github.com/openshift-online/ocm-sdk-go/helpers"
    33  )
    34  
    35  // Error kind is the name of the type used to represent errors.
    36  const ErrorKind = "Error"
    37  
    38  // ErrorNilKind is the name of the type used to nil errors.
    39  const ErrorNilKind = "ErrorNil"
    40  
    41  // ErrorBuilder is a builder for the error type.
    42  type ErrorBuilder struct {
    43  	bitmap_     uint32
    44  	status      int
    45  	id          string
    46  	href        string
    47  	code        string
    48  	reason      string
    49  	details     interface{}
    50  	operationID string
    51  	timestamp   *time.Time
    52  }
    53  
    54  // Error represents errors.
    55  type Error struct {
    56  	bitmap_     uint32
    57  	status      int
    58  	id          string
    59  	href        string
    60  	code        string
    61  	reason      string
    62  	details     interface{}
    63  	operationID string
    64  	timestamp   *time.Time
    65  }
    66  
    67  // NewError creates a new builder that can then be used to create error objects.
    68  func NewError() *ErrorBuilder {
    69  	return &ErrorBuilder{}
    70  }
    71  
    72  // Status sets the HTTP status code.
    73  func (b *ErrorBuilder) Status(value int) *ErrorBuilder {
    74  	b.status = value
    75  	b.bitmap_ |= 1
    76  	return b
    77  }
    78  
    79  // ID sets the identifier of the error.
    80  func (b *ErrorBuilder) ID(value string) *ErrorBuilder {
    81  	b.id = value
    82  	b.bitmap_ |= 2
    83  	return b
    84  }
    85  
    86  // HREF sets the link of the error.
    87  func (b *ErrorBuilder) HREF(value string) *ErrorBuilder {
    88  	b.href = value
    89  	b.bitmap_ |= 4
    90  	return b
    91  }
    92  
    93  // Code sets the code of the error.
    94  func (b *ErrorBuilder) Code(value string) *ErrorBuilder {
    95  	b.code = value
    96  	b.bitmap_ |= 8
    97  	return b
    98  }
    99  
   100  // Reason sets the reason of the error.
   101  func (b *ErrorBuilder) Reason(value string) *ErrorBuilder {
   102  	b.reason = value
   103  	b.bitmap_ |= 16
   104  	return b
   105  }
   106  
   107  // OperationID sets the identifier of the operation that caused the error.
   108  func (b *ErrorBuilder) OperationID(value string) *ErrorBuilder {
   109  	b.operationID = value
   110  	b.bitmap_ |= 32
   111  	return b
   112  }
   113  
   114  // Details sets additional details of the error.
   115  func (b *ErrorBuilder) Details(value interface{}) *ErrorBuilder {
   116  	b.details = value
   117  	b.bitmap_ |= 64
   118  	return b
   119  }
   120  
   121  // Timestamp sets the moment when it happened.
   122  func (b *ErrorBuilder) Timestamp(value *time.Time) *ErrorBuilder {
   123  	b.timestamp = value
   124  	b.bitmap_ |= 128
   125  	return b
   126  }
   127  
   128  // Copy copies the attributes of the given error into this
   129  // builder, discarding any previous values.
   130  func (b *ErrorBuilder) Copy(object *Error) *ErrorBuilder {
   131  	if object == nil {
   132  		return b
   133  	}
   134  	b.bitmap_ = object.bitmap_
   135  	b.status = object.status
   136  	b.id = object.id
   137  	b.href = object.href
   138  	b.code = object.code
   139  	b.reason = object.reason
   140  	b.details = object.details
   141  	b.operationID = object.operationID
   142  	b.timestamp = object.timestamp
   143  	return b
   144  }
   145  
   146  // Build uses the information stored in the builder to create a new error object.
   147  func (b *ErrorBuilder) Build() (result *Error, err error) {
   148  	result = &Error{
   149  		status:      b.status,
   150  		id:          b.id,
   151  		href:        b.href,
   152  		code:        b.code,
   153  		reason:      b.reason,
   154  		details:     b.details,
   155  		operationID: b.operationID,
   156  		timestamp:   b.timestamp,
   157  		bitmap_:     b.bitmap_,
   158  	}
   159  	return
   160  }
   161  
   162  // Kind returns the name of the type of the error.
   163  func (e *Error) Kind() string {
   164  	if e == nil {
   165  		return ErrorNilKind
   166  	}
   167  	return ErrorKind
   168  }
   169  
   170  // Status returns the HTTP status code.
   171  func (e *Error) Status() int {
   172  	if e != nil && e.bitmap_&1 != 0 {
   173  		return e.status
   174  	}
   175  	return 0
   176  }
   177  
   178  // GetStatus returns the HTTP status code of the error and a flag indicating
   179  // if the status has a value.
   180  func (e *Error) GetStatus() (value int, ok bool) {
   181  	ok = e != nil && e.bitmap_&1 != 0
   182  	if ok {
   183  		value = e.status
   184  	}
   185  	return
   186  }
   187  
   188  // ID returns the identifier of the error.
   189  func (e *Error) ID() string {
   190  	if e != nil && e.bitmap_&2 != 0 {
   191  		return e.id
   192  	}
   193  	return ""
   194  }
   195  
   196  // GetID returns the identifier of the error and a flag indicating if the
   197  // identifier has a value.
   198  func (e *Error) GetID() (value string, ok bool) {
   199  	ok = e != nil && e.bitmap_&2 != 0
   200  	if ok {
   201  		value = e.id
   202  	}
   203  	return
   204  }
   205  
   206  // HREF returns the link to the error.
   207  func (e *Error) HREF() string {
   208  	if e != nil && e.bitmap_&4 != 0 {
   209  		return e.href
   210  	}
   211  	return ""
   212  }
   213  
   214  // GetHREF returns the link of the error and a flag indicating if the
   215  // link has a value.
   216  func (e *Error) GetHREF() (value string, ok bool) {
   217  	ok = e != nil && e.bitmap_&4 != 0
   218  	if ok {
   219  		value = e.href
   220  	}
   221  	return
   222  }
   223  
   224  // Code returns the code of the error.
   225  func (e *Error) Code() string {
   226  	if e != nil && e.bitmap_&8 != 0 {
   227  		return e.code
   228  	}
   229  	return ""
   230  }
   231  
   232  // GetCode returns the link of the error and a flag indicating if the
   233  // code has a value.
   234  func (e *Error) GetCode() (value string, ok bool) {
   235  	ok = e != nil && e.bitmap_&8 != 0
   236  	if ok {
   237  		value = e.code
   238  	}
   239  	return
   240  }
   241  
   242  // Reason returns the reason of the error.
   243  func (e *Error) Reason() string {
   244  	if e != nil && e.bitmap_&16 != 0 {
   245  		return e.reason
   246  	}
   247  	return ""
   248  }
   249  
   250  // GetReason returns the link of the error and a flag indicating if the
   251  // reason has a value.
   252  func (e *Error) GetReason() (value string, ok bool) {
   253  	ok = e != nil && e.bitmap_&16 != 0
   254  	if ok {
   255  		value = e.reason
   256  	}
   257  	return
   258  }
   259  
   260  // OperationID returns the identifier of the operation that caused the error.
   261  func (e *Error) OperationID() string {
   262  	if e != nil && e.bitmap_&32 != 0 {
   263  		return e.operationID
   264  	}
   265  	return ""
   266  }
   267  
   268  // GetOperationID returns the identifier of the operation that caused the error and
   269  // a flag indicating if that identifier does have a value.
   270  func (e *Error) GetOperationID() (value string, ok bool) {
   271  	ok = e != nil && e.bitmap_&32 != 0
   272  	if ok {
   273  		value = e.operationID
   274  	}
   275  	return
   276  }
   277  
   278  // Details returns the details of the error
   279  func (e *Error) Details() interface{} {
   280  	if e != nil && e.bitmap_&64 != 0 {
   281  		return e.details
   282  	}
   283  	return nil
   284  }
   285  
   286  // GetDetails returns the details of the error and a flag
   287  // indicating if the details have a value.
   288  func (e *Error) GetDetails() (value interface{}, ok bool) {
   289  	ok = e != nil && e.bitmap_&64 != 0
   290  	if ok {
   291  		value = e.details
   292  	}
   293  	return
   294  }
   295  
   296  // Timestamp sets the moment when it happened
   297  func (e *Error) Timestamp() *time.Time {
   298  	if e != nil && e.bitmap_&128 != 0 {
   299  		return e.timestamp
   300  	}
   301  	return nil
   302  }
   303  
   304  // GetTimestamp returns the timestamp of the error and a flag
   305  // indicating if the timestamp have a value.
   306  func (e *Error) GetTimestamp() (value *time.Time, ok bool) {
   307  	ok = e != nil && e.bitmap_&128 != 0
   308  	if ok {
   309  		value = e.timestamp
   310  	}
   311  	return
   312  }
   313  
   314  // Error is the implementation of the error interface.
   315  // Details are intentionally left out as there is no guarantee of their type
   316  func (e *Error) Error() string {
   317  	chunks := make([]string, 0, 3)
   318  	if e.bitmap_&1 != 0 {
   319  		chunks = append(chunks, fmt.Sprintf("status is %d", e.status))
   320  	}
   321  	if e.bitmap_&2 != 0 {
   322  		chunks = append(chunks, fmt.Sprintf("identifier is '%s'", e.id))
   323  	}
   324  	if e.bitmap_&8 != 0 {
   325  		chunks = append(chunks, fmt.Sprintf("code is '%s'", e.code))
   326  	}
   327  	if e.bitmap_&128 != 0 {
   328  		chunks = append(chunks, fmt.Sprintf("at '%v'", e.timestamp.Format(time.RFC3339)))
   329  	}
   330  	if e.bitmap_&32 != 0 {
   331  		chunks = append(chunks, fmt.Sprintf("operation identifier is '%s'", e.operationID))
   332  	}
   333  	var result string
   334  	size := len(chunks)
   335  	if size == 1 {
   336  		result = chunks[0]
   337  	} else if size > 1 {
   338  		result = strings.Join(chunks[0:size-1], ", ") + " and " + chunks[size-1]
   339  	}
   340  	if e.bitmap_&16 != 0 {
   341  		if result != "" {
   342  			result = result + ": "
   343  		}
   344  		result = result + e.reason
   345  	}
   346  	if result == "" {
   347  		result = "unknown error"
   348  	}
   349  	return result
   350  }
   351  
   352  // String returns a string representing the error.
   353  func (e *Error) String() string {
   354  	return e.Error()
   355  }
   356  
   357  // UnmarshalError reads an error from the given source which can be an slice of
   358  // bytes, a string, a reader or a JSON decoder.
   359  func UnmarshalError(source interface{}) (object *Error, err error) {
   360  	iterator, err := helpers.NewIterator(source)
   361  	if err != nil {
   362  		return
   363  	}
   364  	object = readError(iterator)
   365  	err = iterator.Error
   366  	return
   367  }
   368  
   369  // UnmarshalErrorStatus reads an error from the given source and sets
   370  // the given status code.
   371  func UnmarshalErrorStatus(source interface{}, status int) (object *Error, err error) {
   372  	object, err = UnmarshalError(source)
   373  	if err != nil {
   374  		return
   375  	}
   376  	object.status = status
   377  	object.bitmap_ |= 1
   378  	return
   379  }
   380  func readError(iterator *jsoniter.Iterator) *Error {
   381  	object := &Error{}
   382  	for {
   383  		field := iterator.ReadObject()
   384  		if field == "" {
   385  			break
   386  		}
   387  		switch field {
   388  		case "status":
   389  			object.status = iterator.ReadInt()
   390  			object.bitmap_ |= 1
   391  		case "id":
   392  			object.id = iterator.ReadString()
   393  			object.bitmap_ |= 2
   394  		case "href":
   395  			object.href = iterator.ReadString()
   396  			object.bitmap_ |= 4
   397  		case "code":
   398  			object.code = iterator.ReadString()
   399  			object.bitmap_ |= 8
   400  		case "reason":
   401  			object.reason = iterator.ReadString()
   402  			object.bitmap_ |= 16
   403  		case "operation_id":
   404  			object.operationID = iterator.ReadString()
   405  			object.bitmap_ |= 32
   406  		case "details":
   407  			object.details = iterator.ReadAny().GetInterface()
   408  			object.bitmap_ |= 64
   409  		case "timestamp":
   410  			text := iterator.ReadString()
   411  			value, err := time.Parse(time.RFC3339, text)
   412  			if err != nil {
   413  				iterator.ReportError("", err.Error())
   414  			}
   415  			object.timestamp = &value
   416  			object.bitmap_ |= 128
   417  		default:
   418  			iterator.ReadAny()
   419  		}
   420  	}
   421  	return object
   422  }
   423  
   424  // MarshalError writes an error to the given writer.
   425  func MarshalError(e *Error, writer io.Writer) error {
   426  	stream := helpers.NewStream(writer)
   427  	writeError(e, stream)
   428  	err := stream.Flush()
   429  	if err != nil {
   430  		return err
   431  	}
   432  	return stream.Error
   433  }
   434  func writeError(e *Error, stream *jsoniter.Stream) {
   435  	stream.WriteObjectStart()
   436  	stream.WriteObjectField("kind")
   437  	stream.WriteString(ErrorKind)
   438  	if e.bitmap_&1 != 0 {
   439  		stream.WriteMore()
   440  		stream.WriteObjectField("status")
   441  		stream.WriteInt(e.status)
   442  	}
   443  	if e.bitmap_&2 != 0 {
   444  		stream.WriteMore()
   445  		stream.WriteObjectField("id")
   446  		stream.WriteString(e.id)
   447  	}
   448  	if e.bitmap_&4 != 0 {
   449  		stream.WriteMore()
   450  		stream.WriteObjectField("href")
   451  		stream.WriteString(e.href)
   452  	}
   453  	if e.bitmap_&8 != 0 {
   454  		stream.WriteMore()
   455  		stream.WriteObjectField("code")
   456  		stream.WriteString(e.code)
   457  	}
   458  	if e.bitmap_&16 != 0 {
   459  		stream.WriteMore()
   460  		stream.WriteObjectField("reason")
   461  		stream.WriteString(e.reason)
   462  	}
   463  	if e.bitmap_&32 != 0 {
   464  		stream.WriteMore()
   465  		stream.WriteObjectField("operation_id")
   466  		stream.WriteString(e.operationID)
   467  	}
   468  	if e.bitmap_&64 != 0 {
   469  		stream.WriteMore()
   470  		stream.WriteObjectField("details")
   471  		stream.WriteVal(e.details)
   472  	}
   473  	if e.bitmap_&128 != 0 {
   474  		stream.WriteMore()
   475  		stream.WriteObjectField("timestamp")
   476  		stream.WriteVal(e.timestamp)
   477  	}
   478  	stream.WriteObjectEnd()
   479  }
   480  
   481  var panicID = "1000"
   482  var panicError, _ = NewError().
   483  	ID(panicID).
   484  	Reason("An unexpected error happened, please check the log of the service " +
   485  		"for details").
   486  	Build()
   487  
   488  // SendError writes a given error and status code to a response writer.
   489  // if an error occurred it will log the error and exit.
   490  // This methods is used internaly and no backwards compatibily is guaranteed.
   491  func SendError(w http.ResponseWriter, r *http.Request, object *Error) {
   492  	status, err := strconv.Atoi(object.ID())
   493  	if err != nil {
   494  		SendPanic(w, r)
   495  		return
   496  	}
   497  	w.Header().Set("Content-Type", "application/json")
   498  	w.WriteHeader(status)
   499  	err = MarshalError(object, w)
   500  	if err != nil {
   501  		glog.Errorf("Can't send response body for request '%s'", r.URL.Path)
   502  		return
   503  	}
   504  }
   505  
   506  // SendPanic sends a panic error response to the client, but it doesn't end the process.
   507  // This methods is used internaly and no backwards compatibily is guaranteed.
   508  func SendPanic(w http.ResponseWriter, r *http.Request) {
   509  	w.Header().Set("Content-Type", "application/json")
   510  	err := MarshalError(panicError, w)
   511  	if err != nil {
   512  		glog.Errorf(
   513  			"Can't send panic response for request '%s': %s",
   514  			r.URL.Path,
   515  			err.Error(),
   516  		)
   517  	}
   518  }
   519  
   520  // SendNotFound sends a generic 404 error.
   521  func SendNotFound(w http.ResponseWriter, r *http.Request) {
   522  	reason := fmt.Sprintf(
   523  		"Can't find resource for path '%s'",
   524  		r.URL.Path,
   525  	)
   526  	body, err := NewError().
   527  		ID("404").
   528  		Reason(reason).
   529  		Build()
   530  	if err != nil {
   531  		SendPanic(w, r)
   532  		return
   533  	}
   534  	SendError(w, r, body)
   535  }
   536  
   537  // SendMethodNotAllowed sends a generic 405 error.
   538  func SendMethodNotAllowed(w http.ResponseWriter, r *http.Request) {
   539  	reason := fmt.Sprintf(
   540  		"Method '%s' isn't supported for path '%s'",
   541  		r.Method, r.URL.Path,
   542  	)
   543  	body, err := NewError().
   544  		ID("405").
   545  		Reason(reason).
   546  		Build()
   547  	if err != nil {
   548  		SendPanic(w, r)
   549  		return
   550  	}
   551  	SendError(w, r, body)
   552  }
   553  
   554  // SendInternalServerError sends a generic 500 error.
   555  func SendInternalServerError(w http.ResponseWriter, r *http.Request) {
   556  	reason := fmt.Sprintf(
   557  		"Can't process '%s' request for path '%s' due to an internal"+
   558  			"server error",
   559  		r.Method, r.URL.Path,
   560  	)
   561  	body, err := NewError().
   562  		ID("500").
   563  		Reason(reason).
   564  		Build()
   565  	if err != nil {
   566  		SendPanic(w, r)
   567  		return
   568  	}
   569  	SendError(w, r, body)
   570  }