1package azblob
2
3import (
4	"context"
5	"net/url"
6
7	"github.com/Azure/azure-pipeline-go/pipeline"
8)
9
10// A BlobURL represents a URL to an Azure Storage blob; the blob may be a block blob, append blob, or page blob.
11type BlobURL struct {
12	blobClient blobClient
13}
14
15// NewBlobURL creates a BlobURL object using the specified URL and request policy pipeline.
16func NewBlobURL(url url.URL, p pipeline.Pipeline) BlobURL {
17	blobClient := newBlobClient(url, p)
18	return BlobURL{blobClient: blobClient}
19}
20
21// URL returns the URL endpoint used by the BlobURL object.
22func (b BlobURL) URL() url.URL {
23	return b.blobClient.URL()
24}
25
26// String returns the URL as a string.
27func (b BlobURL) String() string {
28	u := b.URL()
29	return u.String()
30}
31
32// WithPipeline creates a new BlobURL object identical to the source but with the specified request policy pipeline.
33func (b BlobURL) WithPipeline(p pipeline.Pipeline) BlobURL {
34	return NewBlobURL(b.blobClient.URL(), p)
35}
36
37// WithSnapshot creates a new BlobURL object identical to the source but with the specified snapshot timestamp.
38// Pass "" to remove the snapshot returning a URL to the base blob.
39func (b BlobURL) WithSnapshot(snapshot string) BlobURL {
40	p := NewBlobURLParts(b.URL())
41	p.Snapshot = snapshot
42	return NewBlobURL(p.URL(), b.blobClient.Pipeline())
43}
44
45// ToAppendBlobURL creates an AppendBlobURL using the source's URL and pipeline.
46func (b BlobURL) ToAppendBlobURL() AppendBlobURL {
47	return NewAppendBlobURL(b.URL(), b.blobClient.Pipeline())
48}
49
50// ToBlockBlobURL creates a BlockBlobURL using the source's URL and pipeline.
51func (b BlobURL) ToBlockBlobURL() BlockBlobURL {
52	return NewBlockBlobURL(b.URL(), b.blobClient.Pipeline())
53}
54
55// ToPageBlobURL creates a PageBlobURL using the source's URL and pipeline.
56func (b BlobURL) ToPageBlobURL() PageBlobURL {
57	return NewPageBlobURL(b.URL(), b.blobClient.Pipeline())
58}
59
60// DownloadBlob reads a range of bytes from a blob. The response also includes the blob's properties and metadata.
61// Passing azblob.CountToEnd (0) for count will download the blob from the offset to the end.
62// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-blob.
63func (b BlobURL) Download(ctx context.Context, offset int64, count int64, ac BlobAccessConditions, rangeGetContentMD5 bool) (*DownloadResponse, error) {
64	var xRangeGetContentMD5 *bool
65	if rangeGetContentMD5 {
66		xRangeGetContentMD5 = &rangeGetContentMD5
67	}
68	ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
69	dr, err := b.blobClient.Download(ctx, nil, nil,
70		httpRange{offset: offset, count: count}.pointers(),
71		ac.LeaseAccessConditions.pointers(), xRangeGetContentMD5,
72		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
73	if err != nil {
74		return nil, err
75	}
76	return &DownloadResponse{
77		b:       b,
78		r:       dr,
79		ctx:     ctx,
80		getInfo: HTTPGetterInfo{Offset: offset, Count: count, ETag: dr.ETag()},
81	}, err
82}
83
84// DeleteBlob marks the specified blob or snapshot for deletion. The blob is later deleted during garbage collection.
85// Note that deleting a blob also deletes all its snapshots.
86// For more information, see https://docs.microsoft.com/rest/api/storageservices/delete-blob.
87func (b BlobURL) Delete(ctx context.Context, deleteOptions DeleteSnapshotsOptionType, ac BlobAccessConditions) (*BlobDeleteResponse, error) {
88	ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
89	return b.blobClient.Delete(ctx, nil, nil, ac.LeaseAccessConditions.pointers(), deleteOptions,
90		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
91}
92
93// Undelete restores the contents and metadata of a soft-deleted blob and any associated soft-deleted snapshots.
94// For more information, see https://docs.microsoft.com/rest/api/storageservices/undelete-blob.
95func (b BlobURL) Undelete(ctx context.Context) (*BlobUndeleteResponse, error) {
96	return b.blobClient.Undelete(ctx, nil, nil)
97}
98
99// SetTier operation sets the tier on a blob. The operation is allowed on a page
100// blob in a premium storage account and on a block blob in a blob storage account (locally
101// redundant storage only). A premium page blob's tier determines the allowed size, IOPS, and
102// bandwidth of the blob. A block blob's tier determines Hot/Cool/Archive storage type. This operation
103// does not update the blob's ETag.
104// For detailed information about block blob level tiering see https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-storage-tiers.
105func (b BlobURL) SetTier(ctx context.Context, tier AccessTierType, lac LeaseAccessConditions) (*BlobSetTierResponse, error) {
106	return b.blobClient.SetTier(ctx, tier, nil, nil, lac.pointers())
107}
108
109// GetBlobProperties returns the blob's properties.
110// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-blob-properties.
111func (b BlobURL) GetProperties(ctx context.Context, ac BlobAccessConditions) (*BlobGetPropertiesResponse, error) {
112	ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
113	return b.blobClient.GetProperties(ctx, nil, nil, ac.LeaseAccessConditions.pointers(),
114		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
115}
116
117// SetBlobHTTPHeaders changes a blob's HTTP headers.
118// For more information, see https://docs.microsoft.com/rest/api/storageservices/set-blob-properties.
119func (b BlobURL) SetHTTPHeaders(ctx context.Context, h BlobHTTPHeaders, ac BlobAccessConditions) (*BlobSetHTTPHeadersResponse, error) {
120	ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
121	return b.blobClient.SetHTTPHeaders(ctx, nil,
122		&h.CacheControl, &h.ContentType, h.ContentMD5, &h.ContentEncoding, &h.ContentLanguage,
123		ac.LeaseAccessConditions.pointers(), ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag,
124		&h.ContentDisposition, nil)
125}
126
127// SetBlobMetadata changes a blob's metadata.
128// https://docs.microsoft.com/rest/api/storageservices/set-blob-metadata.
129func (b BlobURL) SetMetadata(ctx context.Context, metadata Metadata, ac BlobAccessConditions) (*BlobSetMetadataResponse, error) {
130	ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
131	return b.blobClient.SetMetadata(ctx, nil, metadata, ac.LeaseAccessConditions.pointers(),
132		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
133}
134
135// CreateSnapshot creates a read-only snapshot of a blob.
136// For more information, see https://docs.microsoft.com/rest/api/storageservices/snapshot-blob.
137func (b BlobURL) CreateSnapshot(ctx context.Context, metadata Metadata, ac BlobAccessConditions) (*BlobCreateSnapshotResponse, error) {
138	// CreateSnapshot does NOT panic if the user tries to create a snapshot using a URL that already has a snapshot query parameter
139	// because checking this would be a performance hit for a VERY unusual path and I don't think the common case should suffer this
140	// performance hit.
141	ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers()
142	return b.blobClient.CreateSnapshot(ctx, nil, metadata, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, ac.LeaseAccessConditions.pointers(), nil)
143}
144
145// AcquireLease acquires a lease on the blob for write and delete operations. The lease duration must be between
146// 15 to 60 seconds, or infinite (-1).
147// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-blob.
148func (b BlobURL) AcquireLease(ctx context.Context, proposedID string, duration int32, ac ModifiedAccessConditions) (*BlobAcquireLeaseResponse, error) {
149	ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.pointers()
150	return b.blobClient.AcquireLease(ctx, nil, &duration, &proposedID,
151		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
152}
153
154// RenewLease renews the blob's previously-acquired lease.
155// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-blob.
156func (b BlobURL) RenewLease(ctx context.Context, leaseID string, ac ModifiedAccessConditions) (*BlobRenewLeaseResponse, error) {
157	ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.pointers()
158	return b.blobClient.RenewLease(ctx, leaseID, nil,
159		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
160}
161
162// ReleaseLease releases the blob's previously-acquired lease.
163// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-blob.
164func (b BlobURL) ReleaseLease(ctx context.Context, leaseID string, ac ModifiedAccessConditions) (*BlobReleaseLeaseResponse, error) {
165	ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.pointers()
166	return b.blobClient.ReleaseLease(ctx, leaseID, nil,
167		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
168}
169
170// BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1)
171// constant to break a fixed-duration lease when it expires or an infinite lease immediately.
172// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-blob.
173func (b BlobURL) BreakLease(ctx context.Context, breakPeriodInSeconds int32, ac ModifiedAccessConditions) (*BlobBreakLeaseResponse, error) {
174	ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.pointers()
175	return b.blobClient.BreakLease(ctx, nil, leasePeriodPointer(breakPeriodInSeconds),
176		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
177}
178
179// ChangeLease changes the blob's lease ID.
180// For more information, see https://docs.microsoft.com/rest/api/storageservices/lease-blob.
181func (b BlobURL) ChangeLease(ctx context.Context, leaseID string, proposedID string, ac ModifiedAccessConditions) (*BlobChangeLeaseResponse, error) {
182	ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.pointers()
183	return b.blobClient.ChangeLease(ctx, leaseID, proposedID,
184		nil, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
185}
186
187// LeaseBreakNaturally tells ContainerURL's or BlobURL's BreakLease method to break the lease using service semantics.
188const LeaseBreakNaturally = -1
189
190func leasePeriodPointer(period int32) (p *int32) {
191	if period != LeaseBreakNaturally {
192		p = &period
193	}
194	return nil
195}
196
197// StartCopyFromURL copies the data at the source URL to a blob.
198// For more information, see https://docs.microsoft.com/rest/api/storageservices/copy-blob.
199func (b BlobURL) StartCopyFromURL(ctx context.Context, source url.URL, metadata Metadata, srcac ModifiedAccessConditions, dstac BlobAccessConditions) (*BlobStartCopyFromURLResponse, error) {
200	srcIfModifiedSince, srcIfUnmodifiedSince, srcIfMatchETag, srcIfNoneMatchETag := srcac.pointers()
201	dstIfModifiedSince, dstIfUnmodifiedSince, dstIfMatchETag, dstIfNoneMatchETag := dstac.ModifiedAccessConditions.pointers()
202	dstLeaseID := dstac.LeaseAccessConditions.pointers()
203
204	return b.blobClient.StartCopyFromURL(ctx, source.String(), nil, metadata,
205		srcIfModifiedSince, srcIfUnmodifiedSince,
206		srcIfMatchETag, srcIfNoneMatchETag,
207		dstIfModifiedSince, dstIfUnmodifiedSince,
208		dstIfMatchETag, dstIfNoneMatchETag,
209		dstLeaseID, nil)
210}
211
212// AbortCopyFromURL stops a pending copy that was previously started and leaves a destination blob with 0 length and metadata.
213// For more information, see https://docs.microsoft.com/rest/api/storageservices/abort-copy-blob.
214func (b BlobURL) AbortCopyFromURL(ctx context.Context, copyID string, ac LeaseAccessConditions) (*BlobAbortCopyFromURLResponse, error) {
215	return b.blobClient.AbortCopyFromURL(ctx, copyID, nil, ac.pointers(), nil)
216}
217