github.com/akamai/AkamaiOPEN-edgegrid-golang/v2@v2.17.0/pkg/papi/property.go (about) 1 package papi 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "net/url" 9 10 "github.com/akamai/AkamaiOPEN-edgegrid-golang/v2/pkg/edgegriderr" 11 validation "github.com/go-ozzo/ozzo-validation/v4" 12 ) 13 14 type ( 15 // Properties contains operations available on Property resource 16 // See: https://developer.akamai.com/api/core_features/property_manager/v1.html#propertiesgroup 17 Properties interface { 18 GetProperties(ctx context.Context, r GetPropertiesRequest) (*GetPropertiesResponse, error) 19 CreateProperty(ctx context.Context, params CreatePropertyRequest) (*CreatePropertyResponse, error) 20 GetProperty(ctx context.Context, params GetPropertyRequest) (*GetPropertyResponse, error) 21 RemoveProperty(ctx context.Context, params RemovePropertyRequest) (*RemovePropertyResponse, error) 22 } 23 24 // PropertyCloneFrom optionally identifies another property instance to clone when making a POST request to create a new property 25 PropertyCloneFrom struct { 26 CloneFromVersionEtag string `json:"cloneFromVersionEtag,omitempty"` 27 CopyHostnames bool `json:"copyHostnames,omitempty"` 28 PropertyID string `json:"propertyId"` 29 Version int `json:"version"` 30 } 31 32 // Property contains configuration data to apply to edge content. 33 Property struct { 34 AccountID string `json:"accountId"` 35 AssetID string `json:"assetId"` 36 ContractID string `json:"contractId"` 37 GroupID string `json:"groupId"` 38 LatestVersion int `json:"latestVersion"` 39 Note string `json:"note"` 40 ProductID string `json:"productId"` 41 ProductionVersion *int `json:"productionVersion,omitempty"` 42 PropertyID string `json:"propertyId"` 43 PropertyName string `json:"propertyName"` 44 RuleFormat string `json:"ruleFormat"` 45 StagingVersion *int `json:"stagingVersion,omitempty"` 46 } 47 48 // PropertiesItems is an array of properties 49 PropertiesItems struct { 50 Items []*Property `json:"items"` 51 } 52 53 // GetPropertiesRequest is the argument for GetProperties 54 GetPropertiesRequest struct { 55 ContractID string 56 GroupID string 57 } 58 59 // GetPropertiesResponse is the response for GetProperties 60 GetPropertiesResponse struct { 61 Properties PropertiesItems `json:"properties"` 62 } 63 64 // CreatePropertyRequest is passed to CreateProperty 65 CreatePropertyRequest struct { 66 ContractID string 67 GroupID string 68 Property PropertyCreate 69 } 70 71 // PropertyCreate represents a POST /property request body 72 PropertyCreate struct { 73 CloneFrom *PropertyCloneFrom `json:"cloneFrom,omitempty"` 74 ProductID string `json:"productId"` 75 PropertyName string `json:"propertyName"` 76 RuleFormat string `json:"ruleFormat,omitempty"` 77 } 78 79 // CreatePropertyResponse is returned by CreateProperty 80 CreatePropertyResponse struct { 81 Response 82 PropertyID string 83 PropertyLink string `json:"propertyLink"` 84 } 85 86 // GetPropertyRequest is the argument for GetProperty 87 GetPropertyRequest struct { 88 ContractID string 89 GroupID string 90 PropertyID string 91 } 92 93 // GetPropertyResponse is the response for GetProperty 94 GetPropertyResponse struct { 95 Response 96 Properties PropertiesItems `json:"properties"` 97 Property *Property `json:"-"` 98 } 99 100 // RemovePropertyRequest is the argument for RemoveProperty 101 RemovePropertyRequest struct { 102 PropertyID string 103 ContractID string 104 GroupID string 105 } 106 107 // RemovePropertyResponse is the response for GetProperties 108 RemovePropertyResponse struct { 109 Message string `json:"message"` 110 } 111 ) 112 113 // Validate validates GetPropertiesRequest 114 func (v GetPropertiesRequest) Validate() error { 115 return validation.Errors{ 116 "ContractID": validation.Validate(v.ContractID, validation.Required), 117 "GroupID": validation.Validate(v.GroupID, validation.Required), 118 }.Filter() 119 } 120 121 // Validate validates CreatePropertyRequest 122 func (v CreatePropertyRequest) Validate() error { 123 errs := validation.Errors{ 124 "ContractID": validation.Validate(v.ContractID, validation.Required), 125 "GroupID": validation.Validate(v.GroupID, validation.Required), 126 "Property": validation.Validate(v.Property), 127 } 128 return edgegriderr.ParseValidationErrors(errs) 129 } 130 131 // Validate validates PropertyCreate 132 func (p PropertyCreate) Validate() error { 133 return validation.Errors{ 134 "ProductID": validation.Validate(p.ProductID, validation.Required), 135 "PropertyName": validation.Validate(p.PropertyName, validation.Required), 136 "CloneFrom": validation.Validate(p.CloneFrom), 137 }.Filter() 138 } 139 140 // Validate validates PropertyCloneFrom 141 func (c PropertyCloneFrom) Validate() error { 142 return validation.Errors{ 143 "PropertyID": validation.Validate(c.PropertyID), 144 "Version": validation.Validate(c.Version), 145 }.Filter() 146 } 147 148 // Validate validates GetPropertyRequest 149 func (v GetPropertyRequest) Validate() error { 150 return validation.Errors{ 151 "PropertyID": validation.Validate(v.PropertyID, validation.Required), 152 }.Filter() 153 } 154 155 // Validate validates RemovePropertyRequest 156 func (v RemovePropertyRequest) Validate() error { 157 return validation.Errors{ 158 "PropertyID": validation.Validate(v.PropertyID, validation.Required), 159 }.Filter() 160 } 161 162 var ( 163 // ErrGetProperties represents error when fetching properties fails 164 ErrGetProperties = errors.New("fetching properties") 165 // ErrGetProperty represents error when fetching property fails 166 ErrGetProperty = errors.New("fetching property") 167 // ErrCreateProperty represents error when creating property fails 168 ErrCreateProperty = errors.New("creating property") 169 // ErrRemoveProperty represents error when removing property fails 170 ErrRemoveProperty = errors.New("removing property") 171 ) 172 173 func (p *papi) GetProperties(ctx context.Context, params GetPropertiesRequest) (*GetPropertiesResponse, error) { 174 if err := params.Validate(); err != nil { 175 return nil, fmt.Errorf("%s: %w: %s", ErrGetProperties, ErrStructValidation, err) 176 } 177 178 var rval GetPropertiesResponse 179 180 logger := p.Log(ctx) 181 logger.Debug("GetProperties") 182 183 uri := fmt.Sprintf( 184 "/papi/v1/properties?contractId=%s&groupId=%s", 185 params.ContractID, 186 params.GroupID) 187 188 req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil) 189 if err != nil { 190 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetProperties, err) 191 } 192 193 resp, err := p.Exec(req, &rval) 194 if err != nil { 195 return nil, fmt.Errorf("%w: request failed: %s", ErrGetProperties, err) 196 } 197 198 if resp.StatusCode != http.StatusOK { 199 return nil, fmt.Errorf("%s: %w", ErrGetProperties, p.Error(resp)) 200 } 201 202 return &rval, nil 203 } 204 205 func (p *papi) CreateProperty(ctx context.Context, params CreatePropertyRequest) (*CreatePropertyResponse, error) { 206 if err := params.Validate(); err != nil { 207 return nil, fmt.Errorf("%s: %w:\n%s", ErrCreateProperty, ErrStructValidation, err) 208 } 209 210 logger := p.Log(ctx) 211 logger.Debug("CreateProperty") 212 213 uri := fmt.Sprintf( 214 "/papi/v1/properties?contractId=%s&groupId=%s", 215 params.ContractID, 216 params.GroupID) 217 218 req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, nil) 219 if err != nil { 220 return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreateProperty, err) 221 } 222 223 var rval CreatePropertyResponse 224 225 resp, err := p.Exec(req, &rval, params.Property) 226 if err != nil { 227 return nil, fmt.Errorf("%w: request failed: %s", ErrCreateProperty, err) 228 } 229 230 if resp.StatusCode != http.StatusCreated { 231 return nil, fmt.Errorf("%s: %w", ErrCreateProperty, p.Error(resp)) 232 } 233 234 id, err := ResponseLinkParse(rval.PropertyLink) 235 if err != nil { 236 return nil, fmt.Errorf("%s: %w: %s", ErrCreateProperty, ErrInvalidResponseLink, err) 237 } 238 rval.PropertyID = id 239 240 return &rval, nil 241 } 242 243 func (p *papi) GetProperty(ctx context.Context, params GetPropertyRequest) (*GetPropertyResponse, error) { 244 if err := params.Validate(); err != nil { 245 return nil, fmt.Errorf("%s: %w: %s", ErrGetProperty, ErrStructValidation, err) 246 } 247 248 var rval GetPropertyResponse 249 250 logger := p.Log(ctx) 251 logger.Debug("GetProperty") 252 253 uri, err := url.Parse(fmt.Sprintf( 254 "/papi/v1/properties/%s", 255 params.PropertyID), 256 ) 257 if err != nil { 258 return nil, fmt.Errorf("%w: failed to parse url: %s", ErrGetProperty, err) 259 } 260 q := uri.Query() 261 if params.GroupID != "" { 262 q.Add("groupId", params.GroupID) 263 } 264 if params.ContractID != "" { 265 q.Add("contractId", params.ContractID) 266 } 267 uri.RawQuery = q.Encode() 268 269 req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil) 270 if err != nil { 271 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetProperty, err) 272 } 273 274 resp, err := p.Exec(req, &rval) 275 if err != nil { 276 return nil, fmt.Errorf("%w: request failed: %s", ErrGetProperty, err) 277 } 278 279 if resp.StatusCode != http.StatusOK { 280 return nil, fmt.Errorf("%s: %w", ErrGetProperty, p.Error(resp)) 281 } 282 283 if len(rval.Properties.Items) == 0 { 284 return nil, fmt.Errorf("%s: %w: PropertyID: %s", ErrGetProperty, ErrNotFound, params.PropertyID) 285 } 286 rval.Property = rval.Properties.Items[0] 287 288 return &rval, nil 289 } 290 291 func (p *papi) RemoveProperty(ctx context.Context, params RemovePropertyRequest) (*RemovePropertyResponse, error) { 292 if err := params.Validate(); err != nil { 293 return nil, fmt.Errorf("%s: %w: %s", ErrRemoveProperty, ErrStructValidation, err) 294 } 295 296 var rval RemovePropertyResponse 297 298 logger := p.Log(ctx) 299 logger.Debug("RemoveProperty") 300 301 uri, err := url.Parse(fmt.Sprintf( 302 "/papi/v1/properties/%s", 303 params.PropertyID), 304 ) 305 if err != nil { 306 return nil, fmt.Errorf("%w: failed parse url: %s", ErrRemoveProperty, err) 307 } 308 q := uri.Query() 309 if params.GroupID != "" { 310 q.Add("groupId", params.GroupID) 311 } 312 if params.ContractID != "" { 313 q.Add("contractId", params.ContractID) 314 } 315 uri.RawQuery = q.Encode() 316 317 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri.String(), nil) 318 if err != nil { 319 return nil, fmt.Errorf("%w: failed to create request: %s", ErrRemoveProperty, err) 320 } 321 322 resp, err := p.Exec(req, &rval) 323 if err != nil { 324 return nil, fmt.Errorf("%w: request failed: %s", ErrRemoveProperty, err) 325 } 326 327 if resp.StatusCode != http.StatusOK { 328 return nil, fmt.Errorf("%s: %w", ErrRemoveProperty, p.Error(resp)) 329 } 330 331 return &rval, nil 332 }