1# coding: utf-8
2# Copyright (c) 2016, 2021, Oracle and/or its affiliates.  All rights reserved.
3# This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
4
5from .. import dns, object_storage
6from .. import retry
7from ..response import Response
8
9
10def list_call_get_up_to_limit(list_func_ref, record_limit, page_size, *list_func_args, **list_func_kwargs):
11    """
12    Calls a list operation and automatically fetches more data from the service (automatically following pagination tokens) until
13    the desired number of records is reached (or there are no more records left, if the total number of records is less than
14    the desired number of records). Apart from the formally listed parameters for this function, any additional
15    positional (``*list_func_args``) and keyword (``**list_func_kwargs``) arguments will be passed to the list operation.
16
17    Results are eagerly loaded and the response returned by this function will contain all the data up to the desired
18    number of records (or there are no more records left, whichever is first). If you wish to lazy load results, then use
19    the version of this method which produces a generator: :py:func:`~oci.pagination.list_call_get_up_to_limit_generator`
20
21    :param function list_func_ref:
22        A reference to the list operation which we will call
23
24    :param int record_limit:
25        The maximum number of records to fetch. We may fetch less records than this if the total number of records is less than
26        this value. If a record_limit is not provided then we will only fetch a single page of data
27
28    :param int page_size:
29        The number of records to retrieve per list operation call
30
31    :return:
32        A :class:`~oci.response.Response` whose data attribute contains all the records we retrieved. The other attributes of the
33        :class:`~oci.response.Response` object will be sourced from the last response we received from calling the list operation on the service.
34    :rtype: :class:`~oci.response.Response`
35    """
36    call_result = None
37    aggregated_results = []
38    is_dns_record_collection = False
39    dns_record_collection_class = None
40    is_list_objects_response = False
41    list_objects_prefixes = set()
42    for response in list_call_get_up_to_limit_generator(list_func_ref, record_limit, page_size, 'response', *list_func_args, **list_func_kwargs):
43        call_result = response
44
45        if isinstance(call_result.data, dns.models.RecordCollection) or isinstance(call_result.data, dns.models.RRSet):
46            is_dns_record_collection = True
47            dns_record_collection_class = call_result.data.__class__
48            aggregated_results.extend(call_result.data.items)
49        elif isinstance(call_result.data, object_storage.models.ListObjects):
50            is_list_objects_response = True
51            aggregated_results.extend(call_result.data.objects)
52            if call_result.data.prefixes:
53                list_objects_prefixes.update(call_result.data.prefixes)
54        else:
55            aggregated_results.extend(call_result.data) if isinstance(call_result.data, list) \
56                else aggregated_results.extend(call_result.data.items)
57
58    if is_dns_record_collection:
59        final_response = Response(
60            call_result.status,
61            call_result.headers,
62            dns_record_collection_class(items=aggregated_results),
63            call_result.request
64        )
65    elif is_list_objects_response:
66        final_response = Response(
67            call_result.status,
68            call_result.headers,
69            object_storage.models.ListObjects(
70                objects=aggregated_results,
71                prefixes=list(list_objects_prefixes)
72            ),
73            call_result.request
74        )
75    else:
76        final_response = Response(call_result.status, call_result.headers, aggregated_results, call_result.request)
77    return final_response
78
79
80def list_call_get_up_to_limit_generator(list_func_ref, record_limit, page_size, yield_mode, *list_func_args, **list_func_kwargs):
81    """
82    Calls a list operation and automatically fetches more data from the service (automatically following pagination tokens) until
83    the desired number of records is reached (or there are no more records left, if the total number of records is less than
84    the desired number of records). Apart from the formally listed parameters for this function, any additional
85    positional (``*list_func_args``) and keyword (``**list_func_kwargs``) arguments will be passed to the list operation.
86
87    This function produces a generator and lazily loads results. That is, service calls will only be made to fetch more data
88    when needed as we iterate through results produced by the generator; this contrasts with the eagarily loaded
89    :py:func:`~oci.pagination.list_call_get_up_to_limit` function, which makes all the required service calls when it is called.
90
91    The generator also supports vending two types of objects - either the raw responses received from calling the list operation,
92    or the individual model objects which are contained within the response's ``data`` attribute (which should be
93    a list of model objects).
94
95    :param function list_func_ref:
96        A reference to the list operation which we will call
97
98    :param int record_limit:
99        The maximum number of records to fetch. We may fetch less records than this if the total number of records is less than
100        this value. If a record_limit is not provided then we will only fetch a single page of data
101
102    :param int page_size:
103        The number of records to retrieve per list operation call
104
105    :param str yield_mode:
106        Either ``response`` or ``record``. This will control whether the generator returned by this function yields
107        either :class:`~oci.response.Response` objects (if the value is ``response``), or whether it yields the
108        individual model objects which are contained within the response's ``data`` attribute (which should
109        be a list of model objects)
110
111    :return:
112        A generator that, depending on the ``yield_mode``, will yield either :class:`~oci.response.Response` objects
113        or the individual model objects which are contained within the response's ``data`` attribute (which should
114        be a list of model objects)
115    """
116    if not record_limit and not page_size:
117        raise ValueError('You must provide one, or both, of a record_limit and page_size')
118
119    # If no limit was provided, make a single call
120    if record_limit is None:
121        list_func_kwargs['limit'] = page_size
122        single_call_result = retry.DEFAULT_RETRY_STRATEGY.make_retrying_call(list_func_ref, *list_func_args, **list_func_kwargs)
123
124        if yield_mode == 'response':
125            yield single_call_result
126        else:
127            items_to_yield = []
128            if isinstance(single_call_result.data, dns.models.RecordCollection) or isinstance(single_call_result.data, dns.models.RRSet):
129                items_to_yield = single_call_result.data.items
130            elif isinstance(single_call_result.data, object_storage.models.ListObjects):
131                items_to_yield = single_call_result.data.objects
132            else:
133                items_to_yield = single_call_result.data
134
135            for item in items_to_yield:
136                yield item
137
138        return  # This will terminate after we yield everything we can from the single result
139
140    # If we have a limit, make calls until we get that amount of data
141    keep_paginating = True
142    remaining_items_to_fetch = record_limit
143    call_result = None
144    while keep_paginating and remaining_items_to_fetch > 0:
145        list_func_kwargs['limit'] = min(page_size, remaining_items_to_fetch)
146
147        call_result = retry.DEFAULT_RETRY_STRATEGY.make_retrying_call(list_func_ref, *list_func_args, **list_func_kwargs)
148        if yield_mode == 'response':
149            yield call_result
150        else:
151            items_to_yield = []
152            if isinstance(call_result.data, dns.models.RecordCollection) or isinstance(call_result.data, dns.models.RRSet):
153                items_to_yield = call_result.data.items
154            elif isinstance(call_result.data, object_storage.models.ListObjects):
155                items_to_yield = call_result.data.objects
156            else:
157                items_to_yield = call_result.data if isinstance(call_result.data, list) else call_result.data.items
158
159            for item in items_to_yield:
160                yield item
161
162        if isinstance(call_result.data, dns.models.RecordCollection) or isinstance(call_result.data, dns.models.RRSet):
163            remaining_items_to_fetch -= len(call_result.data.items)
164        elif isinstance(call_result.data, object_storage.models.ListObjects):
165            remaining_items_to_fetch -= len(call_result.data.objects)
166        else:
167            remaining_items_to_fetch -= len(call_result.data) if isinstance(call_result.data, list) else len(call_result.data.items)
168
169        if isinstance(call_result.data, object_storage.models.ListObjects):
170            if call_result.data.next_start_with is not None:
171                list_func_kwargs['start'] = call_result.data.next_start_with
172            keep_paginating = (call_result.data.next_start_with is not None)
173        else:
174            if call_result.next_page is not None:
175                list_func_kwargs['page'] = call_result.next_page
176
177            keep_paginating = call_result.has_next_page
178
179
180def list_call_get_all_results(list_func_ref, *list_func_args, **list_func_kwargs):
181    """
182    Calls a list operation and automatically fetches more data from the service (automatically following pagination tokens) until
183    the no more records are available. Apart from the formally listed parameters for this function, any additional
184    positional (``*list_func_args``) and keyword (``**list_func_kwargs``) arguments will be passed to the list operation.
185
186    Results are eagerly loaded and the response returned by this function will contain all the available data. If you wish
187    to lazy load results, then use the version of this method which produces a generator:
188    :py:func:`~oci.pagination.list_call_get_all_results_generator`
189
190    :param function list_func_ref:
191        A reference to the list operation which we will call
192
193    :return:
194        A :class:`~oci.response.Response` whose data attribute contains all the records we retrieved. The other attributes of the
195        :class:`~oci.response.Response` object will be sourced from the last response we received from calling the list operation on the service.
196    :rtype: :class:`~oci.response.Response`
197    """
198
199    aggregated_results = []
200    call_result = None
201    is_dns_record_collection = False
202    dns_record_collection_class = None
203    is_list_objects_response = False
204    list_objects_prefixes = set()
205    for response in list_call_get_all_results_generator(list_func_ref, 'response', *list_func_args, **list_func_kwargs):
206        call_result = response
207        if isinstance(call_result.data, dns.models.RecordCollection) or isinstance(call_result.data, dns.models.RRSet):
208            is_dns_record_collection = True
209            dns_record_collection_class = call_result.data.__class__
210            aggregated_results.extend(call_result.data.items)
211        elif isinstance(call_result.data, object_storage.models.ListObjects):
212            is_list_objects_response = True
213            aggregated_results.extend(call_result.data.objects)
214            if call_result.data.prefixes:
215                list_objects_prefixes.update(call_result.data.prefixes)
216        else:
217            aggregated_results.extend(call_result.data) if isinstance(call_result.data, list) \
218                else aggregated_results.extend(call_result.data.items)
219
220    if is_dns_record_collection:
221        final_response = Response(
222            call_result.status,
223            call_result.headers,
224            dns_record_collection_class(items=aggregated_results),
225            call_result.request
226        )
227    elif is_list_objects_response:
228        final_response = Response(
229            call_result.status,
230            call_result.headers,
231            object_storage.models.ListObjects(
232                objects=aggregated_results,
233                prefixes=list(list_objects_prefixes)
234            ),
235            call_result.request
236        )
237    else:
238        final_response = Response(call_result.status, call_result.headers, aggregated_results, call_result.request)
239    return final_response
240
241
242def list_call_get_all_results_generator(list_func_ref, yield_mode, *list_func_args, **list_func_kwargs):
243    """
244    Calls a list operation and automatically fetches more data from the service (automatically following pagination tokens) until
245    the no more records are available. Apart from the formally listed parameters for this function, any additional
246    positional (``*list_func_args``) and keyword (``**list_func_kwargs``) arguments will be passed to the list operation.
247
248    This function produces a generator and lazily loads results. That is, service calls will only be made to fetch more data
249    when needed as we iterate through results produced by the generator; this contrasts with the eagarily loaded
250    :py:func:`~oci.pagination.list_call_get_all_results` function, which makes all the required service calls when it is called.
251
252    The generator also supports vending two types of objects - either the raw responses received from calling the list operation,
253    or the individual model objects which are contained within the response's ``data`` attribute (which should be
254    a list of model objects).
255
256    :param function list_func_ref:
257        A reference to the list operation which we will call
258
259    :param str yield_mode:
260        Either ``response`` or ``record``. This will control whether the generator returned by this function yields
261        either :class:`~oci.response.Response` objects (if the value is ``response``), or whether it yields the
262        individual model objects which are contained within the response's ``data`` attribute (which should
263        be a list of model objects)
264
265    :return:
266        A generator that, depending on the ``yield_mode``, will yield either :class:`~oci.response.Response` objects
267        or the individual model objects which are contained within the response's ``data`` attribute (which should
268        be a list of model objects)
269    """
270    keep_paginating = True
271    call_result = None
272
273    while keep_paginating:
274        call_result = retry.DEFAULT_RETRY_STRATEGY.make_retrying_call(list_func_ref, *list_func_args, **list_func_kwargs)
275        if yield_mode == 'response':
276            yield call_result
277        else:
278            items_to_yield = []
279            if isinstance(call_result.data, dns.models.RecordCollection) or isinstance(call_result.data, dns.models.RRSet):
280                items_to_yield = call_result.data.items
281            elif isinstance(call_result.data, object_storage.models.ListObjects):
282                items_to_yield = call_result.data.objects
283            else:
284                items_to_yield = call_result.data
285
286            for item in items_to_yield:
287                yield item
288
289        if isinstance(call_result.data, object_storage.models.ListObjects):
290            if call_result.data.next_start_with is not None:
291                list_func_kwargs['start'] = call_result.data.next_start_with
292            keep_paginating = (call_result.data.next_start_with is not None)
293        else:
294            if call_result.next_page is not None:
295                list_func_kwargs['page'] = call_result.next_page
296
297            keep_paginating = call_result.has_next_page
298