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