Skip to content

Commit 246b2e7

Browse files
authored
Merge pull request #835 from tomkralidis/ogcapi-transactions
implement OGC API - Features - Part 4
2 parents 08c41d1 + d348f9a commit 246b2e7

4 files changed

Lines changed: 123 additions & 24 deletions

File tree

owslib/ogcapi/__init__.py

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# =============================================================================
2-
# Copyright (c) 2020 Tom Kralidis
2+
# Copyright (c) 2022 Tom Kralidis
33
#
44
# Author: Tom Kralidis <tomkralidis@gmail.com>
55
#
@@ -15,7 +15,8 @@
1515
import yaml
1616

1717
from owslib import __version__
18-
from owslib.util import Authentication, http_get, http_post
18+
from owslib.util import (Authentication, http_delete, http_get, http_post,
19+
http_put)
1920

2021
LOGGER = logging.getLogger(__name__)
2122

@@ -114,7 +115,7 @@ def conformance(self) -> dict:
114115
"""
115116

116117
path = 'conformance'
117-
return self._request(path)
118+
return self._request(path=path)
118119

119120
def _build_url(self, path: str = None, params: dict = {}) -> str:
120121
"""
@@ -141,15 +142,20 @@ def _build_url(self, path: str = None, params: dict = {}) -> str:
141142

142143
return url
143144

