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