-
-
Notifications
You must be signed in to change notification settings - Fork 564
/
Copy pathservice.go
153 lines (140 loc) Β· 4.15 KB
/
service.go
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package expr
import (
"fmt"
"goa.design/goa/eval"
)
type (
// ServiceExpr describes a set of related methods.
ServiceExpr struct {
// DSLFunc contains the DSL used to initialize the expression.
eval.DSLFunc
// Name of service.
Name string
// Description of service used in documentation.
Description string
// Docs points to external documentation
Docs *DocsExpr
// Methods is the list of service methods.
Methods []*MethodExpr
// Errors list the errors common to all the service methods.
Errors []*ErrorExpr
// Requirements contains the security requirements that apply to
// all the service methods. One requirement is composed of
// potentially multiple schemes. Incoming requests must validate
// at least one requirement to be authorized.
Requirements []*SecurityExpr
// Meta is a set of key/value pairs with semantic that is
// specific to each generator.
Meta MetaExpr
}
// ErrorExpr defines an error response. It consists of a named
// attribute.
ErrorExpr struct {
// AttributeExpr is the underlying attribute.
*AttributeExpr
// Name is the unique name of the error.
Name string
}
)
// Method returns the method expression with the given name, nil if there isn't
// one.
func (s *ServiceExpr) Method(n string) *MethodExpr {
for _, m := range s.Methods {
if m.Name == n {
return m
}
}
return nil
}
// EvalName returns the generic expression name used in error messages.
func (s *ServiceExpr) EvalName() string {
if s.Name == "" {
return "unnamed service"
}
return fmt.Sprintf("service %#v", s.Name)
}
// Error returns the error with the given name if any.
func (s *ServiceExpr) Error(name string) *ErrorExpr {
for _, erro := range s.Errors {
if erro.Name == name {
return erro
}
}
return Root.Error(name)
}
// Hash returns a unique hash value for s.
func (s *ServiceExpr) Hash() string {
return "_service_+" + s.Name
}
// Validate validates the service methods and errors.
func (s *ServiceExpr) Validate() error {
verr := new(eval.ValidationErrors)
for _, e := range s.Errors {
if err := e.Validate(); err != nil {
if verrs, ok := err.(*eval.ValidationErrors); ok {
verr.Merge(verrs)
}
}
}
return verr
}
// Finalize finalizes all the service methods and errors.
func (s *ServiceExpr) Finalize() {
for _, e := range s.Errors {
e.Finalize()
}
}
// Validate checks that the error name is found in the result meta for
// custom error types.
func (e *ErrorExpr) Validate() error {
verr := new(eval.ValidationErrors)
var errField string
walkAttribute(e.AttributeExpr, func(name string, att *AttributeExpr) error {
if _, ok := att.Meta["struct:error:name"]; ok {
if errField != "" {
verr.Add(e, "attribute %q has 'struct:error:name' meta which is already set for attribute %q in %q type", name, errField, e.AttributeExpr.Type.Name())
}
errField = name
if att.Type != String {
verr.Add(e, "attribute %q with 'struct:error:name' in the meta must be a string in %q type", name, e.AttributeExpr.Type.Name())
}
if !e.AttributeExpr.IsRequired(name) {
verr.Add(e, "attribute %q with 'struct:error:name' in the meta must be required in %q type", name, e.AttributeExpr.Type.Name())
}
}
return nil
})
return verr
}
// Finalize makes sure the error type is a user type since it has to generate a
// Go error.
// Note: this may produce a user type with an attribute that is not an object!
func (e *ErrorExpr) Finalize() {
att := e.AttributeExpr
switch dt := att.Type.(type) {
case UserType:
if dt != ErrorResult {
// If this type contains an attribute with "struct:error:name" meta
// then no need to do anything.
for _, nat := range *AsObject(dt) {
if _, ok := nat.Attribute.Meta["struct:error:name"]; ok {
return
}
}
// This type does not have an attribute with "struct:error:name" meta.
// It means the type is used by at most one error (otherwise validations
// would have failed).
datt := dt.Attribute()
if datt.Meta == nil {
datt.Meta = MetaExpr{}
}
datt.Meta["struct:error:name"] = []string{e.Name}
}
default:
ut := &UserTypeExpr{
AttributeExpr: att,
TypeName: e.Name,
}
e.AttributeExpr = &AttributeExpr{Type: ut}
}
}