summaryrefslogtreecommitdiff
path: root/internal/gtserror/withcode.go
blob: 34889b9619b1e7549395e4acf63b321c97cd6dff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
   GoToSocial
   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package gtserror

import (
	"errors"
	"net/http"
	"strings"
)

// WithCode wraps an internal error with an http code, and a 'safe' version of
// the error that can be served to clients without revealing internal business logic.
//
// A typical use of this error would be to first log the Original error, then return
// the Safe error and the StatusCode to an API caller.
type WithCode interface {
	// Error returns the original internal error for debugging within the GoToSocial logs.
	// This should *NEVER* be returned to a client as it may contain sensitive information.
	Error() string
	// Safe returns the API-safe version of the error for serialization towards a client.
	// There's not much point logging this internally because it won't contain much helpful information.
	Safe() string
	//  Code returns the status code for serving to a client.
	Code() int
}

type withCode struct {
	original error
	safe     error
	code     int
}

func (e withCode) Error() string {
	return e.original.Error()
}

func (e withCode) Safe() string {
	return e.safe.Error()
}

func (e withCode) Code() int {
	return e.code
}

// NewErrorBadRequest returns an ErrorWithCode 400 with the given original error and optional help text.
func NewErrorBadRequest(original error, helpText ...string) WithCode {
	safe := "bad request"
	if helpText != nil {
		safe = safe + ": " + strings.Join(helpText, ": ")
	}
	return withCode{
		original: original,
		safe:     errors.New(safe),
		code:     http.StatusBadRequest,
	}
}

// NewErrorNotAuthorized returns an ErrorWithCode 401 with the given original error and optional help text.
func NewErrorNotAuthorized(original error, helpText ...string) WithCode {
	safe := "not authorized"
	if helpText != nil {
		safe = safe + ": " + strings.Join(helpText, ": ")
	}
	return withCode{
		original: original,
		safe:     errors.New(safe),
		code:     http.StatusUnauthorized,
	}
}

// NewErrorForbidden returns an ErrorWithCode 403 with the given original error and optional help text.
func NewErrorForbidden(original error, helpText ...string) WithCode {
	safe := "forbidden"
	if helpText != nil {
		safe = safe + ": " + strings.Join(helpText, ": ")
	}
	return withCode{
		original: original,
		safe:     errors.New(safe),
		code:     http.StatusForbidden,
	}
}

// NewErrorNotFound returns an ErrorWithCode 404 with the given original error and optional help text.
func NewErrorNotFound(original error, helpText ...string) WithCode {
	safe := "404 not found"
	if helpText != nil {
		safe = safe + ": " + strings.Join(helpText, ": ")
	}
	return withCode{
		original: original,
		safe:     errors.New(safe),
		code:     http.StatusNotFound,
	}
}

// NewErrorInternalError returns an ErrorWithCode 500 with the given original error and optional help text.
func NewErrorInternalError(original error, helpText ...string) WithCode {
	safe := "internal server error"
	if helpText != nil {
		safe = safe + ": " + strings.Join(helpText, ": ")
	}
	return withCode{
		original: original,
		safe:     errors.New(safe),
		code:     http.StatusInternalServerError,
	}
}

// NewErrorConflict returns an ErrorWithCode 409 with the given original error and optional help text.
func NewErrorConflict(original error, helpText ...string) WithCode {
	safe := "conflict"
	if helpText != nil {
		safe = safe + ": " + strings.Join(helpText, ": ")
	}
	return withCode{
		original: original,
		safe:     errors.New(safe),
		code:     http.StatusConflict,
	}
}