1# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"). You
4# may not use this file except in compliance with the License. A copy of
5# the License is located at
6#
7# https://aws.amazon.com/apache2.0/
8#
9# or in the "license" file accompanying this file. This file is
10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11# ANY KIND, either express or implied. See the License for the specific
12# language governing permissions and limitations under the License.
13from botocore import xform_name
14from botocore.docs.utils import get_official_service_name
15
16from boto3.docs.base import BaseDocumenter
17from boto3.docs.action import ActionDocumenter
18from boto3.docs.waiter import WaiterResourceDocumenter
19from boto3.docs.collection import CollectionDocumenter
20from boto3.docs.subresource import SubResourceDocumenter
21from boto3.docs.attr import document_attribute
22from boto3.docs.attr import document_identifier
23from boto3.docs.attr import document_reference
24from boto3.docs.utils import get_identifier_args_for_signature
25from boto3.docs.utils import get_identifier_values_for_example
26from boto3.docs.utils import get_identifier_description
27from boto3.docs.utils import add_resource_type_overview
28
29
30class ResourceDocumenter(BaseDocumenter):
31    def __init__(self, resource, botocore_session):
32        super(ResourceDocumenter, self).__init__(resource)
33        self._botocore_session = botocore_session
34
35    def document_resource(self, section):
36        self._add_title(section)
37        self._add_intro(section)
38        overview_section = section.add_new_section('member-overview')
39        self._add_identifiers(section)
40        self._add_attributes(section)
41        self._add_references(section)
42        self._add_actions(section)
43        self._add_sub_resources(section)
44        self._add_collections(section)
45        self._add_waiters(section)
46        self._add_overview_of_members(overview_section)
47
48    def _add_title(self, section):
49        section.style.h2(self._resource_name)
50
51    def _add_intro(self, section):
52        identifier_names = []
53        if self._resource_model.identifiers:
54            for identifier in self._resource_model.identifiers:
55                identifier_names.append(identifier.name)
56
57        # Write out the class signature.
58        class_args = get_identifier_args_for_signature(identifier_names)
59        section.style.start_sphinx_py_class(
60            class_name='%s(%s)' % (self.class_name, class_args))
61
62        # Add as short description about the resource
63        description_section = section.add_new_section('description')
64        self._add_description(description_section)
65
66        # Add an example of how to instantiate the resource
67        example_section = section.add_new_section('example')
68        self._add_example(example_section, identifier_names)
69
70        # Add the description for the parameters to instantiate the
71        # resource.
72        param_section = section.add_new_section('params')
73        self._add_params_description(param_section, identifier_names)
74
75    def _add_description(self, section):
76        official_service_name = get_official_service_name(
77            self._service_model)
78        section.write(
79            'A resource representing an %s %s' % (
80                official_service_name, self._resource_name))
81
82    def _add_example(self, section, identifier_names):
83        section.style.start_codeblock()
84        section.style.new_line()
85        section.write('import boto3')
86        section.style.new_line()
87        section.style.new_line()
88        section.write(
89            '%s = boto3.resource(\'%s\')' % (
90                self._service_name, self._service_name)
91        )
92        section.style.new_line()
93        example_values = get_identifier_values_for_example(identifier_names)
94        section.write(
95            '%s = %s.%s(%s)' % (
96                xform_name(self._resource_name), self._service_name,
97                self._resource_name, example_values))
98        section.style.end_codeblock()
99
100    def _add_params_description(self, section, identifier_names):
101        for identifier_name in identifier_names:
102            description = get_identifier_description(
103                self._resource_name, identifier_name)
104            section.write(':type %s: string' % identifier_name)
105            section.style.new_line()
106            section.write(':param %s: %s' % (
107                identifier_name, description))
108            section.style.new_line()
109
110    def _add_overview_of_members(self, section):
111        for resource_member_type in self.member_map:
112            section.style.new_line()
113            section.write('These are the resource\'s available %s:' % (
114                resource_member_type))
115            section.style.new_line()
116            for member in self.member_map[resource_member_type]:
117                if resource_member_type in ['identifiers', 'attributes',
118                                            'references', 'collections']:
119                    section.style.li(':py:attr:`%s`' % member)
120                else:
121                    section.style.li(':py:meth:`%s()`' % member)
122
123    def _add_identifiers(self, section):
124        identifiers = self._resource.meta.resource_model.identifiers
125        section = section.add_new_section('identifiers')
126        member_list = []
127        if identifiers:
128            self.member_map['identifiers'] = member_list
129            add_resource_type_overview(
130                section=section,
131                resource_type='Identifiers',
132                description=(
133                    'Identifiers are properties of a resource that are '
134                    'set upon instantation of the resource.'),
135                intro_link='identifiers_attributes_intro')
136        for identifier in identifiers:
137            identifier_section = section.add_new_section(identifier.name)
138            member_list.append(identifier.name)
139            document_identifier(
140                section=identifier_section,
141                resource_name=self._resource_name,
142                identifier_model=identifier
143            )
144
145    def _add_attributes(self, section):
146        service_model = self._resource.meta.client.meta.service_model
147        attributes = {}
148        if self._resource.meta.resource_model.shape:
149            shape = service_model.shape_for(
150                self._resource.meta.resource_model.shape)
151            attributes = self._resource.meta.resource_model.get_attributes(
152                shape)
153        section = section.add_new_section('attributes')
154        attribute_list = []
155        if attributes:
156            add_resource_type_overview(
157                section=section,
158                resource_type='Attributes',
159                description=(
160                    'Attributes provide access'
161                    ' to the properties of a resource. Attributes are lazy-'
162                    'loaded the first time one is accessed via the'
163                    ' :py:meth:`load` method.'),
164                intro_link='identifiers_attributes_intro')
165            self.member_map['attributes'] = attribute_list
166        for attr_name in sorted(attributes):
167            _, attr_shape = attributes[attr_name]
168            attribute_section = section.add_new_section(attr_name)
169            attribute_list.append(attr_name)
170            document_attribute(
171                section=attribute_section,
172                service_name=self._service_name,
173                resource_name=self._resource_name,
174                attr_name=attr_name,
175                event_emitter=self._resource.meta.client.meta.events,
176                attr_model=attr_shape
177            )
178
179    def _add_references(self, section):
180        section = section.add_new_section('references')
181        references = self._resource.meta.resource_model.references
182        reference_list = []
183        if references:
184            add_resource_type_overview(
185                section=section,
186                resource_type='References',
187                description=(
188                    'References are related resource instances that have '
189                    'a belongs-to relationship.'),
190                intro_link='references_intro')
191            self.member_map['references'] = reference_list
192        for reference in references:
193            reference_section = section.add_new_section(reference.name)
194            reference_list.append(reference.name)
195            document_reference(
196                section=reference_section,
197                reference_model=reference
198            )
199
200    def _add_actions(self, section):
201        section = section.add_new_section('actions')
202        actions = self._resource.meta.resource_model.actions
203        if actions:
204            documenter = ActionDocumenter(self._resource)
205            documenter.member_map = self.member_map
206            documenter.document_actions(section)
207
208    def _add_sub_resources(self, section):
209        section = section.add_new_section('sub-resources')
210        sub_resources = self._resource.meta.resource_model.subresources
211        if sub_resources:
212            documenter = SubResourceDocumenter(self._resource)
213            documenter.member_map = self.member_map
214            documenter.document_sub_resources(section)
215
216    def _add_collections(self, section):
217        section = section.add_new_section('collections')
218        collections = self._resource.meta.resource_model.collections
219        if collections:
220            documenter = CollectionDocumenter(self._resource)
221            documenter.member_map = self.member_map
222            documenter.document_collections(section)
223
224    def _add_waiters(self, section):
225        section = section.add_new_section('waiters')
226        waiters = self._resource.meta.resource_model.waiters
227        if waiters:
228            service_waiter_model = self._botocore_session.get_waiter_model(
229                self._service_name)
230            documenter = WaiterResourceDocumenter(
231                self._resource, service_waiter_model)
232            documenter.member_map = self.member_map
233            documenter.document_resource_waiters(section)
234
235
236class ServiceResourceDocumenter(ResourceDocumenter):
237    @property
238    def class_name(self):
239        return '%s.ServiceResource' % self._service_docs_name
240
241    def _add_title(self, section):
242        section.style.h2('Service Resource')
243
244    def _add_description(self, section):
245        official_service_name = get_official_service_name(
246            self._service_model)
247        section.write(
248            'A resource representing %s' % official_service_name)
249
250    def _add_example(self, section, identifier_names):
251        section.style.start_codeblock()
252        section.style.new_line()
253        section.write('import boto3')
254        section.style.new_line()
255        section.style.new_line()
256        section.write(
257            '%s = boto3.resource(\'%s\')' % (
258                self._service_name, self._service_name))
259        section.style.end_codeblock()
260