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