1//go:build go1.16 2// +build go1.16 3 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// Licensed under the MIT License. 6 7package body 8 9import ( 10 "errors" 11 "net/http" 12 13 armpollers "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/pollers" 14 "github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers" 15 "github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared" 16 "github.com/Azure/azure-sdk-for-go/sdk/internal/log" 17) 18 19// Kind is the identifier of this type in a resume token. 20const Kind = "Body" 21 22// Applicable returns true if the LRO is using no headers, just provisioning state. 23// This is only applicable to PATCH and PUT methods and assumes no polling headers. 24func Applicable(resp *http.Response) bool { 25 // we can't check for absense of headers due to some misbehaving services 26 // like redis that return a Location header but don't actually use that protocol 27 return resp.Request.Method == http.MethodPatch || resp.Request.Method == http.MethodPut 28} 29 30// Poller is an LRO poller that uses the Body pattern. 31type Poller struct { 32 // The poller's type, used for resume token processing. 33 Type string `json:"type"` 34 35 // The URL for polling. 36 PollURL string `json:"pollURL"` 37 38 // The LRO's current state. 39 CurState string `json:"state"` 40} 41 42// New creates a new Poller from the provided initial response. 43func New(resp *http.Response, pollerID string) (*Poller, error) { 44 log.Write(log.LongRunningOperation, "Using Body poller.") 45 p := &Poller{ 46 Type: pollers.MakeID(pollerID, Kind), 47 PollURL: resp.Request.URL.String(), 48 } 49 // default initial state to InProgress. depending on the HTTP 50 // status code and provisioning state, we might change the value. 51 curState := pollers.StatusInProgress 52 provState, err := armpollers.GetProvisioningState(resp) 53 if err != nil && !errors.Is(err, shared.ErrNoBody) { 54 return nil, err 55 } 56 if resp.StatusCode == http.StatusCreated && provState != "" { 57 // absense of provisioning state is ok for a 201, means the operation is in progress 58 curState = provState 59 } else if resp.StatusCode == http.StatusOK { 60 if provState != "" { 61 curState = provState 62 } else if provState == "" { 63 // for a 200, absense of provisioning state indicates success 64 curState = pollers.StatusSucceeded 65 } 66 } else if resp.StatusCode == http.StatusNoContent { 67 curState = pollers.StatusSucceeded 68 } 69 p.CurState = curState 70 return p, nil 71} 72 73// URL returns the polling URL. 74func (p *Poller) URL() string { 75 return p.PollURL 76} 77 78// Done returns true if the LRO has reached a terminal state. 79func (p *Poller) Done() bool { 80 return pollers.IsTerminalState(p.Status()) 81} 82 83// Update updates the Poller from the polling response. 84func (p *Poller) Update(resp *http.Response) error { 85 if resp.StatusCode == http.StatusNoContent { 86 p.CurState = pollers.StatusSucceeded 87 return nil 88 } 89 state, err := armpollers.GetProvisioningState(resp) 90 if errors.Is(err, shared.ErrNoBody) { 91 // a missing response body in non-204 case is an error 92 return err 93 } else if state == "" { 94 // a response body without provisioning state is considered terminal success 95 state = pollers.StatusSucceeded 96 } else if err != nil { 97 return err 98 } 99 p.CurState = state 100 return nil 101} 102 103// FinalGetURL returns the empty string as no final GET is required for this poller type. 104func (*Poller) FinalGetURL() string { 105 return "" 106} 107 108// Status returns the status of the LRO. 109func (p *Poller) Status() string { 110 return p.CurState 111} 112