1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5#      http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13import uuid
14
15from openstack import resource
16
17
18class Subscription(resource.Resource):
19    # FIXME(anyone): The name string of `location` field of Zaqar API response
20    # is lower case. That is inconsistent with the guide from API-WG. This is
21    # a workaround for this issue.
22    location = resource.Header("location")
23
24    resources_key = 'subscriptions'
25    base_path = '/queues/%(queue_name)s/subscriptions'
26
27    # capabilities
28    allow_create = True
29    allow_list = True
30    allow_fetch = True
31    allow_delete = True
32
33    # Properties
34    #: The value in seconds indicating how long the subscription has existed.
35    age = resource.Body("age")
36    #: Alternate id of the subscription. This key is used in response of
37    #: subscription create API to return id of subscription created.
38    subscription_id = resource.Body("subscription_id", alternate_id=True)
39    #: The extra metadata for the subscription. The value must be a dict.
40    #: If the subscriber is `mailto`. The options can contain `from` and
41    #: `subject` to indicate the email's author and title.
42    options = resource.Body("options", type=dict)
43    #: The queue name which the subscription is registered on.
44    source = resource.Body("source")
45    #: The destination of the message. Two kinds of subscribers are supported:
46    #: http/https and email. The http/https subscriber should start with
47    #: `http/https`. The email subscriber should start with `mailto`.
48    subscriber = resource.Body("subscriber")
49    #: Number of seconds the subscription remains alive? The ttl value must
50    #: be great than 60 seconds. The default value is 3600 seconds.
51    ttl = resource.Body("ttl")
52    #: The queue name which the subscription is registered on.
53    queue_name = resource.URI("queue_name")
54    #: The ID to identify the client accessing Zaqar API. Must be specified
55    #: in header for each API request.
56    client_id = resource.Header("Client-ID")
57    #: The ID to identify the project. Must be provided when keystone
58    #: authentication is not enabled in Zaqar service.
59    project_id = resource.Header("X-PROJECT-ID")
60
61    def create(self, session, prepend_key=True, base_path=None):
62        request = self._prepare_request(requires_id=False,
63                                        prepend_key=prepend_key,
64                                        base_path=base_path)
65        headers = {
66            "Client-ID": self.client_id or str(uuid.uuid4()),
67            "X-PROJECT-ID": self.project_id or session.get_project_id()
68        }
69        request.headers.update(headers)
70        response = session.post(request.url,
71                                json=request.body, headers=request.headers)
72
73        self._translate_response(response)
74        return self
75
76    @classmethod
77    def list(cls, session, paginated=True, base_path=None, **params):
78        """This method is a generator which yields subscription objects.
79
80        This is almost the copy of list method of resource.Resource class.
81        The only difference is the request header now includes `Client-ID`
82        and `X-PROJECT-ID` fields which are required by Zaqar v2 API.
83        """
84        more_data = True
85
86        if base_path is None:
87            base_path = cls.base_path
88
89        uri = base_path % params
90        headers = {
91            "Client-ID": params.get('client_id', None) or str(uuid.uuid4()),
92            "X-PROJECT-ID": params.get('project_id', None
93                                       ) or session.get_project_id()
94        }
95
96        query_params = cls._query_mapping._transpose(params, cls)
97        while more_data:
98            resp = session.get(uri,
99                               headers=headers, params=query_params)
100            resp = resp.json()
101            resp = resp[cls.resources_key]
102
103            if not resp:
104                more_data = False
105
106            yielded = 0
107            new_marker = None
108            for data in resp:
109                value = cls.existing(**data)
110                new_marker = value.id
111                yielded += 1
112                yield value
113
114            if not paginated:
115                return
116            if "limit" in query_params and yielded < query_params["limit"]:
117                return
118            query_params["limit"] = yielded
119            query_params["marker"] = new_marker
120
121    def fetch(self, session, requires_id=True,
122              base_path=None, error_message=None):
123        request = self._prepare_request(requires_id=requires_id,
124                                        base_path=base_path)
125        headers = {
126            "Client-ID": self.client_id or str(uuid.uuid4()),
127            "X-PROJECT-ID": self.project_id or session.get_project_id()
128        }
129
130        request.headers.update(headers)
131        response = session.get(request.url,
132                               headers=request.headers)
133        self._translate_response(response)
134
135        return self
136
137    def delete(self, session):
138        request = self._prepare_request()
139        headers = {
140            "Client-ID": self.client_id or str(uuid.uuid4()),
141            "X-PROJECT-ID": self.project_id or session.get_project_id()
142        }
143
144        request.headers.update(headers)
145        response = session.delete(request.url,
146                                  headers=request.headers)
147
148        self._translate_response(response, has_body=False)
149        return self
150