github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/api/cloudcontroller/ccv3/internal/routing.go (about) 1 package internal 2 3 import ( 4 "fmt" 5 "io" 6 "net/http" 7 "net/url" 8 "path" 9 "strings" 10 ) 11 12 // Params map path keys to values. For example, if your route has the path 13 // pattern: 14 // /person/:person_id/pets/:pet_type 15 // Then a correct Params map would look like: 16 // router.Params{ 17 // "person_id": "123", 18 // "pet_type": "cats", 19 // } 20 type Params map[string]string 21 22 // Route defines the property of a Cloud Controller V3 endpoint. 23 // 24 // Method can be one of the following: 25 // GET HEAD POST PUT PATCH DELETE CONNECT OPTIONS TRACE 26 // 27 // Path conforms to Pat-style pattern matching. The following docs are taken 28 // from http://godoc.org/github.com/bmizerany/pat#PatternServeMux 29 // 30 // Path Patterns may contain literals or captures. Capture names start with a 31 // colon and consist of letters A-Z, a-z, _, and 0-9. The rest of the pattern 32 // matches literally. The portion of the URL matching each name ends with an 33 // occurrence of the character in the pattern immediately following the name, 34 // or a /, whichever comes first. It is possible for a name to match the empty 35 // string. 36 // 37 // Example pattern with one capture: 38 // /hello/:name 39 // Will match: 40 // /hello/blake 41 // /hello/keith 42 // Will not match: 43 // /hello/blake/ 44 // /hello/blake/foo 45 // /foo 46 // /foo/bar 47 // 48 // Example 2: 49 // /hello/:name/ 50 // Will match: 51 // /hello/blake/ 52 // /hello/keith/foo 53 // /hello/blake 54 // /hello/keith 55 // Will not match: 56 // /foo 57 // /foo/bar 58 type Route struct { 59 // Method is any valid HTTP method 60 Method string 61 // Path contains a path pattern 62 Path string 63 } 64 65 // CreatePath combines the route's path pattern with a Params map 66 // to produce a valid path. 67 func (r Route) CreatePath(params Params) (string, error) { 68 components := strings.Split(r.Path, "/") 69 for i, c := range components { 70 if len(c) == 0 { 71 continue 72 } 73 if c[0] == ':' { 74 val, ok := params[c[1:]] 75 if !ok { 76 return "", fmt.Errorf("missing param %s", c) 77 } 78 components[i] = val 79 } 80 } 81 82 u, err := url.Parse(strings.Join(components, "/")) 83 if err != nil { 84 return "", err 85 } 86 return u.String(), nil 87 } 88 89 // Router combines route and resource information in order to generate HTTP 90 // requests. 91 type Router struct { 92 routes map[string]Route 93 baseURL string 94 } 95 96 // NewRouter returns a pointer to a new Router. 97 func NewRouter(routes map[string]Route, baseURL string) *Router { 98 return &Router{ 99 routes: routes, 100 baseURL: baseURL, 101 } 102 } 103 104 // CreateRequest returns a request key'd off of the name given. The params are 105 // merged into the URL and body is set as the request body. 106 func (router Router) CreateRequest(name string, params Params, body io.Reader) (*http.Request, error) { 107 route, ok := router.routes[name] 108 if !ok { 109 return &http.Request{}, fmt.Errorf("no route exists with the name %s", name) 110 } 111 112 uri, err := route.CreatePath(params) 113 if err != nil { 114 return &http.Request{}, err 115 } 116 117 url, err := router.urlFrom(router.baseURL, uri) 118 if err != nil { 119 return &http.Request{}, err 120 } 121 122 return http.NewRequest(route.Method, url, body) 123 } 124 125 func (Router) urlFrom(resource string, uri string) (string, error) { 126 u, err := url.Parse(resource) 127 if err != nil { 128 return "", err 129 } 130 u.Path = path.Join(u.Path, uri) 131 return u.String(), nil 132 }