144-
def _request(self, path: str = None, as_dict: bool = True,
145+
def _request(self, method: str = 'GET', path: str = None,
146+
data: str = None, as_dict: bool = True,
145147
kwargs: dict = {}) -> dict:
146148
"""
147149
helper function for request/response patterns against OGC API endpoints
148150
149151
@type path: string
150152
@param path: path of request
153+
@type method: string
154+
@param method: HTTP method (default ``GET``)
155+
@type data: string
156+
@param data: request data payload
151157
@type as_dict: bool
152-
@param as_dict: whether to return JSON dict (default True)
158+
@param as_dict: whether to return JSON dict (default ``True``)
153159
@type kwargs: string
154160
@param kwargs: ``dict`` of keyword value pair request parameters
155161
@@ -159,28 +165,35 @@ def _request(self, path: str = None, as_dict: bool = True,
159165
url = self._build_url(path)
160166
self.request = url
161167

168+
LOGGER.debug(f'Method: {method}')
162169
LOGGER.debug(f'Request: {url}')
170+
LOGGER.debug(f'Data: {data}')
163171
LOGGER.debug(f'Params: {kwargs}')
164172

165-
if 'cql' not in kwargs:
173+
if method == 'GET':
166174
response = http_get(url, headers=self.headers, auth=self.auth,
167175
params=kwargs)
168-
else:
169-
LOGGER.debug('CQL query detected')
170-
kwargs2 = deepcopy(kwargs)
171-
cql = kwargs2.pop('cql')
172-
url2 = self._build_url(path, kwargs2)
173-
response = http_post(url2, request=cql, auth=self.auth)
176+
elif method == 'POST':
177+
response = http_post(url, request=data, auth=self.auth)
178+
elif method == 'PUT':
179+
response = http_put(url, data=data, auth=self.auth)
180+
elif method == 'DELETE':
181+
response = http_delete(url, auth=self.auth)
174182

175183
LOGGER.debug(f'URL: {response.url}')
184+
LOGGER.debug(f'Response status code: {response.status_code}')
176185

177-
if response.status_code != requests.codes.ok:
186+
if not response:
178187
raise RuntimeError(response.text)
179188

180189
self.request = response.url
181190

182191
if as_dict:
183-
return response.json()
192+
if len(response.content) == 0:
193+
LOGGER.debug('Empty response')
194+
return {}
195+
else:
196+
return response.json()
184197
else:
185198
return response.content
186199

@@ -199,7 +212,7 @@ def collections(self) -> dict:
199212
"""
200213

201214
path = 'collections'
202-
return self._request(path)
215+
return self._request(path=path)
203216

204217
def collection(self, collection_id: str) -> dict:
205218
"""
@@ -212,7 +225,7 @@ def collection(self, collection_id: str) -> dict:
212225
"""
213226

214227
path = f'collections/{collection_id}'
215-
return self._request(path)
228+
return self._request(path=path)
216229

217230
def collection_queryables(self, collection_id: str) -> dict:
218231
"""
@@ -225,4 +238,4 @@ def collection_queryables(self, collection_id: str) -> dict:
225238
"""
226239

227240
path = f'collections/{collection_id}/queryables'
228-
return self._request(path)
241+
return self._request(path=path)

owslib/ogcapi/features.py

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
# =============================================================================
2-
# Copyright (c) 2020 Tom Kralidis
2+
# Copyright (c) 2022 Tom Kralidis
33
#
44
# Author: Tom Kralidis <tomkralidis@gmail.com>
55
#
66
# Contact email: tomkralidis@gmail.com
77
# =============================================================================
88

9+
from copy import deepcopy
910
import logging
11+
from urllib.parse import urlencode
1012

1113
from owslib.ogcapi import Collections
1214
from owslib.util import Authentication
@@ -65,8 +67,15 @@ def collection_items(self, collection_id: str, **kwargs: dict) -> dict:
6567
if 'bbox' in kwargs:
6668
kwargs['bbox'] = ','.join(list(map(str, kwargs['bbox'])))
6769

68-
path = f'collections/{collection_id}/items'
69-
return self._request(path=path, kwargs=kwargs)
70+
if 'cql' in kwargs:
71+
LOGGER.debug('CQL query detected')
72+
kwargs2 = deepcopy(kwargs)
73+
cql = kwargs2.pop('cql')
74+
path = f'collections/{collection_id}/items?{urlencode(kwargs2)}'
75+
return self._request(method='POST', path=path, data=cql, kwargs=kwargs2)
76+
else:
77+
path = f'collections/{collection_id}/items'
78+
return self._request(path=path, kwargs=kwargs)
7079

7180
def collection_item(self, collection_id: str, identifier: str) -> dict:
7281
"""
@@ -82,3 +91,57 @@ def collection_item(self, collection_id: str, identifier: str) -> dict:
8291

8392
path = f'collections/{collection_id}/items/{identifier}'
8493
return self._request(path=path)
94+
95+
def collection_item_create(self, collection_id: str, data: str) -> bool:
96+
"""
97+
implements POST /collections/{collectionId}/items
98+
99+
@type collection_id: string
100+
@param collection_id: id of collection
101+
@type data: string
102+
@param data: raw representation of data
103+
104+
@returns: single feature result
105+
"""
106+
107+
path = f'collections/{collection_id}/items'
108+
_ = self._request(method='POST', path=path, data=data)
109+
110+
return True
111+
112+
def collection_item_update(self, collection_id: str, identifier: str,
113+
data: str) -> bool:
114+
"""
115+
implements PUT /collections/{collectionId}/items/{featureId}
116+
117+
@type collection_id: string
118+
@param collection_id: id of collection
119+
@type identifier: string
120+
@param identifier: feature identifier
121+
@type data: string
122+
@param data: raw representation of data
123+
124+
@returns: ``bool`` of deletion result
125+
"""
126+
127+
path = f'collections/{collection_id}/items/{identifier}'
128+
_ = self._request(method='PUT', path=path, data=data)
129+
130+
return True
131+
132+
def collection_item_delete(self, collection_id: str, identifier: str) -> bool:
133+
"""
134+
implements DELETE /collections/{collectionId}/items/{featureId}
135+
136+
@type collection_id: string
137+
@param collection_id: id of collection
138+
@type identifier: string
139+
@param identifier: feature identifier
140+
141+
@returns: ``bool`` of deletion result
142+
"""
143+
144+
path = f'collections/{collection_id}/items/{identifier}'
145+
_ = self._request(method='DELETE', path=path)
146+
147+
return True

owslib/ogcapi/processes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def processes(self) -> dict:
3030
"""
3131

3232
path = 'processes'
33-
return self._request(path)
33+
return self._request(path=path)
3434

3535
def process(self, process_id: str) -> dict:
3636
"""
@@ -43,4 +43,4 @@ def process(self, process_id: str) -> dict:
4343
"""
4444

4545
path = f'processes/{process_id}'
46-
return self._request(path)
46+
return self._request(path=path)

owslib/util.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: ISO-8859-15 -*-
22
# =============================================================================
3-
# Copyright (c) 2008 Tom Kralidis
3+
# Copyright (c) 2022 Tom Kralidis
44
#
55
# Authors : Tom Kralidis <tomkralidis@gmail.com>
66
#
@@ -444,7 +444,7 @@ def http_post(url=None, request=None, lang='en-US', timeout=10, username=None, p
444444
return requests.post(url, json=request, headers=headers_, **rkwargs)
445445

446446

447-
def http_get(*args, **kwargs):
447+
def http_prepare(*args, **kwargs):
448448
# Copy input kwargs so the dict can be modified
449449
rkwargs = copy.deepcopy(kwargs)
450450

@@ -475,9 +475,32 @@ def http_get(*args, **kwargs):
475475
rkwargs.setdefault('auth', None)
476476
rkwargs.setdefault('cert', rkwargs.get('cert'))
477477
rkwargs.setdefault('verify', rkwargs.get('verify', True))
478+
479+
return rkwargs
480+
481+
482+
def http_get(*args, **kwargs):
483+
rkwargs = http_prepare(*args, **kwargs)
478484
return requests.get(*args, **rkwargs)
479485

480486

487+
def http_put(*args, **kwargs):
488+
rkwargs = http_prepare(*args, **kwargs)
489+
490+
if 'data' in kwargs:
491+
if isinstance(kwargs['data'], dict):
492+
rkwargs['json'] = kwargs['data']
493+
else:
494+
rkwargs['data'] = kwargs['data']
495+
496+
return requests.put(*args, **rkwargs)
497+
498+
499+
def http_delete(*args, **kwargs):
500+
rkwargs = http_prepare(*args, **kwargs)
501+
return requests.delete(*args, **rkwargs)
502+
503+
481504
def element_to_string(element, encoding=None, xml_declaration=False):
482505
"""
483506
Returns a string from a XML object

0 commit comments

Comments
 (0)