1# pylint: disable=too-many-lines
2# -------------------------------------------------------------------------
3# Copyright (c) Microsoft Corporation. All rights reserved.
4# Licensed under the MIT License. See License.txt in the project root for
5# license information.
6# --------------------------------------------------------------------------
7
8from azure.core.paging import PageIterator, ItemPaged
9from azure.core.exceptions import HttpResponseError
10from ._deserialize import get_blob_properties_from_generated_code, parse_tags
11from ._generated.models import BlobItemInternal, BlobPrefix as GenBlobPrefix, FilterBlobItem
12from ._models import BlobProperties, FilteredBlob
13from ._shared.models import DictMixin
14from ._shared.response_handlers import return_context_and_deserialized, process_storage_error
15
16
17class BlobPropertiesPaged(PageIterator):
18    """An Iterable of Blob properties.
19
20    :ivar str service_endpoint: The service URL.
21    :ivar str prefix: A blob name prefix being used to filter the list.
22    :ivar str marker: The continuation token of the current page of results.
23    :ivar int results_per_page: The maximum number of results retrieved per API call.
24    :ivar str continuation_token: The continuation token to retrieve the next page of results.
25    :ivar str location_mode: The location mode being used to list results. The available
26        options include "primary" and "secondary".
27    :ivar current_page: The current page of listed results.
28    :vartype current_page: list(~azure.storage.blob.BlobProperties)
29    :ivar str container: The container that the blobs are listed from.
30    :ivar str delimiter: A delimiting character used for hierarchy listing.
31
32    :param callable command: Function to retrieve the next page of items.
33    :param str container: The name of the container.
34    :param str prefix: Filters the results to return only blobs whose names
35        begin with the specified prefix.
36    :param int results_per_page: The maximum number of blobs to retrieve per
37        call.
38    :param str continuation_token: An opaque continuation token.
39    :param str delimiter:
40        Used to capture blobs whose names begin with the same substring up to
41        the appearance of the delimiter character. The delimiter may be a single
42        character or a string.
43    :param location_mode: Specifies the location the request should be sent to.
44        This mode only applies for RA-GRS accounts which allow secondary read access.
45        Options include 'primary' or 'secondary'.
46    """
47    def __init__(
48            self, command,
49            container=None,
50            prefix=None,
51            results_per_page=None,
52            continuation_token=None,
53            delimiter=None,
54            location_mode=None):
55        super(BlobPropertiesPaged, self).__init__(
56            get_next=self._get_next_cb,
57            extract_data=self._extract_data_cb,
58            continuation_token=continuation_token or ""
59        )
60        self._command = command
61        self.service_endpoint = None
62        self.prefix = prefix
63        self.marker = None
64        self.results_per_page = results_per_page
65        self.container = container
66        self.delimiter = delimiter
67        self.current_page = None
68        self.location_mode = location_mode
69
70    def _get_next_cb(self, continuation_token):
71        try:
72            return self._command(
73                prefix=self.prefix,
74                marker=continuation_token or None,
75                maxresults=self.results_per_page,
76                cls=return_context_and_deserialized,
77                use_location=self.location_mode)
78        except HttpResponseError as error:
79            process_storage_error(error)
80
81    def _extract_data_cb(self, get_next_return):
82        self.location_mode, self._response = get_next_return
83        self.service_endpoint = self._response.service_endpoint
84        self.prefix = self._response.prefix
85        self.marker = self._response.marker
86        self.results_per_page = self._response.max_results
87        self.container = self._response.container_name
88        self.current_page = [self._build_item(item) for item in self._response.segment.blob_items]
89
90        return self._response.next_marker or None, self.current_page
91
92    def _build_item(self, item):
93        if isinstance(item, BlobProperties):
94            return item
95        if isinstance(item, BlobItemInternal):
96            blob = get_blob_properties_from_generated_code(item)  # pylint: disable=protected-access
97            blob.container = self.container
98            return blob
99        return item
100
101
102class BlobPrefixPaged(BlobPropertiesPaged):
103    def __init__(self, *args, **kwargs):
104        super(BlobPrefixPaged, self).__init__(*args, **kwargs)
105        self.name = self.prefix
106
107    def _extract_data_cb(self, get_next_return):
108        continuation_token, _ = super(BlobPrefixPaged, self)._extract_data_cb(get_next_return)
109        self.current_page = self._response.segment.blob_prefixes + self._response.segment.blob_items
110        self.current_page = [self._build_item(item) for item in self.current_page]
111        self.delimiter = self._response.delimiter
112
113        return continuation_token, self.current_page
114
115    def _build_item(self, item):
116        item = super(BlobPrefixPaged, self)._build_item(item)
117        if isinstance(item, GenBlobPrefix):
118            return BlobPrefix(
119                self._command,
120                container=self.container,
121                prefix=item.name,
122                results_per_page=self.results_per_page,
123                location_mode=self.location_mode)
124        return item
125
126
127class BlobPrefix(ItemPaged, DictMixin):
128    """An Iterable of Blob properties.
129
130    Returned from walk_blobs when a delimiter is used.
131    Can be thought of as a virtual blob directory.
132
133    :ivar str name: The prefix, or "directory name" of the blob.
134    :ivar str service_endpoint: The service URL.
135    :ivar str prefix: A blob name prefix being used to filter the list.
136    :ivar str marker: The continuation token of the current page of results.
137    :ivar int results_per_page: The maximum number of results retrieved per API call.
138    :ivar str next_marker: The continuation token to retrieve the next page of results.
139    :ivar str location_mode: The location mode being used to list results. The available
140        options include "primary" and "secondary".
141    :ivar current_page: The current page of listed results.
142    :vartype current_page: list(~azure.storage.blob.BlobProperties)
143    :ivar str container: The container that the blobs are listed from.
144    :ivar str delimiter: A delimiting character used for hierarchy listing.
145
146    :param callable command: Function to retrieve the next page of items.
147    :param str prefix: Filters the results to return only blobs whose names
148        begin with the specified prefix.
149    :param int results_per_page: The maximum number of blobs to retrieve per
150        call.
151    :param str marker: An opaque continuation token.
152    :param str delimiter:
153        Used to capture blobs whose names begin with the same substring up to
154        the appearance of the delimiter character. The delimiter may be a single
155        character or a string.
156    :param location_mode: Specifies the location the request should be sent to.
157        This mode only applies for RA-GRS accounts which allow secondary read access.
158        Options include 'primary' or 'secondary'.
159    """
160    def __init__(self, *args, **kwargs):
161        super(BlobPrefix, self).__init__(*args, page_iterator_class=BlobPrefixPaged, **kwargs)
162        self.name = kwargs.get('prefix')
163        self.prefix = kwargs.get('prefix')
164        self.results_per_page = kwargs.get('results_per_page')
165        self.container = kwargs.get('container')
166        self.delimiter = kwargs.get('delimiter')
167        self.location_mode = kwargs.get('location_mode')
168
169
170class FilteredBlobPaged(PageIterator):
171    """An Iterable of Blob properties.
172
173    :ivar str service_endpoint: The service URL.
174    :ivar str prefix: A blob name prefix being used to filter the list.
175    :ivar str marker: The continuation token of the current page of results.
176    :ivar int results_per_page: The maximum number of results retrieved per API call.
177    :ivar str continuation_token: The continuation token to retrieve the next page of results.
178    :ivar str location_mode: The location mode being used to list results. The available
179        options include "primary" and "secondary".
180    :ivar current_page: The current page of listed results.
181    :vartype current_page: list(~azure.storage.blob.FilteredBlob)
182    :ivar str container: The container that the blobs are listed from.
183
184    :param callable command: Function to retrieve the next page of items.
185    :param str container: The name of the container.
186    :param int results_per_page: The maximum number of blobs to retrieve per
187        call.
188    :param str continuation_token: An opaque continuation token.
189    :param location_mode: Specifies the location the request should be sent to.
190        This mode only applies for RA-GRS accounts which allow secondary read access.
191        Options include 'primary' or 'secondary'.
192    """
193    def __init__(
194            self, command,
195            container=None,
196            results_per_page=None,
197            continuation_token=None,
198            location_mode=None):
199        super(FilteredBlobPaged, self).__init__(
200            get_next=self._get_next_cb,
201            extract_data=self._extract_data_cb,
202            continuation_token=continuation_token or ""
203        )
204        self._command = command
205        self.service_endpoint = None
206        self.marker = continuation_token
207        self.results_per_page = results_per_page
208        self.container = container
209        self.current_page = None
210        self.location_mode = location_mode
211
212    def _get_next_cb(self, continuation_token):
213        try:
214            return self._command(
215                marker=continuation_token or None,
216                maxresults=self.results_per_page,
217                cls=return_context_and_deserialized,
218                use_location=self.location_mode)
219        except HttpResponseError as error:
220            process_storage_error(error)
221
222    def _extract_data_cb(self, get_next_return):
223        self.location_mode, self._response = get_next_return
224        self.service_endpoint = self._response.service_endpoint
225        self.marker = self._response.next_marker
226        self.current_page = [self._build_item(item) for item in self._response.blobs]
227
228        return self._response.next_marker or None, self.current_page
229
230    @staticmethod
231    def _build_item(item):
232        if isinstance(item, FilterBlobItem):
233            tags = parse_tags(item.tags)
234            blob = FilteredBlob(name=item.name, container_name=item.container_name, tags=tags)
235            return blob
236        return item
237