1package blobs
2
3import (
4	"context"
5	"net/http"
6	"strings"
7
8	"github.com/Azure/go-autorest/autorest"
9	"github.com/Azure/go-autorest/autorest/azure"
10	"github.com/Azure/go-autorest/autorest/validation"
11	"github.com/tombuildsstuff/giovanni/storage/internal/endpoints"
12	"github.com/tombuildsstuff/giovanni/storage/internal/metadata"
13)
14
15type PutPageBlobInput struct {
16	CacheControl       *string
17	ContentDisposition *string
18	ContentEncoding    *string
19	ContentLanguage    *string
20	ContentMD5         *string
21	ContentType        *string
22	LeaseID            *string
23	MetaData           map[string]string
24
25	BlobContentLengthBytes int64
26	BlobSequenceNumber     *int64
27	AccessTier             *AccessTier
28}
29
30// PutPageBlob is a wrapper around the Put API call (with a stricter input object)
31// which creates a new block blob, or updates the content of an existing page blob.
32func (client Client) PutPageBlob(ctx context.Context, accountName, containerName, blobName string, input PutPageBlobInput) (result autorest.Response, err error) {
33	if accountName == "" {
34		return result, validation.NewError("blobs.Client", "PutPageBlob", "`accountName` cannot be an empty string.")
35	}
36	if containerName == "" {
37		return result, validation.NewError("blobs.Client", "PutPageBlob", "`containerName` cannot be an empty string.")
38	}
39	if strings.ToLower(containerName) != containerName {
40		return result, validation.NewError("blobs.Client", "PutPageBlob", "`containerName` must be a lower-cased string.")
41	}
42	if blobName == "" {
43		return result, validation.NewError("blobs.Client", "PutPageBlob", "`blobName` cannot be an empty string.")
44	}
45	if input.BlobContentLengthBytes == 0 || input.BlobContentLengthBytes%512 != 0 {
46		return result, validation.NewError("blobs.Client", "PutPageBlob", "`input.BlobContentLengthBytes` must be aligned to a 512-byte boundary.")
47	}
48
49	req, err := client.PutPageBlobPreparer(ctx, accountName, containerName, blobName, input)
50	if err != nil {
51		err = autorest.NewErrorWithError(err, "blobs.Client", "PutPageBlob", nil, "Failure preparing request")
52		return
53	}
54
55	resp, err := client.PutPageBlobSender(req)
56	if err != nil {
57		result = autorest.Response{Response: resp}
58		err = autorest.NewErrorWithError(err, "blobs.Client", "PutPageBlob", resp, "Failure sending request")
59		return
60	}
61
62	result, err = client.PutPageBlobResponder(resp)
63	if err != nil {
64		err = autorest.NewErrorWithError(err, "blobs.Client", "PutPageBlob", resp, "Failure responding to request")
65		return
66	}
67
68	return
69}
70
71// PutPageBlobPreparer prepares the PutPageBlob request.
72func (client Client) PutPageBlobPreparer(ctx context.Context, accountName, containerName, blobName string, input PutPageBlobInput) (*http.Request, error) {
73	pathParameters := map[string]interface{}{
74		"containerName": autorest.Encode("path", containerName),
75		"blobName":      autorest.Encode("path", blobName),
76	}
77
78	headers := map[string]interface{}{
79		"x-ms-blob-type": string(PageBlob),
80		"x-ms-version":   APIVersion,
81
82		// For a page blob or an page blob, the value of this header must be set to zero,
83		// as Put Blob is used only to initialize the blob
84		"Content-Length": 0,
85
86		// This header specifies the maximum size for the page blob, up to 8 TB.
87		// The page blob size must be aligned to a 512-byte boundary.
88		"x-ms-blob-content-length": input.BlobContentLengthBytes,
89	}
90
91	if input.AccessTier != nil {
92		headers["x-ms-access-tier"] = string(*input.AccessTier)
93	}
94	if input.BlobSequenceNumber != nil {
95		headers["x-ms-blob-sequence-number"] = *input.BlobSequenceNumber
96	}
97
98	if input.CacheControl != nil {
99		headers["x-ms-blob-cache-control"] = *input.CacheControl
100	}
101	if input.ContentDisposition != nil {
102		headers["x-ms-blob-content-disposition"] = *input.ContentDisposition
103	}
104	if input.ContentEncoding != nil {
105		headers["x-ms-blob-content-encoding"] = *input.ContentEncoding
106	}
107	if input.ContentLanguage != nil {
108		headers["x-ms-blob-content-language"] = *input.ContentLanguage
109	}
110	if input.ContentMD5 != nil {
111		headers["x-ms-blob-content-md5"] = *input.ContentMD5
112	}
113	if input.ContentType != nil {
114		headers["x-ms-blob-content-type"] = *input.ContentType
115	}
116	if input.LeaseID != nil {
117		headers["x-ms-lease-id"] = *input.LeaseID
118	}
119
120	headers = metadata.SetIntoHeaders(headers, input.MetaData)
121
122	preparer := autorest.CreatePreparer(
123		autorest.AsPut(),
124		autorest.WithBaseURL(endpoints.GetBlobEndpoint(client.BaseURI, accountName)),
125		autorest.WithPathParameters("/{containerName}/{blobName}", pathParameters),
126		autorest.WithHeaders(headers))
127	return preparer.Prepare((&http.Request{}).WithContext(ctx))
128}
129
130// PutPageBlobSender sends the PutPageBlob request. The method will close the
131// http.Response Body if it receives an error.
132func (client Client) PutPageBlobSender(req *http.Request) (*http.Response, error) {
133	return autorest.SendWithSender(client, req,
134		azure.DoRetryWithRegistration(client.Client))
135}
136
137// PutPageBlobResponder handles the response to the PutPageBlob request. The method always
138// closes the http.Response Body.
139func (client Client) PutPageBlobResponder(resp *http.Response) (result autorest.Response, err error) {
140	err = autorest.Respond(
141		resp,
142		client.ByInspecting(),
143		azure.WithErrorUnlessStatusCode(http.StatusCreated),
144		autorest.ByClosing())
145	result = autorest.Response{Response: resp}
146
147	return
148}
149