1// +build go1.13
2
3// Copyright (c) Microsoft Corporation. All rights reserved.
4// Licensed under the MIT License.
5
6package loc
7
8import (
9	"errors"
10	"fmt"
11	"net/http"
12
13	"github.com/Azure/azure-sdk-for-go/sdk/armcore/internal/pollers"
14	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
15)
16
17// Applicable returns true if the LRO is using Location.
18func Applicable(resp *azcore.Response) bool {
19	return resp.StatusCode == http.StatusAccepted && resp.Header.Get(pollers.HeaderLocation) != ""
20}
21
22// Poller is an LRO poller that uses the Location pattern.
23type Poller struct {
24	// The poller's type, used for resume token processing.
25	Type string `json:"type"`
26
27	// The URL for polling.
28	PollURL string `json:"pollURL"`
29
30	// The LRO's current state.
31	CurState string `json:"state"`
32}
33
34// New creates a new Poller from the provided initial response.
35func New(resp *azcore.Response, pollerID string) (*Poller, error) {
36	azcore.Log().Write(azcore.LogLongRunningOperation, "Using Location poller.")
37	locURL := resp.Header.Get(pollers.HeaderLocation)
38	if locURL == "" {
39		return nil, errors.New("response is missing Location header")
40	}
41	if !pollers.IsValidURL(locURL) {
42		return nil, fmt.Errorf("invalid polling URL %s", locURL)
43	}
44	p := &Poller{
45		Type:     pollers.MakeID(pollerID, "loc"),
46		PollURL:  locURL,
47		CurState: pollers.StatusInProgress,
48	}
49	return p, nil
50}
51
52// URL returns the polling URL.
53func (p *Poller) URL() string {
54	return p.PollURL
55}
56
57// Done returns true if the LRO has reached a terminal state.
58func (p *Poller) Done() bool {
59	return pollers.IsTerminalState(p.Status())
60}
61
62// Update updates the Poller from the polling response.
63func (p *Poller) Update(resp *azcore.Response) error {
64	// location polling can return an updated polling URL
65	if h := resp.Header.Get(pollers.HeaderLocation); h != "" {
66		p.PollURL = h
67	}
68	if resp.HasStatusCode(http.StatusOK, http.StatusCreated) {
69		// if a 200/201 returns a provisioning state, use that instead
70		state, err := pollers.GetProvisioningState(resp)
71		if err != nil && !errors.Is(err, pollers.ErrNoBody) {
72			return err
73		}
74		if state != "" {
75			p.CurState = state
76		} else {
77			// a 200/201 with no provisioning state indicates success
78			p.CurState = pollers.StatusSucceeded
79		}
80	} else if resp.StatusCode == http.StatusNoContent {
81		p.CurState = pollers.StatusSucceeded
82	} else if resp.StatusCode > 399 && resp.StatusCode < 500 {
83		p.CurState = pollers.StatusFailed
84	}
85	// a 202 falls through, means the LRO is still in progress and we don't check for provisioning state
86	return nil
87}
88
89// FinalGetURL returns the empty string as no final GET is required for this poller type.
90func (p *Poller) FinalGetURL() string {
91	return ""
92}
93
94// Status returns the status of the LRO.
95func (p *Poller) Status() string {
96	return p.CurState
97}
98