Skip to content

Commit 9b96a27

Browse files
author
Raphaël Simon
committed
Merge pull request goadesign#168 from raphael/versioning_support
Fix routing to versioned APIs
2 parents 53073ba + 0be33a6 commit 9b96a27

14 files changed

+183
-180
lines changed

design/definitions.go

+9-31
Original file line numberDiff line numberDiff line change
@@ -697,29 +697,29 @@ func (r *ResourceDefinition) CanonicalAction() *ActionDefinition {
697697
// URITemplate returns a httprouter compliant URI template to this resource.
698698
// The result is the empty string if the resource does not have a "show" action
699699
// and does not define a different canonical action.
700-
func (r *ResourceDefinition) URITemplate() string {
700+
func (r *ResourceDefinition) URITemplate(version *APIVersionDefinition) string {
701701
ca := r.CanonicalAction()
702702
if ca == nil || len(ca.Routes) == 0 {
703703
return ""
704704
}
705-
return ca.Routes[0].FullPath()
705+
return ca.Routes[0].FullPath(version)
706706
}
707707

708708
// FullPath computes the base path to the resource actions concatenating the API and parent resource
709709
// base paths as needed.
710-
func (r *ResourceDefinition) FullPath() string {
710+
func (r *ResourceDefinition) FullPath(version *APIVersionDefinition) string {
711711
var basePath string
712712
if p := r.Parent(); p != nil {
713713
if ca := p.CanonicalAction(); ca != nil {
714714
if routes := ca.Routes; len(routes) > 0 {
715715
// Note: all these tests should be true at code generation time
716716
// as DSL validation makes sure that parent resources have a
717717
// canonical path.
718-
basePath = path.Join(routes[0].FullPath())
718+
basePath = path.Join(routes[0].FullPath(version))
719719
}
720720
}
721721
} else {
722-
basePath = Design.BasePath
722+
basePath = version.BasePath
723723
}
724724
return httprouter.CleanPath(path.Join(basePath, r.BasePath))
725725
}
@@ -886,28 +886,6 @@ func (a *ActionDefinition) AllParams() *AttributeDefinition {
886886
return res
887887
}
888888

889-
// AllParamNames returns the path and query string parameter names of the action across all its
890-
// routes.
891-
func (a *ActionDefinition) AllParamNames() []string {
892-
var params []string
893-
for _, r := range a.Routes {
894-
for _, p := range r.Params() {
895-
found := false
896-
for _, pa := range params {
897-
if pa == p {
898-
found = true
899-
break
900-
}
901-
}
902-
if !found {
903-
params = append(params, p)
904-
}
905-
}
906-
}
907-
sort.Strings(params)
908-
return params
909-
}
910-
911889
// Context returns the generic definition name used in error messages.
912890
func (a *AttributeDefinition) Context() string {
913891
return ""
@@ -1178,19 +1156,19 @@ func (r *RouteDefinition) Context() string {
11781156

11791157
// Params returns the route parameters.
11801158
// For example for the route "GET /foo/:fooID" Params returns []string{"fooID"}.
1181-
func (r *RouteDefinition) Params() []string {
1182-
return ExtractWildcards(r.FullPath())
1159+
func (r *RouteDefinition) Params(version *APIVersionDefinition) []string {
1160+
return ExtractWildcards(r.FullPath(version))
11831161
}
11841162

11851163
// FullPath returns the action full path computed by concatenating the API and resource base paths
11861164
// with the action specific path.
1187-
func (r *RouteDefinition) FullPath() string {
1165+
func (r *RouteDefinition) FullPath(version *APIVersionDefinition) string {
11881166
if strings.HasPrefix(r.Path, "//") {
11891167
return httprouter.CleanPath(r.Path[1:])
11901168
}
11911169
var base string
11921170
if r.Parent != nil && r.Parent.Parent != nil {
1193-
base = r.Parent.Parent.FullPath()
1171+
base = r.Parent.Parent.FullPath(version)
11941172
}
11951173
return httprouter.CleanPath(path.Join(base, r.Path))
11961174
}

design/dsl/action.go

-10
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,6 @@ func Action(name string, dsl func()) {
7070
func Routing(routes ...*design.RouteDefinition) {
7171
if a, ok := actionDefinition(true); ok {
7272
for _, r := range routes {
73-
rwcs := design.ExtractWildcards(a.Parent.FullPath())
74-
wcs := design.ExtractWildcards(r.Path)
75-
for _, rwc := range rwcs {
76-
for _, wc := range wcs {
77-
if rwc == wc {
78-
ReportError(`duplicate wildcard "%s" in resource base path "%s" and action route "%s"`,
79-
wc, a.Parent.FullPath(), r.Path)
80-
}
81-
}
82-
}
8373
r.Parent = a
8474
a.Routes = append(a.Routes, r)
8575
}

design/dsl/action_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ var _ = Describe("Action", func() {
3939
It("produces an invalid action", func() {
4040
Ω(Errors).ShouldNot(HaveOccurred())
4141
Ω(action).ShouldNot(BeNil())
42-
Ω(action.Validate()).Should(HaveOccurred())
42+
Ω(action.Validate(Design.APIVersionDefinition)).Should(HaveOccurred())
4343
})
4444
})
4545

@@ -55,7 +55,7 @@ var _ = Describe("Action", func() {
5555
Ω(Errors).ShouldNot(HaveOccurred())
5656
Ω(action).ShouldNot(BeNil())
5757
Ω(action.Name).Should(Equal(name))
58-
Ω(action.Validate()).ShouldNot(HaveOccurred())
58+
Ω(action.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
5959
Ω(action.Routes).ShouldNot(BeNil())
6060
Ω(action.Routes).Should(HaveLen(1))
6161
Ω(action.Routes[0]).Should(Equal(route))
@@ -74,7 +74,7 @@ var _ = Describe("Action", func() {
7474
It("produces a valid action with the given properties", func() {
7575
Ω(Errors).ShouldNot(HaveOccurred())
7676
Ω(action).ShouldNot(BeNil())
77-
Ω(action.Validate()).ShouldNot(HaveOccurred())
77+
Ω(action.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
7878
Ω(action.Payload).ShouldNot(BeNil())
7979
Ω(action.Payload.Type).Should(Equal(String))
8080
})
@@ -102,7 +102,7 @@ var _ = Describe("Action", func() {
102102
It("produces a valid action with the given properties", func() {
103103
Ω(Errors).ShouldNot(HaveOccurred())
104104
Ω(action).ShouldNot(BeNil())
105-
Ω(action.Validate()).ShouldNot(HaveOccurred())
105+
Ω(action.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
106106
Ω(action.Name).Should(Equal(name))
107107
Ω(action.Description).Should(Equal(description))
108108
Ω(action.Routes).Should(HaveLen(1))

design/dsl/resource_test.go

+12-12
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var _ = Describe("Resource", func() {
2828
Context("with no DSL and no name", func() {
2929
It("produces an invalid resource definition", func() {
3030
Ω(res).ShouldNot(BeNil())
31-
Ω(res.Validate()).Should(HaveOccurred())
31+
Ω(res.Validate(Design.APIVersionDefinition)).Should(HaveOccurred())
3232
})
3333
})
3434

@@ -39,7 +39,7 @@ var _ = Describe("Resource", func() {
3939

4040
It("produces a valid resource definition and defaults the media type to plain/text", func() {
4141
Ω(res).ShouldNot(BeNil())
42-
Ω(res.Validate()).ShouldNot(HaveOccurred())
42+
Ω(res.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
4343
Ω(res.MediaType).Should(Equal("plain/text"))
4444
})
4545
})
@@ -56,7 +56,7 @@ var _ = Describe("Resource", func() {
5656

5757
It("sets the description", func() {
5858
Ω(res).ShouldNot(BeNil())
59-
Ω(res.Validate()).ShouldNot(HaveOccurred())
59+
Ω(res.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
6060
Ω(res.Description).Should(Equal(description))
6161
})
6262
})
@@ -74,7 +74,7 @@ var _ = Describe("Resource", func() {
7474
It("sets the parent and produces an invalid resource definition", func() {
7575
Ω(res).ShouldNot(BeNil())
7676
Ω(res.ParentName).Should(Equal(parent))
77-
Ω(res.Validate()).Should(HaveOccurred())
77+
Ω(res.Validate(Design.APIVersionDefinition)).Should(HaveOccurred())
7878
})
7979
})
8080

@@ -90,7 +90,7 @@ var _ = Describe("Resource", func() {
9090

9191
It("sets the actions", func() {
9292
Ω(res).ShouldNot(BeNil())
93-
Ω(res.Validate()).ShouldNot(HaveOccurred())
93+
Ω(res.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
9494
Ω(res.Actions).Should(HaveLen(1))
9595
Ω(res.Actions).Should(HaveKey(actionName))
9696
})
@@ -109,7 +109,7 @@ var _ = Describe("Resource", func() {
109109
It("sets the canonical action and produces an invalid resource definition", func() {
110110
Ω(res).ShouldNot(BeNil())
111111
Ω(res.CanonicalActionName).Should(Equal(can))
112-
Ω(res.Validate()).Should(HaveOccurred())
112+
Ω(res.Validate(Design.APIVersionDefinition)).Should(HaveOccurred())
113113
})
114114
})
115115

@@ -127,7 +127,7 @@ var _ = Describe("Resource", func() {
127127
It("sets the canonical action and produces a valid resource definition", func() {
128128
Ω(res).ShouldNot(BeNil())
129129
Ω(res.CanonicalActionName).Should(Equal(can))
130-
Ω(res.Validate()).ShouldNot(HaveOccurred())
130+
Ω(res.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
131131
})
132132
})
133133

@@ -143,7 +143,7 @@ var _ = Describe("Resource", func() {
143143

144144
It("sets the base path", func() {
145145
Ω(res).ShouldNot(BeNil())
146-
Ω(res.Validate()).ShouldNot(HaveOccurred())
146+
Ω(res.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
147147
Ω(res.BasePath).Should(Equal(basePath))
148148
})
149149
})
@@ -163,7 +163,7 @@ var _ = Describe("Resource", func() {
163163

164164
It("sets the base path and params", func() {
165165
Ω(res).ShouldNot(BeNil())
166-
Ω(res.Validate()).ShouldNot(HaveOccurred())
166+
Ω(res.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
167167
Ω(res.BasePath).Should(Equal(basePath))
168168
Ω(res.BaseParams).ShouldNot(BeNil())
169169
Ω(res.BaseParams.Type).ShouldNot(BeNil())
@@ -183,7 +183,7 @@ var _ = Describe("Resource", func() {
183183

184184
It("sets the media type", func() {
185185
Ω(res).ShouldNot(BeNil())
186-
Ω(res.Validate()).ShouldNot(HaveOccurred())
186+
Ω(res.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
187187
Ω(res.MediaType).Should(Equal(mediaType))
188188
})
189189
})
@@ -223,7 +223,7 @@ var _ = Describe("Resource", func() {
223223

224224
It("sets the media type", func() {
225225
Ω(res).ShouldNot(BeNil())
226-
Ω(res.Validate()).ShouldNot(HaveOccurred())
226+
Ω(res.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
227227
Ω(res.MediaType).Should(Equal(identifier))
228228
})
229229
})
@@ -255,7 +255,7 @@ var _ = Describe("Resource", func() {
255255

256256
It("runs the trait", func() {
257257
Ω(res).ShouldNot(BeNil())
258-
Ω(res.Validate()).ShouldNot(HaveOccurred())
258+
Ω(res.Validate(Design.APIVersionDefinition)).ShouldNot(HaveOccurred())
259259
Ω(res.Description).Should(Equal(description))
260260
})
261261
})

design/dsl/runner.go

+29-23
Original file line numberDiff line numberDiff line change
@@ -179,36 +179,42 @@ func finalizeResource(r *design.ResourceDefinition) {
179179
}
180180
// 2. Create implicit action parameters for path wildcards that dont' have one
181181
for _, r := range a.Routes {
182-
wcs := design.ExtractWildcards(r.FullPath())
183-
for _, wc := range wcs {
184-
found := false
185-
var o design.Object
186-
if all := a.AllParams(); all != nil {
187-
o = all.Type.ToObject()
188-
} else {
189-
o = design.Object{}
190-
a.Params = &design.AttributeDefinition{Type: o}
191-
}
192-
for n := range o {
193-
if n == wc {
194-
found = true
195-
break
182+
design.Design.IterateVersions(func(ver *design.APIVersionDefinition) error {
183+
wcs := design.ExtractWildcards(r.FullPath(ver))
184+
for _, wc := range wcs {
185+
found := false
186+
var o design.Object
187+
if all := a.AllParams(); all != nil {
188+
o = all.Type.ToObject()
189+
} else {
190+
o = design.Object{}
191+
a.Params = &design.AttributeDefinition{Type: o}
192+
}
193+
for n := range o {
194+
if n == wc {
195+
found = true
196+
break
197+
}
198+
}
199+
if !found {
200+
o[wc] = &design.AttributeDefinition{Type: design.String}
196201
}
197202
}
198-
if !found {
199-
o[wc] = &design.AttributeDefinition{Type: design.String}
200-
}
201-
}
203+
return nil
204+
})
202205
}
203206
// 3. Compute QueryParams from Params
204207
if params := a.Params; params != nil {
205208
queryParams := params.Dup()
206-
for _, route := range a.Routes {
207-
pnames := route.Params()
208-
for _, pname := range pnames {
209-
delete(queryParams.Type.ToObject(), pname)
209+
design.Design.IterateVersions(func(ver *design.APIVersionDefinition) error {
210+
for _, route := range a.Routes {
211+
pnames := route.Params(ver)
212+
for _, pname := range pnames {
213+
delete(queryParams.Type.ToObject(), pname)
214+
}
210215
}
211-
}
216+
return nil
217+
})
212218
// (note: we may end up with required attribute names that don't correspond
213219
// to actual attributes cos' we just deleted them but that's probably OK.)
214220
a.QueryParams = queryParams

0 commit comments

Comments
 (0)