5 | * @version 0.0.1
6 | * @license MIT
7 | */
8 |
9 | :root {
10 | --color-bg: #252a33;
11 | --color-text: #eee;
12 | --color-text-subtle: #a2a2a2;
13 | }
14 |
15 | [data-termynal] {
16 | width: 750px;
17 | max-width: 100%;
18 | background: var(--color-bg);
19 | color: var(--color-text);
20 | /* font-size: 18px; */
21 | font-size: 15px;
22 | /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */
23 | font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace;
24 | border-radius: 4px;
25 | padding: 75px 45px 35px;
26 | position: relative;
27 | -webkit-box-sizing: border-box;
28 | box-sizing: border-box;
29 | }
30 |
31 | [data-termynal]:before {
32 | content: '';
33 | position: absolute;
34 | top: 15px;
35 | left: 15px;
36 | display: inline-block;
37 | width: 15px;
38 | height: 15px;
39 | border-radius: 50%;
40 | /* A little hack to display the window buttons in one pseudo element. */
41 | background: #d9515d;
42 | -webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;
43 | box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;
44 | }
45 |
46 | [data-termynal]:after {
47 | content: 'bash';
48 | position: absolute;
49 | color: var(--color-text-subtle);
50 | top: 5px;
51 | left: 0;
52 | width: 100%;
53 | text-align: center;
54 | }
55 |
56 | a[data-terminal-control] {
57 | text-align: right;
58 | display: block;
59 | color: #aebbff;
60 | }
61 |
62 | [data-ty] {
63 | display: block;
64 | line-height: 2;
65 | }
66 |
67 | [data-ty]:before {
68 | /* Set up defaults and ensure empty lines are displayed. */
69 | content: '';
70 | display: inline-block;
71 | vertical-align: middle;
72 | }
73 |
74 | [data-ty="input"]:before,
75 | [data-ty-prompt]:before {
76 | margin-right: 0.75em;
77 | color: var(--color-text-subtle);
78 | }
79 |
80 | [data-ty="input"]:before {
81 | content: '$';
82 | }
83 |
84 | [data-ty][data-ty-prompt]:before {
85 | content: attr(data-ty-prompt);
86 | }
87 |
88 | [data-ty-cursor]:after {
89 | content: attr(data-ty-cursor);
90 | font-family: monospace;
91 | margin-left: 0.5em;
92 | -webkit-animation: blink 1s infinite;
93 | animation: blink 1s infinite;
94 | }
95 |
96 |
97 | /* Cursor animation */
98 |
99 | @-webkit-keyframes blink {
100 | 50% {
101 | opacity: 0;
102 | }
103 | }
104 |
105 | @keyframes blink {
106 | 50% {
107 | opacity: 0;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | hide:
3 | - navigation
4 | - toc
5 |
6 | title : GeoServerX
7 | description: modern python and CLI package for communicating with GeoServer
8 | ---
9 | # Welcome to geoserverx
10 |
11 | `geoserverx` is a modern Python package that provides an efficient and scalable way to interact with Geoserver REST APIs. It leverages the asynchronous capabilities of Python to offer a high-performance and reliable solution for managing Geoserver data and services.
12 |
13 | With geoserverx, users can easily access and modify data in Geoserver, such as uploading and deleting shapefiles, publishing layers, creating workspaces, styles, etc. . The package supports asynchronous requests along with synchronous method to the Geoserver REST API, which enables users to perform multiple tasks simultaneously, improving performance and reducing wait times.
14 |
15 | Apart from being implemented as Python package, geoserverx also provides CLI support for all of its operations. Which makes it useful for people who want to avoid Python all-together.
16 |
17 | Checkout official pypi link [here](https://pypi.org/project/geoserverx/)
18 |
19 | ## Get Started
20 |
21 | `geoserverx` can be installed using `pip` or `pip3`
22 |
23 |
24 |
25 | ```console
26 |
27 | pip install geoserverx
28 |
29 | ---> 100%
30 | ```
31 |
32 |
33 | After which , It can be used in Python projects using sync, async methods or can ve used as Command Line tool
34 |
35 | ## Architecture
36 |
37 | `geoserverx` is built on top of `httpx` and `pydantic` libraries. It uses `httpx` for making HTTP requests and `pydantic` for data validation. The package is designed to be modular and extensible, allowing for easy integration with other libraries and frameworks.
38 |
39 | The package is structured into two main components: the `SyncGeoServerX` class and the `AsyncGeoServerX` class. The `SyncGeoServerX` class provides synchronous methods for interacting with Geoserver, while the `AsyncGeoServerX` class provides asynchronous methods using the `anyio` library
40 |
41 | { align=left }
42 |
43 |
44 | ## For testing purpose
45 | If you don't have GeoServer installed locally, feel free to use following command to quickly spin up Geoserver using [Docker](https://www.docker.com/)
46 |
47 |
48 | ```console
49 | docker run -e GEOSERVER_ADMIN_USER=admin -e GEOSERVER_ADMIN_PASSWORD=geoserver -e SAMPLE_DATA=true -p 8080:8080 kartoza/GeoServer
50 | ```
51 |
52 |
53 | Please note that this will work on amd64 architecture machines.
--------------------------------------------------------------------------------
/docs/pages/async/example.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | Here, we'll have a look at implementation `geoserverx` asynchronous Class
4 |
5 | !!! get "Get started"
6 | To start using `geoserverx` in Async mode, create a new instance of `AsyncGeoServerX` Class
7 |
8 | ## Setup Class instance
9 |
10 | `AsyncGeoServerX` Class has default username, password, url which points to default GeoServer settings.
11 | ```Python
12 | # Import class from package
13 | from geoserverx._async.gsx import AsyncGeoServerX
14 | import asyncio
15 | # Create class Instance with default paramaters
16 | client = AsyncGeoServerX()
17 | ```
18 | We'll assume connection to local GeoServer with default credentials
19 |
20 | ## Get all workspaces
21 |
22 | ```Python hl_lines="7"
23 | from geoserverx._async.gsx import AsyncGeoServerX
24 | import asyncio
25 |
26 | async def get_info_raster_workspaces(url, username, password):
27 | print("-------------start-----------------")
28 | client = AsyncGeoServerX(username, password,url)
29 | print(await client.get_all_workspaces())
30 |
31 | async def main():
32 | await asyncio.gather(get_info_raster_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer'),get_info_raster_workspaces(url='http://locahost:8080/geoserver/rest',username='admin', password='myP'),get_info_raster_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer'))
33 |
34 | asyncio.run(main())
35 |
36 | ''' Console
37 | -------------start-----------------
38 | -------------start-----------------
39 | -------------start-----------------
40 | workspaces=workspaceDict(workspace=[WorkspaceInBulk(name='MySimple', href='http://localhost:8080/geoserver/rest/workspaces/MySimple.json'), WorkspaceInBulk(name='MyHidden', href='http://localhost:8080/geoserver/rest/workspaces/MyHidden.json'), WorkspaceInBulk(name='MyDefault', href='http://localhost:8080/geoserver/rest/workspaces/MyDefault.json'), WorkspaceInBulk(name='nondefaultws', href='http://localhost:8080/geoserver/rest/workspaces/nondefaultws.json'), WorkspaceInBulk(name='mydefaultws', href='http://localhost:8080/geoserver/rest/workspaces/mydefaultws.json'), WorkspaceInBulk(name='ajadasfasdf', href='http://localhost:8080/geoserver/rest/workspaces/ajadasfasdf.json'), WorkspaceInBulk(name='ajada', href='http://localhost:8080/geoserver/rest/workspaces/ajada.json'), WorkspaceInBulk(name='aja', href='http://localhost:8080/geoserver/rest/workspaces/aja.json'), WorkspaceInBulk(name='cesium', href='http://localhost:8080/geoserver/rest/workspaces/cesium.json')])
41 | '''
42 | ```
43 |
44 | ## Get Information about `cesium` workspace
45 |
46 | ```Python hl_lines="7"
47 | from geoserverx._async.gsx import AsyncGeoServerX
48 | import asyncio
49 |
50 | async def get_info_raster_workspaces(url, username, password,workspace):
51 | print("-------------start-----------------")
52 | client = AsyncGeoServerX(username, password,url)
53 | print(await client.get_workspace(workspace))
54 |
55 | async def main():
56 | await asyncio.gather(get_info_raster_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',workspace='cesium'))
57 |
58 | asyncio.run(main())
59 |
60 | ''' Console
61 | -------------start-----------------
62 | workspace=SingleWorkspace(name='cesium', isolated=False, dateCreated='2023-02-13 06:43:28.793 UTC', dataStores='http://localhost:8080/geoserver/rest/workspaces/cesium/datastores.json', coverageStores='http://localhost:8080/geoserver/rest/workspaces/cesium/coveragestores.json', wmsStores='http://localhost:8080/geoserver/rest/workspaces/cesium/wmsstores.json', wmtsStores='http://localhost:8080/geoserver/rest/workspaces/cesium/wmtsstores.json')
63 | '''
64 | ```
65 |
66 | ## Create New workspaces
67 |
68 | * MyDefault - Default and not Isolated
69 | * MyHidden - Not Default and Isolated
70 | * MySimple - Not Default and not Isolated
71 |
72 | ```Python hl_lines="7"
73 | from geoserverx._async.gsx import AsyncGeoServerX
74 | import asyncio
75 |
76 | async def create_single_workspaces(url, username, password,workspace,default,isolated):
77 | print("-------------start-----------------")
78 | client = AsyncGeoServerX(username, password,url)
79 | print(await client.create_workspace(workspace, default,isolated))
80 |
81 | async def main():
82 | await asyncio.gather(create_single_workspaces(
83 | url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
84 | workspace='AsyncMyDefault',default=True,isolated= False),
85 | create_single_workspaces(
86 | url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
87 | workspace='AsyncMyHidden',default=False,isolated= True),
88 | create_single_workspaces(
89 | url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
90 | workspace='AsyncMySimple',default=False,isolated= False))
91 |
92 | asyncio.run(main())
93 |
94 | ''' Console
95 | -------------start-----------------
96 | -------------start-----------------
97 | -------------start-----------------
98 | code=201 response='Data added successfully'
99 | code=201 response='Data added successfully'
100 | code=201 response='Data added successfully'
101 | '''
102 | ```
103 |
104 | ## Get all Vector stores in `cesium` workspace
105 |
106 | ```Python hl_lines="7"
107 | from geoserverx._async.gsx import AsyncGeoServerX
108 | import asyncio
109 |
110 | async def create_single_workspaces(url, username, password,workspace):
111 | print("-------------start-----------------")
112 | client = AsyncGeoServerX(username, password,url)
113 | print(await client.get_vector_stores_in_workspaces(workspace))
114 |
115 | async def main():
116 | await asyncio.gather(create_single_workspaces(
117 | url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
118 | workspace='cesium'))
119 |
120 | asyncio.run(main())
121 |
122 | ''' Console
123 | -------------start-----------------
124 | dataStores=DataStoreDict(dataStore=[DataStoreInBulk(name='mygpkgs', href='http://localhost:8080/geoserver/rest/workspaces/cesium/datastores/mygpkgs.json'), DataStoreInBulk(name='myshp', href='http://localhost:8080/geoserver/rest/workspaces/cesium/datastores/myshp.json'), DataStoreInBulk(name='mysql', href='http://localhost:8080/geoserver/rest/workspaces/cesium/datastores/mysql.json')])
125 | '''
126 | ```
127 |
128 | ## Get Information of Vector store `myshp` in `cesium` workspace
129 |
130 | ```Python hl_lines="7"
131 | from geoserverx._async.gsx import AsyncGeoServerX
132 | import asyncio
133 |
134 | async def get_info_vector_workspaces(url, username, password,workspace,store):
135 | print("-------------start-----------------")
136 | client = AsyncGeoServerX(username, password,url)
137 | print(await client.get_vector_store(workspace,store))
138 |
139 | async def main():
140 | await asyncio.gather(get_info_vector_workspaces(
141 | url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
142 | workspace='cesium',store='myshp'))
143 |
144 | asyncio.run(main())
145 |
146 |
147 | ''' Console
148 | -------------start-----------------
149 | dataStore=DataStoreModelDetails(name='myshp', description=None, enabled=True, workspace=WorkspaceInBulk(name='cesium', href='http://localhost:8080/geoserver/rest/workspaces/cesium.json'), connectionParameters=EntryItem(entry=[DatastoreConnection(key='namespace', path='cesium'), DatastoreConnection(key='url', path='file:/path/to/nyc.shp')]), dateCreated='2023-02-28 18:14:01.199 UTC', dateModified=None, featureTypes='http://localhost:8080/geoserver/rest/workspaces/cesium/datastores/myshp/featuretypes.json')
150 | '''
151 | ```
152 |
177 |
178 | ## Get all Raster stores in `cesium` workspace
179 |
180 | ```Python hl_lines="7"
181 | from geoserverx._async.gsx import AsyncGeoServerX
182 | import asyncio
183 |
184 | async def get_all_raster_workspaces(url, username, password,workspace):
185 | print("-------------start-----------------")
186 | client = AsyncGeoServerX(username, password,url)
187 | print(await client.get_raster_stores_in_workspaces(workspace))
188 |
189 | async def main():
190 | await asyncio.gather(get_all_raster_workspaces(
191 | url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',workspace='cesium'))
192 |
193 | asyncio.run(main())
194 |
195 | ''' Console
196 | -------------start-----------------
197 | coverageStores=CoveragesStoresDict(coverageStore=[CoveragesStoreInBulk(name='dem', href='http://localhost:8080/geoserver/rest/workspaces/cesium/coveragestores/dem.json'), CoveragesStoreInBulk(name='dsm', href='http://localhost:8080/geoserver/rest/workspaces/cesium/coveragestores/dsm.json'), CoveragesStoreInBulk(name='ortho', href='http://localhost:8080/geoserver/rest/workspaces/cesium/coveragestores/ortho.json')])
198 | '''
199 | ```
200 |
201 | ## Get Information of Raster store `dsm` in `cesium` workspace
202 |
203 | ```Python hl_lines="7"
204 | from geoserverx._async.gsx import AsyncGeoServerX
205 | import asyncio
206 |
207 | async def get_info_raster_workspaces(url, username, password,workspace,store):
208 | print("-------------start-----------------")
209 | client = AsyncGeoServerX(username, password,url)
210 | print(await client.get_raster_store(workspace,store))
211 |
212 | async def main():
213 | await asyncio.gather(get_info_raster_workspaces(
214 | url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',workspace='cesium',store='dsm'))
215 |
216 | asyncio.run(main())
217 |
218 | ''' Console
219 | -------------start-----------------
220 | coverageStore=CoveragesStoreModelDetail(name='dsm', description=None, enabled=True, workspace=WorkspaceInBulk(name='cesium', href='http://localhost:8080/geoserver/rest/workspaces/cesium.json'), url='file:///Users/krishnaglodha/Desktop/IGI_DATA/DSM/IGI_DSM1m1.tif', coverages='http://localhost:8080/geoserver/rest/workspaces/cesium/coveragestores/dsm/coverages.json', dateCreated='2023-02-23 13:39:48.417 UTC', metadata=None)
221 | '''
222 | ```
223 |
224 | ## Get all Styles in GeoServer
225 |
226 | ```Python hl_lines="7"
227 | from geoserverx._async.gsx import AsyncGeoServerX
228 | import asyncio
229 |
230 | async def get_info_raster_workspaces(url, username, password):
231 | print("-------------start-----------------")
232 | client = AsyncGeoServerX(username, password,url)
233 | print(await client.get_all_styles())
234 |
235 | async def main():
236 | await asyncio.gather(get_info_raster_workspaces(
237 | url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer'))
238 |
239 | asyncio.run(main())
240 |
241 | ''' Console
242 | -------------start-----------------
243 | styles=allStyle(style=[allStyleList(name='burg', href='http://localhost:8080/geoserver/rest/styles/burg.json'), allStyleList(name='capitals', href='http://localhost:8080/geoserver/rest/styles/capitals.json'), allStyleList(name='cite_lakes', href='http://localhost:8080/geoserver/rest/styles/cite_lakes.json'), allStyleList(name='dem', href='http://localhost:8080/geoserver/rest/styles/dem.json'), allStyleList(name='generic', href='http://localhost:8080/geoserver/rest/styles/generic.json'), allStyleList(name='giant_polygon', href='http://localhost:8080/geoserver/rest/styles/giant_polygon.json'), allStyleList(name='grass', href='http://localhost:8080/geoserver/rest/styles/grass.json'), allStyleList(name='green', href='http://localhost:8080/geoserver/rest/styles/green.json'), allStyleList(name='line', href='http://localhost:8080/geoserver/rest/styles/line.json'), allStyleList(name='poi', href='http://localhost:8080/geoserver/rest/styles/poi.json'), allStyleList(name='point', href='http://localhost:8080/geoserver/rest/styles/point.json'), allStyleList(name='poly_landmarks', href='http://localhost:8080/geoserver/rest/styles/poly_landmarks.json'), allStyleList(name='polygon', href='http://localhost:8080/geoserver/rest/styles/polygon.json'), allStyleList(name='pophatch', href='http://localhost:8080/geoserver/rest/styles/pophatch.json'), allStyleList(name='population', href='http://localhost:8080/geoserver/rest/styles/population.json'), allStyleList(name='rain', href='http://localhost:8080/geoserver/rest/styles/rain.json'), allStyleList(name='raster', href='http://localhost:8080/geoserver/rest/styles/raster.json'), allStyleList(name='restricted', href='http://localhost:8080/geoserver/rest/styles/restricted.json'), allStyleList(name='simple_roads', href='http://localhost:8080/geoserver/rest/styles/simple_roads.json'), allStyleList(name='simple_streams', href='http://localhost:8080/geoserver/rest/styles/simple_streams.json'), allStyleList(name='tiger_roads', href='http://localhost:8080/geoserver/rest/styles/tiger_roads.json')])
244 | '''
245 | ```
246 |
247 | ## Get Single Style in GeoServer
248 |
249 | ```Python hl_lines="7"
250 | from geoserverx._async.gsx import AsyncGeoServerX
251 | import asyncio
252 |
253 | async def get_info_raster_workspaces(url, username, password,style):
254 | print("-------------start-----------------")
255 | client = AsyncGeoServerX(username, password,url)
256 | print(await client.get_style(style))
257 |
258 | async def main():
259 | await asyncio.gather(get_info_raster_workspaces(
260 | url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',style='poi'))
261 |
262 | asyncio.run(main())
263 | ''' Console
264 | -------------start-----------------
265 | style=SingleStyle(name='poi', format='sld', languageVersion=langVersion(version='1.0.0'), filename='poi.sld')
266 | '''
267 | ```
268 |
--------------------------------------------------------------------------------
/docs/pages/async/index.md:
--------------------------------------------------------------------------------
1 | # Asynchronous way of using geoserverx
2 |
3 | `geoserverx` allows user to call methods asynchronously.
4 |
5 | !!! get "Get started"
6 | To start using `geoserverx` in Sync mode, create a new instance of `AsyncGeoServerX` Class
7 |
8 | ## Setup Class instance
9 |
10 | `AsyncGeoServerX` Class has default username, password, url which points to default GeoServer settings.
11 | ```Python
12 | # Import class from package
13 | from geoserverx._async.gsx import AsyncGeoServerX
14 | # Create class Instance with default paramaters
15 | client = AsyncGeoServerX()
16 | ```
17 |
18 | These paramaters however can be changed as follows
19 | ```Python
20 | # Import class from package
21 | from geoserverx._async.gsx import AsyncGeoServerX
22 | # Create class Instance with custom paramaters
23 | client = AsyncGeoServerX(username='mygeos', password='SecuredPass',url='http://127.0.0.1:9090/geoserver/rest/')
24 | ```
25 |
26 |
27 | This class can also be used as context manager to manage the opening and closing connection automatically.
28 | ```Python
29 | # Import class from package
30 | from geoserverx._async.gsx import AsyncGeoServerX,GeoServerXAuth
31 | import asyncio
32 | # Create class Instance with custom paramaters
33 | client = AsyncGeoServerX(username='mygeos', password='SecuredPass',url='http://127.0.0.1:9090/geoserver/rest/')
34 |
35 | #Using with as
36 | async def main():
37 | async with client as cl:
38 | response = await cl.get_all_workspaces()
39 | print(response)
40 |
41 | loop = asyncio.get_event_loop()
42 | loop.run_until_complete(main())
43 | ```
--------------------------------------------------------------------------------
/docs/pages/async/raster-store.md:
--------------------------------------------------------------------------------
1 | # Raster Stores
2 |
3 | `geoserverx` allows users to access all/one raster stores from GeoServer
4 |
5 |
6 | ## Get all raster stores
7 | This command fetches all Vector store available in given workspace from GeoServer.
8 |
9 | ```py
10 | # Get all raster stores available in `cite` workspace
11 | await client.get_raster_stores_in_workspaces('cite')
12 | ```
13 |
14 |
15 | ## Get single raster store
16 |
17 | This command fetches all Information about raster store available in given workspace from GeoServer.
18 |
19 | ```Python
20 | # Get all information about `image` raster stores available in `cite` workspace
21 | await client.get_raster_store(workspace='cite', store='image')
22 | ```
23 |
--------------------------------------------------------------------------------
/docs/pages/async/style.md:
--------------------------------------------------------------------------------
1 | # Style
2 |
3 |
4 | ## Get all Styles
5 |
6 | This command fetches all Styles available in GeoServer.
7 |
8 | ```Python
9 | # Get all styles available in GeoServer
10 | await client.get_all_styles()
11 | ```
12 |
13 |
14 | ## Get single Style
15 |
16 | This command fetches information about particular Style from GeoServer.
17 |
18 | ```py
19 | # Get information about `population` style from GeoServer
20 | await client.get_style('population')
21 | ```
22 |
--------------------------------------------------------------------------------
/docs/pages/async/vector-store.md:
--------------------------------------------------------------------------------
1 | # Vector Stores
2 |
3 | `geoserverx` allows users to access all/one vector stores from GeoServer. As of now, `geoserverx` also supports new vector store creation for `shapefile` and `gpkg` data
4 |
5 | ## Get all Vector stores
6 | This command fetches all Vector store available in given workspace from GeoServer.
7 |
8 | ```Python
9 | # Get all vector stores available in `cite` workspace
10 | await client.get_vector_stores_in_workspaces('cite')
11 | ```
12 |
13 |
14 |
15 | ## Get single Vector store
16 | This command fetches all Information about Vector store available in given workspace from GeoServer.
17 |
18 | ```Python
19 | # Get all information about `shape` vector stores available in `cite` workspace
20 | await client.get_vector_store(workspace='cite', store='shape')
21 | ```
22 |
23 |
24 | ## Create new shapefile Vector store
25 | Use this command to create new Vector store based on `shapefile` path.
26 |
27 | ```Python
28 | # Create new store in `cite` workspace with name `shape` and using `path/for/shapefile` as local shapefile path
29 | await client.create_file_store(workspace='cite', store='shape', file='path/for/shapefile', service_type='shapefile')
30 | ```
31 |
32 |
33 | ## Create new geopackage Vector store
34 | Use this command to create new Vector store based on `Geopackage` path.
35 |
36 | ```Python
37 | # Create new store in `cite` workspace with name `shape` and using `path/for/gpkg` as local Geopackage path
38 | await client.create_file_store(workspace='cite', store='shape', file='path/for/gpkg', service_type='gpkg')
39 | ```
40 |
41 |
42 | ## Create new PostGIS Vector store
43 | Use this command to create new Vector store based on `PostGIS` connection.
44 |
45 | ```Python
46 | # Create new store in `cite` workspace with name `pg` and using `PostgreSQL` credentials
47 | await client.create_pg_store(
48 | name="pg",
49 | workspace="cite",
50 | host="localhost",
51 | port=5432,
52 | username="XXXXXXXX",
53 | password="XXXXXXXX",
54 | database="test")
55 | ```
56 |
57 | ## Get all Vector layers
58 | This command fetches all Vector layers available in given workspace from GeoServer.
59 |
60 | ```Python
61 | # Get all vector layers available in `cite` workspace
62 | await client.get_all_layers(workspace='cite')
63 | ```
64 |
65 | ## Get single Vector layer
66 | This command fetches all Information about Vector layer available in given workspace from GeoServer.
67 |
68 | ```Python
69 | # Get all information about `roads` vector layers available in `cite` workspace
70 | await client.get_vector_layer(workspace='cite', store='shape', layer='roads')
71 | ```
--------------------------------------------------------------------------------
/docs/pages/async/workspace.md:
--------------------------------------------------------------------------------
1 | # Workspaces
2 |
3 | `geoserverx` allows users to access all/one workspace from GeoServer, along with ability to add new workspaces.
4 |
5 | ## Get all workspaces
6 | This command fetches all workspaces available in GeoServer. No parameters are required to be passed.
7 |
8 | ```py
9 | # Get all workspaces in GeoServer
10 | await client.get_all_workspaces()
11 | ```
12 |
13 | ## Get single workspace
14 | This command fetches workspace with paramter as name of it from GeoServer.
15 | ```Python
16 | # Get workspace with name `cite`
17 | await client.get_workspace('cite')
18 | ```
19 |
20 | ## Create workspace
21 | This command allows user to create new workspace.
22 | Creating new workspace requires following parameters
23 |
24 | * Name `str` : To define Name of the workspace
25 | * default `bool` : To define whether to keep workspace as default or not
26 | * Isolated `bool` : To define whether to keep workspace Isolated or not
27 |
28 | ```Python
29 | #Create new workspace with name `my_wrkspc` , make it Default and Isolated
30 | await client.create_workspace(name='my_wrkspc',default=True,Isolated=True)
31 | ```
32 |
--------------------------------------------------------------------------------
/docs/pages/cli/example.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | Here, we'll have a look at implementation `geoserverx` synchronous Class.
4 |
5 | !!! get "Get started"
6 | To start using `gsx` CLI tool, install `geoserverx` package and turn on the environment.
7 |
8 | ## Setup CLI instance
9 |
10 |
11 | ```console
12 | $ pip install geoserverx
13 | ---> 100%
14 |
15 | $ gsx
16 | Usage: gsx [OPTIONS] COMMAND [ARGS]...
17 | Try 'gsx --help' for help.
18 |
19 | Error: Missing command.
20 | ```
21 |
22 |
23 | We'll assume connection to local GeoServer with default credentials
24 |
25 |
26 | ## Get all workspaces
27 |
28 |
29 | ```console
30 | $ gsx workspaces
31 |
32 | {"workspaces": {"workspace": [{"name": "cesium", "href":
33 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium.json"}]}}
34 | ```
35 |
36 |
37 |
38 | ## Get single workspaces
39 |
40 |
41 | ```console
42 | $ gsx workspace --workspace cesium
43 | {"workspace": {"name": "cesium", "isolated": false, "dateCreated": "2023-02-13
44 | 06:43:28.793 UTC", "dataStores":
45 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/datastores.json",
46 | "coverageStores":
47 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/coveragestores.json",
48 | "wmsStores": "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/wmsstores.json",
49 | "wmtsStores": "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/wmtsstores.json"}}
50 | ```
51 |
52 |
53 |
54 | ## Create single workspaces
55 |
56 |
57 | ```console
58 | $ gsx create-workspace --workspace mydefaultws --default
59 | code=201 response='Data added successfully'
60 | ```
61 |
62 |
63 | ## Get all Vector stores
64 |
65 |
66 | ```console
67 | $ gsx vector-st-wp --workspace cesium
68 | {"dataStores": {"dataStore": [{"name": "mysqlllllll", "href":
69 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/datastores/mysqlllllll.json"}]}}
70 | ```
71 |
72 |
73 |
74 | ## Get single Vector store information
75 |
76 |
77 | ```console
78 | $ gsx vector-store --workspace cesium --store mysqlllllll
79 | {"dataStore": {"name": "mysqlllllll", "description": null, "enabled": true, "workspace":
80 | {"name": "cesium", "href":
81 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium.json"}, "connectionParameters":
82 | {"entry": [{"key": "Evictor run periodicity", "path": "300"}, {"key": "fetch size",
83 | "path": "1000"}, {"key": "Expose primary keys", "path": "false"}, {"key": "validate
84 | connections", "path": "true"}, {"key": "Connection timeout", "path": "20"}, {"key":
85 | "Batch insert size", "path": "1"}, {"key": "database", "path": "appsolicitous_dcra"},
86 | {"key": "port", "path": "3306"}, {"key": "passwd", "path":
87 | "crypt1:njsGJk9CEY8jiaqfSYyQGZeB9RLB2sh7"}, {"key": "storage engine", "path": "MyISAM"},
88 | {"key": "min connections", "path": "1"}, {"key": "dbtype", "path": "mysql"}, {"key":
89 | "host", "path": "23.29.118.44"}, {"key": "namespace", "path": "cesium"}, {"key": "max
90 | connections", "path": "10"}, {"key": "Evictor tests per run", "path": "3"}, {"key": "Test
91 | while idle", "path": "true"}, {"key": "user", "path": "appsolicitous_dcra"}, {"key": "Max
92 | connection idle time", "path": "300"}]}, "dateCreated": "2023-02-28 10:38:52.70 UTC",
93 | "dateModified": null, "featureTypes":
94 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/datastores/mysqlllllll/featuretyp
95 | es.json"}}
96 |
97 | ```
98 |
99 |
100 |
101 | ## Get all raster stores
102 |
103 |
104 | ```console
105 | $ gsx raster-st-wp --workspace cesium
106 | {"coverageStores": {"coverageStore": [{"name": "dem", "href":
107 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/coveragestores/dem.json"},
108 | {"name": "dsm", "href":
109 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/coveragestores/dsm.json"},
110 | {"name": "ortho", "href":
111 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/coveragestores/ortho.json"}]}}
112 | ```
113 |
114 |
115 |
116 | ## Get single raster store information
117 |
118 |
119 | ```console
120 | $ gsx raster-store --workspace cesium --store dsm
121 | {"coverageStore": {"name": "dsm", "description": null, "enabled": true, "workspace":
122 | {"name": "cesium", "href":
123 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium.json"}, "url":
124 | "file:///Users/krishnaglodha/Desktop/IGI_DATA/DSM/IGI_DSM1m1.tif", "coverages":
125 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/coveragestores/dsm/coverages.json
126 | ", "dateCreated": "2023-02-23 13:39:48.417 UTC", "metadata": null}}
127 |
128 | ```
129 |
130 |
131 |
132 | ## Get all styles
133 |
134 |
135 | ```console
136 | $ gsx styles
137 | {"styles": {"style": [{"name": "burg", "href":
138 | "http://127.0.0.1:8080/geoserver/rest/styles/burg.json"}, {"name": "capitals", "href":
139 | "http://127.0.0.1:8080/geoserver/rest/styles/capitals.json"}, {"name": "cite_lakes",
140 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/cite_lakes.json"}, {"name": "dem",
141 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/dem.json"}, {"name": "generic",
142 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/generic.json"}, {"name":
143 | "giant_polygon", "href":
144 | "http://127.0.0.1:8080/geoserver/rest/styles/giant_polygon.json"}, {"name": "grass",
145 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/grass.json"}, {"name": "green",
146 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/green.json"}, {"name": "line",
147 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/line.json"}]}}
148 | ```
149 |
150 |
151 | ## Get single style information
152 |
153 |
154 | ```console
155 | $ gsx style --style p
156 | oi
157 | {"style": {"name": "poi", "format": "sld", "languageVersion": {"version": "1.0.0"},
158 | "filename": "poi.sld"}}
159 | ```
160 |
161 |
162 |
--------------------------------------------------------------------------------
/docs/pages/cli/index.md:
--------------------------------------------------------------------------------
1 | # Command line access
2 |
3 | `geoserverx` allows users to leverage power of command line to communicate with GeoServer.
4 | `gsx` is a command line tool by `geoserverx`.
5 |
6 | ## Installation
7 | To use `gsx` , Install `geoserverx` using `pip` on local environment.
8 |
9 |
10 |
11 | ```console
12 |
13 | pip install geoserverx
14 |
15 | ---> 100%
16 |
17 | $ gsx --help
18 | Usage: gsx [OPTIONS] COMMAND [ARGS]...
19 |
20 | GeoserverX CLI tools to talk to Geoserver efficiently .
21 |
22 | Options:
23 | --install-completion [bash|zsh|fish|powershell|pwsh]
24 | Install completion for the specified shell.
25 | --show-completion [bash|zsh|fish|powershell|pwsh]
26 | Show completion for the specified shell, to
27 | copy it or customize the installation.
28 | --help Show this message and exit.
29 |
30 | Commands:
31 | create-file Create Vector Layer in Geoserver
32 | create-workspace Add workspace in the Geoserver
33 | raster-st-wp Get raster stores in specific workspaces
34 | raster-store Get raster store information in specific workspaces
35 | style Get style in Geoserver
36 | styles Get all styles in Geoserver
37 | vector-st-wp Get vector stores in specific workspaces
38 | vector-store Get vector store information in specific workspaces
39 | workspace Get workspace in the Geoserver
40 | workspaces Get all workspaces in the Geoserver
41 | ```
42 |
43 |
44 | Checkout other pages to understand how to use Command line
--------------------------------------------------------------------------------
/docs/pages/cli/raster-store.md:
--------------------------------------------------------------------------------
1 | # Raster Stores
2 |
3 | `geoserverx` allows users to access all/one raster stores from GeoServer. As of now, `geoserverx` also supports new raster store creation for `shapefile` and `gpkg` data
4 |
5 | !!! get "Get started"
6 | To start using `geoserverx` using command line, activate the Environment where package is installed and use `gsx` command
7 |
8 |
9 | ## Paramters for all raster stores in Workspace command
10 |
11 |
12 | ```console
13 | $ gsx raster-st-wp --help
14 | Usage: gsx raster-st-wp [OPTIONS]
15 |
16 | Get raster stores in specific workspaces
17 |
18 | Options:
19 | --request [sync|async] [default: requestEnum._sync]
20 | --workspace TEXT Workspace name [required]
21 | --url TEXT Geoserver REST URL [default:
22 | http://127.0.0.1:8080/geoserver/rest/]
23 | --password TEXT Geoserver Password [default: geoserver]
24 | --username TEXT Geoserver username [default: admin]
25 | --help Show this message and exit.
26 | ```
27 |
28 |
29 | As listed above, `raster-st-wp` command accepts following parameters.
30 |
31 | * request type ( sync or async )
32 | * url - Geoserver REST URL
33 | * password - Password for GeoServer
34 | * username - Username for GeoServer
35 |
36 | All these parameters have default value setup which will work for local default installation. Apart from this `workspace` paramters must be added which aims at the workspace we are interested in
37 |
38 | ## Get all raster stores
39 |
40 |
41 | ```console
42 | $ gsx raster-st-wp --workspace cesium
43 | {"coverageStores": {"coverageStore": [{"name": "dem", "href":
44 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/coveragestores/dem.json"},
45 | {"name": "dsm", "href":
46 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/coveragestores/dsm.json"},
47 | {"name": "ortho", "href":
48 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/coveragestores/ortho.json"}]}}
49 | ```
50 |
51 |
52 | ## Paramters for single raster stores command
53 |
54 |
55 | ```console
56 | $ gsx raster-store --help
57 | Usage: gsx raster-store [OPTIONS]
58 |
59 | Get raster store information in specific workspaces
60 |
61 | Options:
62 | --request [sync|async] [default: requestEnum._sync]
63 | --workspace TEXT Workspace name [required]
64 | --store TEXT Store name [required]
65 | --url TEXT Geoserver REST URL [default:
66 | http://127.0.0.1:8080/geoserver/rest/]
67 | --password TEXT Geoserver Password [default: GeoServer]
68 | --username TEXT Geoserver username [default: admin]
69 | --help Show this message and exit.
70 |
71 | ```
72 |
73 |
74 | This command takes an additional parameter of name of the store.
75 |
76 | ## Get single raster store information
77 |
78 |
79 | ```console
80 | $ gsx raster-store --workspace cesium --store dsm
81 | {"coverageStore": {"name": "dsm", "description": null, "enabled": true, "workspace":
82 | {"name": "cesium", "href":
83 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium.json"}, "url":
84 | "file:///Users/krishnaglodha/Desktop/IGI_DATA/DSM/IGI_DSM1m1.tif", "coverages":
85 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/coveragestores/dsm/coverages.json
86 | ", "dateCreated": "2023-02-23 13:39:48.417 UTC", "metadata": null}}
87 |
88 | ```
89 |
90 |
--------------------------------------------------------------------------------
/docs/pages/cli/style.md:
--------------------------------------------------------------------------------
1 | # Style
2 |
3 | `geoserverx` allows users to access all/one raster stores from GeoServer. As of now, `geoserverx` also supports new raster store creation for `shapefile` and `gpkg` data
4 |
5 | !!! get "Get started"
6 | To start using `geoserverx` using command line, activate the Environment where package is installed and use `gsx` command
7 |
8 |
9 | ## Paramters for all styles command
10 |
11 |
12 | ```console
13 | $ gsx styles --help
14 | Usage: gsx styles [OPTIONS]
15 |
16 | Get all styles in Geoserver
17 |
18 | Options:
19 | --request [sync|async] [default: requestEnum._sync]
20 | --url TEXT Geoserver REST URL [default:
21 | http://127.0.0.1:8080/geoserver/rest/]
22 | --password TEXT Geoserver Password [default: GeoServer]
23 | --username TEXT Geoserver username [default: admin]
24 | --help Show this message and exit.
25 | ```
26 |
27 |
28 | As listed above, `styles` command accepts following parameters.
29 |
30 | * request type ( sync or async )
31 | * url - Geoserver REST URL
32 | * password - Password for GeoServer
33 | * username - Username for GeoServer
34 |
35 | All these parameters have default value setup which will work for local default installation.
36 |
37 | ## Get all styles
38 |
39 |
40 | ```console
41 | $ gsx styles
42 | {"styles": {"style": [{"name": "burg", "href":
43 | "http://127.0.0.1:8080/geoserver/rest/styles/burg.json"}, {"name": "capitals", "href":
44 | "http://127.0.0.1:8080/geoserver/rest/styles/capitals.json"}, {"name": "cite_lakes",
45 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/cite_lakes.json"}, {"name": "dem",
46 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/dem.json"}, {"name": "generic",
47 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/generic.json"}, {"name":
48 | "giant_polygon", "href":
49 | "http://127.0.0.1:8080/geoserver/rest/styles/giant_polygon.json"}, {"name": "grass",
50 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/grass.json"}, {"name": "green",
51 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/green.json"}, {"name": "line",
52 | "href": "http://127.0.0.1:8080/geoserver/rest/styles/line.json"}]}}
53 | ```
54 |
55 |
56 | ## Paramters for single style command
57 |
58 |
59 | ```console
60 | $ gsx style --help
61 | Usage: gsx style [OPTIONS]
62 |
63 | Get style in Geoserver
64 |
65 | Options:
66 | --request [sync|async] [default: requestEnum._sync]
67 | --url TEXT Geoserver REST URL [default:
68 | http://127.0.0.1:8080/geoserver/rest/]
69 | --style TEXT Style name [required]
70 | --password TEXT Geoserver Password [default: GeoServer]
71 | --username TEXT Geoserver username [default: admin]
72 | --help Show this message and exit.
73 |
74 | ```
75 |
76 |
77 | This command takes an additional parameter of name of the style.
78 |
79 | ## Get single style information
80 |
81 |
82 | ```console
83 | $ gsx style --style p
84 | oi
85 | {"style": {"name": "poi", "format": "sld", "languageVersion": {"version": "1.0.0"},
86 | "filename": "poi.sld"}}
87 | ```
88 |
89 |
90 |
--------------------------------------------------------------------------------
/docs/pages/cli/vector-store.md:
--------------------------------------------------------------------------------
1 | # Vector Stores
2 |
3 | `geoserverx` allows users to access all/one vector stores from GeoServer. As of now, `geoserverx` also supports new vector store creation for `shapefile` and `gpkg` data
4 |
5 | !!! get "Get started"
6 | To start using `geoserverx` using command line, activate the Environment where package is installed and use `gsx` command
7 |
8 |
9 | ## Paramters for all Vector stores in Workspace command
10 |
11 |
12 | ```console
13 | $ gsx vector-st-wp --help
14 | Usage: gsx vector-st-wp [OPTIONS]
15 |
16 | Get vector stores in specific workspaces
17 | Options:
18 | --request [sync|async] [default: requestEnum._sync]
19 | --workspace TEXT Workspace name [required]
20 | --url TEXT Geoserver REST URL [default:
21 | http://127.0.0.1:8080/geoserver/rest/]
22 | --password TEXT Geoserver Password [default: GeoServer]
23 | --username TEXT Geoserver username [default: admin]
24 | --help Show this message and exit.
25 | ```
26 |
27 |
28 | As listed above, `vector-st-wp` command accepts following parameters.
29 |
30 | * request type ( sync or async )
31 | * url - Geoserver REST URL
32 | * password - Password for GeoServer
33 | * username - Username for GeoServer
34 |
35 | All these parameters have default value setup which will work for local default installation. Apart from this `workspace` paramters must be added which aims at the workspace we are interested in
36 |
37 | ## Get all Vector stores
38 |
39 |
40 | ```console
41 | $ gsx vector-st-wp --workspace cesium
42 | {"dataStores": {"dataStore": [{"name": "mysqlllllll", "href":
43 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/datastores/mysqlllllll.json"}]}}
44 | ```
45 |
46 |
47 | ## Paramters for single Vector stores command
48 |
49 |
50 | ```console
51 | $ gsx vector-store --help
52 | Usage: gsx vector-store [OPTIONS]
53 |
54 | Get vector store information in specific workspaces
55 |
56 | Options:
57 | --request [sync|async] [default: requestEnum._sync]
58 | --workspace TEXT Workspace name [required]
59 | --store TEXT Store name [required]
60 | --url TEXT Geoserver REST URL [default:
61 | http://127.0.0.1:8080/geoserver/rest/]
62 | --password TEXT Geoserver Password [default: GeoServer]
63 | --username TEXT Geoserver username [default: admin]
64 | --help Show this message and exit.
65 |
66 | ```
67 |
68 |
69 | This command takes an additional parameter of name of the store.
70 |
71 | ## Get single Vector store information
72 |
73 |
74 | ```console
75 | $ gsx vector-store --workspace cesium --store mysqlllllll
76 | {"dataStore": {"name": "mysqlllllll", "description": null, "enabled": true, "workspace":
77 | {"name": "cesium", "href":
78 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium.json"}, "connectionParameters":
79 | {"entry": [{"key": "Evictor run periodicity", "path": "300"}, {"key": "fetch size",
80 | "path": "1000"}, {"key": "Expose primary keys", "path": "false"}, {"key": "validate
81 | connections", "path": "true"}, {"key": "Connection timeout", "path": "20"}, {"key":
82 | "Batch insert size", "path": "1"}, {"key": "database", "path": "appsolicitous_dcra"},
83 | {"key": "port", "path": "3306"}, {"key": "passwd", "path":
84 | "crypt1:njsGJk9CEY8jiaqfSYyQGZeB9RLB2sh7"}, {"key": "storage engine", "path": "MyISAM"},
85 | {"key": "min connections", "path": "1"}, {"key": "dbtype", "path": "mysql"}, {"key":
86 | "host", "path": "23.29.118.44"}, {"key": "namespace", "path": "cesium"}, {"key": "max
87 | connections", "path": "10"}, {"key": "Evictor tests per run", "path": "3"}, {"key": "Test
88 | while idle", "path": "true"}, {"key": "user", "path": "appsolicitous_dcra"}, {"key": "Max
89 | connection idle time", "path": "300"}]}, "dateCreated": "2023-02-28 10:38:52.70 UTC",
90 | "dateModified": null, "featureTypes":
91 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/datastores/mysqlllllll/featuretyp
92 | es.json"}}
93 |
94 | ```
95 |
96 |
--------------------------------------------------------------------------------
/docs/pages/cli/workspace.md:
--------------------------------------------------------------------------------
1 | # Workspaces
2 |
3 | `geoserverx` allows users to access all/one workspace from GeoServer, along with ability to add new workspaces.
4 |
5 | !!! get "Get started"
6 | To start using `geoserverx` using command line, activate the Environment where package is installed and use `gsx` command
7 |
8 |
9 |
10 | ## Paramters for all workspaces command
11 |
12 |
13 |
14 | ```console
15 | $ gsx workspaces --help
16 |
17 | Usage: gsx workspaces [OPTIONS]
18 |
19 | Get all workspaces in the Geoserver
20 |
21 | Options:
22 | --request [sync|async] [default: requestEnum._sync]
23 | --url TEXT Geoserver REST URL [default:
24 | http://127.0.0.1:8080/geoserver/rest/]
25 | --password TEXT Geoserver Password [default: GeoServer]
26 | --username TEXT Geoserver username [default: admin]
27 | --help Show this message and exit.
28 | ```
29 |
30 |
31 | As listed above, `workspaces` command accepts four parameters.
32 |
33 | * request type ( sync or async )
34 | * url - Geoserver REST URL
35 | * password - Password for GeoServer
36 | * username - Username for GeoServer
37 |
38 | All these parameters have default value setup which will work for local default installation
39 |
40 | ## Get all workspaces
41 |
42 |
43 | ```console
44 | $ gsx workspaces
45 |
46 | {"workspaces": {"workspace": [{"name": "cesium", "href":
47 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium.json"}]}}
48 | ```
49 |
50 |
51 | ## Get all workspaces of hosted GeoServer
52 |
53 |
54 | ```console
55 | $ gsx workspaces --url http://locahost:8080/geoserver/rest --password myPassword --username admin
56 | {"workspaces": {"workspace": [{"name": "giz", "href":
57 | "http://locahost:8080/geoserver/rest/workspaces/giz.json"}]}}
58 | ```
59 |
60 |
61 |
62 | ## Paramters to get single workspace command
63 |
64 |
65 |
66 | ```console
67 | $ gsx workspace --help
68 | Usage: gsx workspace [OPTIONS]
69 |
70 | Get workspace in the Geoserver
71 |
72 | Options:
73 | --request [sync|async] [default: requestEnum._sync]
74 | --workspace TEXT Workspace name [required]
75 | --url TEXT Geoserver REST URL [default:
76 | http://127.0.0.1:8080/geoserver/rest/]
77 | --password TEXT Geoserver Password [default: GeoServer]
78 | --username TEXT Geoserver username [default: admin]
79 | --help Show this message and exit.
80 | ```
81 |
82 |
83 | As listed above, `workspace` accepts `workspace` parameter as the name of workspace
84 |
85 |
86 | ## Get single workspaces
87 |
88 |
89 | ```console
90 | $ gsx workspace --workspace cesium
91 | {"workspace": {"name": "cesium", "isolated": false, "dateCreated": "2023-02-13
92 | 06:43:28.793 UTC", "dataStores":
93 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/datastores.json",
94 | "coverageStores":
95 | "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/coveragestores.json",
96 | "wmsStores": "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/wmsstores.json",
97 | "wmtsStores": "http://127.0.0.1:8080/geoserver/rest/workspaces/cesium/wmtsstores.json"}}
98 | ```
99 |
100 |
101 |
102 | ## Paramters for create workspace command
103 |
104 |
105 |
106 | ```console
107 | $ gsx create-workspace --help
108 | Usage: gsx create-workspace [OPTIONS]
109 |
110 | Add workspace in the Geoserver
111 |
112 | Options:
113 | --request [sync|async] [default: requestEnum._sync]
114 | --workspace TEXT Workspace name [required]
115 | --default / --no-default Make workspace default? [default: no-default]
116 | --isolated / --no-isolated Make workspace isolated? [default: no-isolated]
117 | --url TEXT Geoserver REST URL [default:
118 | http://127.0.0.1:8080/geoserver/rest/]
119 | --password TEXT Geoserver Password [default: GeoServer]
120 | --username TEXT Geoserver username [default: admin]
121 | --help Show this message and exit.
122 | ```
123 |
124 |
125 | As listed above, `create-workspace` command accepts parameters as follows
126 |
127 | * workspace - name of workspace
128 | * --default/--no-default - To keep workspace either default or not
129 | * --isolated/--no-isolated - To keep workspace either isolated or not
130 |
131 |
132 | ## Create single workspaces
133 |
134 |
135 | ```console
136 | $ gsx create-workspace --workspace mydefaultws --default
137 | code=201 response='Data added successfully'
138 | ```
139 |
--------------------------------------------------------------------------------
/docs/pages/sync/example.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | Here, we'll have a look at implementation `geoserverx` synchronous Class
4 |
5 | !!! get "Get started"
6 | To start using `geoserverx` in Sync mode, create a new instance of `SyncGeoServerX` Class
7 |
8 | ## Setup Class instance
9 |
10 | `SyncGeoServerX` Class has default username, password, url which points to default GeoServer settings.
11 | ```Python
12 | # Import class from package
13 | from geoserverx._sync.gsx import SyncGeoServerX
14 | # Create class Instance with default paramaters
15 | client = SyncGeoServerX()
16 | ```
17 | We'll assume connection to local GeoServer with default credentials
18 |
19 | ## Get all workspaces
20 |
21 | ```Python hl_lines="8" linenums="1"
22 | # Import Class from Package
23 | from geoserverx._sync.gsx import SyncGeoServerX
24 |
25 | def get_all_gs_workspaces(url, username, password):
26 | print("-------------start-----------------")
27 | # Setup Class Instance
28 | client = SyncGeoServerX(username, password,url)
29 | return client.get_all_workspaces()
30 |
31 | result = get_all_gs_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer')
32 | print(result.json())
33 | ''' Console
34 | -------------start-----------------
35 | {"workspaces": {"workspace": [{"name": "nondefaultws", "href": "http://localhost:8080/geoserver/rest/workspaces/nondefaultws.json"},
36 | {"name": "mydefaultws", "href": "http://localhost:8080/geoserver/rest/workspaces/mydefaultws.json"},
37 | {"name": "ajadasfasdf", "href": "http://localhost:8080/geoserver/rest/workspaces/ajadasfasdf.json"},
38 | {"name": "ajada", "href": "http://localhost:8080/geoserver/rest/workspaces/ajada.json"},
39 | {"name": "aja", "href": "http://localhost:8080/geoserver/rest/workspaces/aja.json"}, {"name": "cesium", "href": "http://localhost:8080/geoserver/rest/workspaces/cesium.json"}]}}
40 | '''
41 | ```
42 |
43 | ## Get Information about `cesium` workspace
44 |
45 | ```Python hl_lines="7"
46 | # Import Class from Package
47 | from geoserverx._sync.gsx import SyncGeoServerX
48 |
49 | def get_single_workspaces(url, username, password,workspace):
50 | print("-------------start-----------------")
51 | # Setup Class Instance
52 | client = SyncGeoServerX(username, password,url)
53 | return client.get_workspace(workspace)
54 |
55 | result = get_single_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',workspace='cesium')
56 |
57 | print(result.json())
58 | ''' Console
59 | -------------start-----------------
60 | {"workspace": {"name": "cesium", "isolated": false, "dateCreated": "2023-02-13 06:43:28.793 UTC", "dataStores": "http://localhost:8080/geoserver/rest/workspaces/cesium/datastores.json", "coverageStores": "http://localhost:8080/geoserver/rest/workspaces/cesium/coveragestores.json", "wmsStores": "http://localhost:8080/geoserver/rest/workspaces/cesium/wmsstores.json", "wmtsStores": "http://localhost:8080/geoserver/rest/workspaces/cesium/wmtsstores.json"}}
61 | '''
62 | ```
63 |
64 | ## Create New workspaces
65 |
66 | * MyDefault - Default and not Isolated
67 | * MyHidden - Not Default and Isolated
68 | * MySimple - Not Default and not Isolated
69 |
70 | ```Python hl_lines="11 12 14 15 17 18"
71 |
72 | # Import Class from Package
73 | from geoserverx._sync.gsx import SyncGeoServerX
74 |
75 |
76 | def create_single_workspaces(url, username, password,workspace,default,isolated):
77 | print("-------------start-----------------")
78 | # Setup Class Instance
79 | client = SyncGeoServerX(username, password,url)
80 | return client.create_workspace(workspace, default,isolated)
81 |
82 | first = create_single_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
83 | workspace='MyDefault',default=True,isolated= False)
84 | print(first.json())
85 | second = create_single_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
86 | workspace='MyHidden',default=False,isolated= True)
87 | print(second.json())
88 | third = create_single_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
89 | workspace='MySimple',default=False,isolated= False)
90 | print(third.json())
91 | ''' Console
92 | -------------start-----------------
93 | -------------start-----------------
94 | -------------start-----------------
95 | code=201 response='Data added successfully'
96 | code=201 response='Data added successfully'
97 | code=201 response='Data added successfully'
98 | '''
99 | ```
100 | 
101 |
102 | ## Get all Vector stores in `cesium` workspace
103 |
104 | ```Python hl_lines="8"
105 |
106 | # Import Class from Package
107 | from geoserverx._sync.gsx import SyncGeoServerX
108 |
109 | def get_all_vector_workspaces(url, username, password,workspace):
110 | print("-------------start-----------------")
111 | # Setup Class Instance
112 | client = SyncGeoServerX(username, password,url)
113 | return client.get_vector_stores_in_workspaces(workspace)
114 |
115 | result = get_vector_store(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
116 | workspace='cesium')
117 | print(result.json())
118 |
119 | ''' Console
120 | -------------start-----------------
121 | {"dataStores": {"dataStore": [{"name": "mysqlllllll", "href": "http://localhost:8080/geoserver/rest/workspaces/cesium/datastores/mysqlllllll.json"}]}}
122 | '''
123 | ```
124 |
125 | ## Get Information of Vector store `mysqldb` in `cesium` workspace
126 |
127 | ```Python hl_lines="8"
128 |
129 | # Import Class from Package
130 | from geoserverx._sync.gsx import SyncGeoServerX
131 |
132 | def get_info_vector_workspaces(url, username, password,workspace,store):
133 | print("-------------start-----------------")
134 | # Setup Class Instance
135 | client = SyncGeoServerX(username, password,url)
136 | return client.get_vector_store(workspace,store)
137 |
138 | result = get_info_vector_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
139 | workspace='cesium',store='mysqldb' )
140 | print(result.json())
141 |
142 | ''' Console
143 | -------------start-----------------
144 | {"dataStore": {"name": "mysqldb", "description": null, "enabled": true,
145 | "workspace": {"name": "cesium", "href": "http://localhost:8080/geoserver/rest/workspaces/cesium.json"},
146 | "connectionParameters": {"entry": [{"key": "Evictor run periodicity", "path": "300"}, {"key": "fetch size", "path": "1000"}, {"key": "Expose primary keys", "path": "false"}, {"key": "validate connections", "path": "true"}, {"key": "Connection timeout", "path": "20"}, {"key": "Batch insert size", "path": "1"}, {"key": "database", "path": "appsolicitous_dcra"}, {"key": "port", "path": "3306"}, {"key": "passwd", "path": "crypt1:jxnPgWTsBoUAVin1wtCLWgIqmZ4DSEWx"}, {"key": "storage engine", "path": "MyISAM"}, {"key": "min connections", "path": "1"}, {"key": "dbtype", "path": "mysql"}, {"key": "host", "path": "23.29.118.44"}, {"key": "namespace", "path": "cesium"}, {"key": "max connections", "path": "10"}, {"key": "Evictor tests per run", "path": "3"}, {"key": "Test while idle", "path": "true"}, {"key": "user", "path": "appsolicitous_dcra"}, {"key": "Max connection idle time", "path": "300"}]}, "dateCreated": "2023-02-28 10:38:52.70 UTC", "dateModified": null,
147 | "featureTypes": "http://localhost:8080/geoserver/rest/workspaces/cesium/datastores/mysqlllllll/featuretypes.json"}}
148 | '''
149 | ```
150 |
151 | ## Create new shapefile Vector store in `cesium` workspace with name `natural_earth`
152 |
153 | Create new store using Shapefile available at given path
154 | ```Python hl_lines="8"
155 | from geoserverx._sync.gsx import SyncGeoServerX
156 |
157 | def add_vector_workspaces(url, username, password,workspace,store,file):
158 | print("-------------start-----------------")
159 | # Setup Class Instance
160 | client = SyncGeoServerX(username, password,url)
161 | return client.create_file_store(workspace, store, file, service_type='shapefile')
162 |
163 | result = add_vector_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
164 | workspace='cesium',store='natural_earth', file='/Users/krishnaglodha/Downloads/ne_10m_populated_places_simple/ne_10m_populated_places_simple.shp' )
165 | print(result.json())
166 | ''' Console
167 | -------------start-----------------
168 | {"code": 201, "response": "Data added successfully"}
169 | '''
170 | ```
171 |
172 | 
173 |
174 | ## Get all Raster stores in `cesium` workspace
175 |
176 | ```Python hl_lines="8"
177 | # Import Class from Package
178 | from geoserverx._sync.gsx import SyncGeoServerX
179 |
180 | def get_all_raster_workspaces(url, username, password,workspace):
181 | print("-------------start-----------------")
182 | # Setup Class Instance
183 | client = SyncGeoServerX(username, password,url)
184 | return client.get_raster_stores_in_workspaces(workspace)
185 |
186 | result = get_all_raster_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
187 | workspace='cesium')
188 | print(result.json())
189 |
190 | ''' Console
191 | -------------start-----------------
192 | {"coverageStores": {"coverageStore": [{"name": "dem", "href": "http://localhost:8080/geoserver/rest/workspaces/cesium/coveragestores/dem.json"}, {"name": "dsm", "href": "http://localhost:8080/geoserver/rest/workspaces/cesium/coveragestores/dsm.json"}, {"name": "ortho", "href": "http://localhost:8080/geoserver/rest/workspaces/cesium/coveragestores/ortho.json"}]}}
193 | '''
194 | ```
195 |
196 | ## Get Information of Raster store `dsm` in `cesium` workspace
197 |
198 | ```Python hl_lines="8"
199 | # Import Class from Package
200 | from geoserverx._sync.gsx import SyncGeoServerX
201 |
202 | def get_info_raster_workspaces(url, username, password,workspace,store):
203 | print("-------------start-----------------")
204 | # Setup Class Instance
205 | client = SyncGeoServerX(username, password,url)
206 | return client.get_raster_store(workspace,store)
207 |
208 | result = get_info_raster_workspaces(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',
209 | workspace='cesium',store='dsm' )
210 | print(result.json())
211 |
212 | ''' Console
213 | -------------start-----------------
214 | {"coverageStore": {"name": "dsm", "description": null, "enabled": true, "workspace": {"name": "cesium", "href": "http://localhost:8080/geoserver/rest/workspaces/cesium.json"}, "url": "file:///Users/krishnaglodha/Desktop/IGI_DATA/DSM/IGI_DSM1m1.tif", "coverages": "http://localhost:8080/geoserver/rest/workspaces/cesium/coveragestores/dsm/coverages.json", "dateCreated": "2023-02-23 13:39:48.417 UTC", "metadata": null}}
215 | '''
216 | ```
217 |
218 | ## Get all Styles in GeoServer
219 |
220 | ```Python hl_lines="8"
221 | # Import Class from Package
222 | from geoserverx._sync.gsx import SyncGeoServerX
223 |
224 | def get_all_styles(url, username, password):
225 | print("-------------start-----------------")
226 |
227 | client = SyncGeoServerX(username, password,url)
228 | return client.get_all_styles()
229 |
230 | result = get_all_styles(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer' )
231 | print(result.json())
232 |
233 | ''' Console
234 | -------------start-----------------
235 | {"styles": {"style": [{"name": "burg", "href": "http://localhost:8080/geoserver/rest/styles/burg.json"}, {"name": "capitals", "href": "http://localhost:8080/geoserver/rest/styles/capitals.json"}, {"name": "cite_lakes", "href": "http://localhost:8080/geoserver/rest/styles/cite_lakes.json"}, {"name": "dem", "href": "http://localhost:8080/geoserver/rest/styles/dem.json"}, {"name": "generic", "href": "http://localhost:8080/geoserver/rest/styles/generic.json"}, {"name": "giant_polygon", "href": "http://localhost:8080/geoserver/rest/styles/giant_polygon.json"}, {"name": "grass", "href": "http://localhost:8080/geoserver/rest/styles/grass.json"}, {"name": "green", "href": "http://localhost:8080/geoserver/rest/styles/green.json"}, {"name": "line", "href": "http://localhost:8080/geoserver/rest/styles/line.json"}, {"name": "poi", "href": "http://localhost:8080/geoserver/rest/styles/poi.json"}, {"name": "point", "href": "http://localhost:8080/geoserver/rest/styles/point.json"}, {"name": "poly_landmarks", "href": "http://localhost:8080/geoserver/rest/styles/poly_landmarks.json"}, {"name": "polygon", "href": "http://localhost:8080/geoserver/rest/styles/polygon.json"}, {"name": "pophatch", "href": "http://localhost:8080/geoserver/rest/styles/pophatch.json"}, {"name": "population", "href": "http://localhost:8080/geoserver/rest/styles/population.json"}, {"name": "rain", "href": "http://localhost:8080/geoserver/rest/styles/rain.json"}, {"name": "raster", "href": "http://localhost:8080/geoserver/rest/styles/raster.json"}, {"name": "restricted", "href": "http://localhost:8080/geoserver/rest/styles/restricted.json"}, {"name": "simple_roads", "href": "http://localhost:8080/geoserver/rest/styles/simple_roads.json"}, {"name": "simple_streams", "href": "http://localhost:8080/geoserver/rest/styles/simple_streams.json"}, {"name": "tiger_roads", "href": "http://localhost:8080/geoserver/rest/styles/tiger_roads.json"}]}}
236 | '''
237 | ```
238 |
239 | ## Get Single Style in GeoServer
240 |
241 | ```Python hl_lines="8"
242 | # Import Class from Package
243 | from geoserverx._sync.gsx import SyncGeoServerX
244 |
245 | def get_style_info(url, username, password,style):
246 | print("-------------start-----------------")
247 |
248 | client = SyncGeoServerX(username, password,url)
249 | return client.get_style(style)
250 |
251 | result = get_style_info(url='http://localhost:8080/geoserver/rest/',username='admin', password='GeoServer',style='poi' )
252 | print(result.json())
253 | ''' Console
254 | -------------start-----------------
255 | {"style": {"name": "poi", "format": "sld", "languageVersion": {"version": "1.0.0"}, "filename": "poi.sld"}}
256 | '''
257 | ```
258 |
--------------------------------------------------------------------------------
/docs/pages/sync/index.md:
--------------------------------------------------------------------------------
1 | # Synchronous way of using geoserverx
2 |
3 | `geoserverx` allows user to call methods synchronously.
4 |
5 | !!! get "Get started"
6 | To start using `geoserverx` in Sync mode, create a new instance of `SyncGeoServerX` Class
7 |
8 | ## Setup Class instance
9 |
10 | `SyncGeoServerX` Class has default username, password, url which points to default GeoServer settings.
11 | ```Python
12 | # Import class from package
13 | from geoserverx._sync.gsx import SyncGeoServerX
14 | # Create class Instance with default paramaters
15 | client = SyncGeoServerX()
16 | ```
17 |
18 | These paramaters however can be changed as follows
19 | ```Python
20 | # Import class from package
21 | from geoserverx._sync.gsx import SyncGeoServerX
22 | # Create class Instance with custom paramaters
23 | client = SyncGeoServerX(username='mygeos', password='SecuredPass',url='http://127.0.0.1:9090/geoserver/rest/')
24 | ```
25 |
26 | This class can also be used as context manager to manage the opening and closing connection automatically.
27 | ```Python
28 | # Import class from package
29 | from geoserverx._sync.gsx import SyncGeoServerX
30 | # Create class Instance with custom paramaters
31 | client = SyncGeoServerX(username='mygeos', password='SecuredPass',url='http://127.0.0.1:9090/geoserver/rest/')
32 |
33 | #Using with as
34 | with client as cl :
35 | response = cl.get_all_workspaces()
36 | ```
--------------------------------------------------------------------------------
/docs/pages/sync/raster-store.md:
--------------------------------------------------------------------------------
1 | # Raster Stores
2 |
3 | `geoserverx` allows users to access all/one raster stores from GeoServer
4 |
5 |
6 | ## Get all raster stores
7 | This command fetches all Vector store available in given workspace from GeoServer.
8 |
9 | ```py
10 | # Get all raster stores available in `cite` workspace
11 | client.get_raster_stores_in_workspaces('cite')
12 | ```
13 |
14 |
15 | ## Get single raster store
16 |
17 | This command fetches all Information about raster store available in given workspace from GeoServer.
18 |
19 | ```Python
20 | # Get all information about `image` raster stores available in `cite` workspace
21 |
22 | client.get_raster_store(workspace='cite', store='image')
23 | ```
24 |
--------------------------------------------------------------------------------
/docs/pages/sync/style.md:
--------------------------------------------------------------------------------
1 | # Style
2 |
3 |
4 | ## Get all Styles
5 |
6 | This command fetches all Styles available in GeoServer.
7 |
8 | ```Python
9 | # Get all styles available in GeoServer
10 | client.get_all_styles()
11 | ```
12 |
13 |
14 | ## Get single Style
15 |
16 | This command fetches information about particular Style from GeoServer.
17 |
18 | ```py
19 | # Get information about `population` style from GeoServer
20 | client.get_style('population')
21 | ```
22 |
--------------------------------------------------------------------------------
/docs/pages/sync/vector-store.md:
--------------------------------------------------------------------------------
1 | # Vector Stores
2 |
3 | `geoserverx` allows users to access all/one vector stores from GeoServer. As of now, `geoserverx` also supports new vector store creation for `shapefile` and `gpkg` data
4 |
5 | ## Get all Vector stores
6 | This command fetches all Vector store available in given workspace from GeoServer.
7 |
8 | ```Python
9 | # Get all vector stores available in `cite` workspace
10 | client.get_vector_stores_in_workspaces('cite')
11 | ```
12 |
13 |
14 |
15 | ## Get single Vector store
16 | This command fetches all Information about Vector store available in given workspace from GeoServer.
17 |
18 | ```Python
19 | # Get all information about `shape` vector stores available in `cite` workspace
20 |
21 | client.get_vector_store(workspace='cite', store='shape')
22 | ```
23 |
24 |
25 | ## Create new shapefile Vector store
26 | Use this command to create new Vector store based on `shapefile` path.
27 |
28 | ```Python
29 | # Create new store in `cite` workspace with name `shape` and using `path/for/shapefile` as local shapefile path
30 | client.create_file_store(workspace='cite', store='shape', file='path/for/shapefile', service_type='shapefile')
31 | ```
32 |
33 |
34 | ## Create new geopackage Vector store
35 | Use this command to create new Vector store based on `Geopackage` path.
36 |
37 | ```Python
38 | # Create new store in `cite` workspace with name `shape` and using `path/for/gpkg` as local Geopackage path
39 | client.create_file_store(workspace='cite', store='shape', file='path/for/gpkg', service_type='gpkg')
40 | ```
41 |
42 | ## Create new PostGIS Vector store
43 | Use this command to create new Vector store based on `PostGIS` connection.
44 |
45 | ```Python
46 | # Create new store in `cite` workspace with name `pg` and using `PostgreSQL` credentials
47 | client.create_pg_store(
48 | name="pg",
49 | workspace="cite",
50 | host="localhost",
51 | port=5432,
52 | username="XXXXXXXX",
53 | password="XXXXXXXX",
54 | database="test")
55 | ```
56 |
57 | ## Get all Vector layers
58 | This command fetches all Vector layers available in given workspace from GeoServer.
59 |
60 | ```Python
61 | # Get all vector layers available in `cite` workspace
62 | client.get_all_layers(workspace='cite')
63 | ```
64 |
65 | ## Get single Vector layer
66 | This command fetches all Information about Vector layer available in given workspace from GeoServer.
67 |
68 | ```Python
69 | # Get all information about `roads` vector layers available in `cite` workspace
70 | client.get_vector_layer(workspace='cite', store='shape', layer='roads')
71 | ```
72 |
--------------------------------------------------------------------------------
/docs/pages/sync/workspace.md:
--------------------------------------------------------------------------------
1 | # Workspaces
2 |
3 | `geoserverx` allows users to access all/one workspace from GeoServer, along with ability to add new workspaces.
4 |
5 | ## Get all workspaces
6 | This command fetches all workspaces available in GeoServer. No paramters are required to be passed.
7 |
8 | ```Python
9 | # Get all workspaces in GeoServer
10 | client.get_all_workspaces()
11 | ```
12 |
13 | ## Get single workspace
14 | This command fetches workspace with paramter as name of it from GeoServer.
15 | ```Python
16 | # Get workspace with name `cite`
17 | client.get_workspace('cite')
18 | ```
19 |
20 | ## Create workspace
21 | This command allows user to create new workspace.
22 | Creating new workspace requires following parameters
23 |
24 | * Name `str` : To define Name of the workspace
25 | * default `bool` : To define whether to keep workspace as default or not
26 | * Isolated `bool` : To define whether to keep workspace Isolated or not
27 |
28 | ```Python
29 | #Create new workspace with name `my_wrkspc` , make it Default and Isolated
30 | client.create_workspace(name='my_wrkspc',default=True,Isolated=True)
31 | ```
32 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: geoserverx
2 | site_description: "geoserverx is a modern python and CLI package for communicating with Geoserver."
3 | site_url: https://geobeyond.github.io/geoserverx/
4 | site_author: Geobeyond
5 | copyright: "© 2024 Geobeyond"
6 | theme:
7 | name: material
8 | icon:
9 | logo: material/package-variant
10 | favicon: material/package-variant
11 | palette:
12 | primary: white
13 | features:
14 | - content.code.copy
15 | - navigation.tabs
16 | - navigation.tabs.sticky
17 | repo_url: https://github.com/geobeyond/geoserverx
18 | plugins:
19 | - social
20 | - search
21 | nav:
22 | - Home: index.md
23 | - Sync :
24 | - pages/sync/index.md
25 | - Workspaces : pages/sync/workspace.md
26 | - Vector Data : pages/sync/vector-store.md
27 | - Raster Data : pages/sync/raster-store.md
28 | - Style : pages/sync/style.md
29 | - Example : pages/sync/example.md
30 | - Async :
31 | - pages/async/index.md
32 | - Workspaces : pages/async/workspace.md
33 | - Vector Data : pages/async/vector-store.md
34 | - Raster Data : pages/async/raster-store.md
35 | - Style : pages/async/style.md
36 | - Example : pages/async/example.md
37 | - Command Line :
38 | - pages/cli/index.md
39 | - Workspaces : pages/cli/workspace.md
40 | - Vector Data : pages/cli/vector-store.md
41 | - Raster Data : pages/cli/raster-store.md
42 | - Style : pages/cli/style.md
43 | - Example : pages/cli/example.md
44 | markdown_extensions:
45 | - meta # option to add some meta tags on top, title, author, date, etc
46 | - admonition # adds the note, question, tip boxes, eg: !!! tip "my tip"
47 | - pymdownx.details # advanced collapsible panels
48 | - pymdownx.superfences # advanced features; such as line number, flow chart, python shell
49 | - footnotes # notes bottom of page
50 | - attr_list # used to size images
51 | - md_in_html # used to size images
52 | - pymdownx.tabbed:
53 | alternate_style: true
54 |
55 | extra_css:
56 | # pygeoapi primary color with light and dark variations from material.io
57 | # https://material.io/resources/color/#!/?view.left=0&view.right=1
58 | - assets/stylesheets/termynal.css
59 | - assets/stylesheets/custom.css
60 |
61 | extra_javascript:
62 | - assets/javascripts/termynal.js
63 | - assets/javascripts/custom.js
64 |
65 | extra:
66 | generator: false
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "geoserverx"
3 | version = "0.0.4"
4 | description = "geoserverx is a modern python and CLI package for communicating with Geoserver."
5 | authors = ["krishnaglodha "]
6 | readme = "README.md"
7 |
8 | [tool.poetry.scripts]
9 | gsx = "geoserverx.cli.cli:app"
10 |
11 | [tool.poetry.dependencies]
12 | python = "^3.9"
13 | httpx = "^0.24.1"
14 | pydantic = "2.8.2"
15 | typer = "^0.4.1"
16 | rich = "^12.5.1"
17 |
18 | [tool.poetry.group.dev.dependencies]
19 | pytest = "^7.1.2"
20 | respx = "^0.20.1"
21 | mypy = "*"
22 | ruff = "^0.6.3"
23 | black = "^24.8.0"
24 | isort = "^5.10.1"
25 | pytest-asyncio = "^0.21.0"
26 | anyio = {extras = ["trio"], version = "^3.3.4"}
27 |
28 | [tool.poetry.group.docs]
29 | optional = true
30 | [tool.poetry.group.docs.dependencies]
31 | mkdocs-material = "^9.5.0"
32 | pillow = "*"
33 | CairoSVG= "*"
34 |
35 |
36 | [build-system]
37 | requires = ["poetry-core>=1.0.0"]
38 | build-backend = "poetry.core.masonry.api"
39 |
--------------------------------------------------------------------------------
/social/2be55c97396bb6811061ba3194e30e4d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/2be55c97396bb6811061ba3194e30e4d.png
--------------------------------------------------------------------------------
/social/53d44762e0d6016d6de24215dfdd6676.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/53d44762e0d6016d6de24215dfdd6676.png
--------------------------------------------------------------------------------
/social/548c1d117ad531ea470f4c6f675062a4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/548c1d117ad531ea470f4c6f675062a4.png
--------------------------------------------------------------------------------
/social/92f5fd10f5766422436223bc15a7c8ef.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/92f5fd10f5766422436223bc15a7c8ef.png
--------------------------------------------------------------------------------
/social/9eef2d77b3d71b7533499b2f651f63d4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/9eef2d77b3d71b7533499b2f651f63d4.png
--------------------------------------------------------------------------------
/social/a05d671ab8d4f043eadc8e9f00b2feec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/a05d671ab8d4f043eadc8e9f00b2feec.png
--------------------------------------------------------------------------------
/social/ab5486e06e38b9b0518f6930cc6a8248.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/ab5486e06e38b9b0518f6930cc6a8248.png
--------------------------------------------------------------------------------
/social/df41e99874754a66cab3b04028b0eb6b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/df41e99874754a66cab3b04028b0eb6b.png
--------------------------------------------------------------------------------
/social/f510d45f3b407da60e907251443fcf59.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/f510d45f3b407da60e907251443fcf59.png
--------------------------------------------------------------------------------
/social/fonts/Roboto/Black Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Black Italic.ttf
--------------------------------------------------------------------------------
/social/fonts/Roboto/Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Black.ttf
--------------------------------------------------------------------------------
/social/fonts/Roboto/Bold Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Bold Italic.ttf
--------------------------------------------------------------------------------
/social/fonts/Roboto/Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Bold.ttf
--------------------------------------------------------------------------------
/social/fonts/Roboto/Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Italic.ttf
--------------------------------------------------------------------------------
/social/fonts/Roboto/Light Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Light Italic.ttf
--------------------------------------------------------------------------------
/social/fonts/Roboto/Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Light.ttf
--------------------------------------------------------------------------------
/social/fonts/Roboto/Medium Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Medium Italic.ttf
--------------------------------------------------------------------------------
/social/fonts/Roboto/Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Medium.ttf
--------------------------------------------------------------------------------
/social/fonts/Roboto/Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Regular.ttf
--------------------------------------------------------------------------------
/social/fonts/Roboto/Thin Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Thin Italic.ttf
--------------------------------------------------------------------------------
/social/fonts/Roboto/Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/social/fonts/Roboto/Thin.ttf
--------------------------------------------------------------------------------
/src/geoserverx/__init__.py:
--------------------------------------------------------------------------------
1 | from . import _async, _sync, models, utils
2 |
3 | __version__ = "0.1.0"
4 | __author__ = "krishnaglodha "
5 | __all__ = [_sync, _async, utils, models]
6 |
--------------------------------------------------------------------------------
/src/geoserverx/_async/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/src/geoserverx/_async/__init__.py
--------------------------------------------------------------------------------
/src/geoserverx/_async/gsx.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass
2 | from typing import Optional, Union
3 |
4 | import httpx
5 |
6 | from geoserverx.models.coverages_store import CoveragesStoreModel, CoveragesStoresModel
7 | from geoserverx.models.data_store import (
8 | CreateDataStoreModel,
9 | CreateStoreItem,
10 | DataStoreModel,
11 | DataStoresModel,
12 | MainCreateDataStoreModel,
13 | )
14 | from geoserverx.models.geofence import NewRule, Rule, RulesResponse
15 | from geoserverx.models.gs_response import GSResponse
16 | from geoserverx.models.layer_group import LayerGroupsModel
17 | from geoserverx.models.layers import LayerModel, LayersModel
18 | from geoserverx.models.style import AllStylesModel, StyleModel
19 | from geoserverx.models.workspace import (
20 | NewWorkspace,
21 | NewWorkspaceInfo,
22 | WorkspaceModel,
23 | WorkspacesModel,
24 | )
25 | from geoserverx.utils.auth import GeoServerXAuth
26 | from geoserverx.utils.custom_exceptions import GSModuleNotFound
27 | from geoserverx.utils.enums import GSResponseEnum
28 | from geoserverx.utils.errors import GeoServerXError
29 | from geoserverx.utils.http_client import AsyncClient
30 | from geoserverx.utils.logger import std_out_logger
31 | from geoserverx.utils.services.async_datastore import (
32 | AddDataStoreProtocol,
33 | CreateFileStore,
34 | GPKGfileStore,
35 | ShapefileStore,
36 | )
37 |
38 |
39 | @dataclass
40 | class AsyncGeoServerX:
41 | """
42 | Async Geoserver client
43 | """
44 |
45 | username: str = "admin"
46 | password: str = "geoserver"
47 | url: str = "http://127.0.0.1:8080/geoserver/rest/"
48 | head = {"Content-Type": "application/json"}
49 |
50 | def __post_init__(self):
51 | if not self.username and not self.password and not self.url:
52 | raise GeoServerXError(0, "Username, Password and URL is missing")
53 | elif not self.username or self.username == "":
54 | raise GeoServerXError(0, "Username is missing")
55 | elif not self.password or self.password == "":
56 | raise GeoServerXError(0, "password is missing")
57 | elif not self.url or self.url == "":
58 | raise GeoServerXError(0, "URL is missing")
59 | self.http_client = AsyncClient(
60 | base_url=self.url,
61 | auth=(self.username, self.password),
62 | )
63 |
64 | async def __aenter__(self) -> "AsyncGeoServerX":
65 | return self
66 |
67 | async def __aexit__(self, exc_t, exc_v, exc_tb) -> None:
68 | await self.close()
69 |
70 | async def close(self) -> None:
71 | await self.http_client.aclose()
72 |
73 | @staticmethod
74 | def from_auth(
75 | auth: GeoServerXAuth,
76 | ) -> "AsyncGeoServerX":
77 | return AsyncGeoServerX(auth.username, auth.password, auth.url)
78 |
79 | def response_recognise(self, r) -> GSResponse:
80 | if r == 401:
81 | resp = GSResponseEnum._401.value
82 | elif r == 500:
83 | resp = GSResponseEnum._500.value
84 | elif r == 503:
85 | resp = GSResponseEnum._503.value
86 | elif r == 404:
87 | resp = GSResponseEnum._404.value
88 | elif r == 403:
89 | resp = GSResponseEnum._403.value
90 | elif r == 201:
91 | resp = GSResponseEnum._201.value
92 | elif r == 200:
93 | resp = GSResponseEnum._200.value
94 | elif r == 409:
95 | resp = GSResponseEnum._409.value
96 | return GSResponse.model_validate(resp)
97 |
98 | # check if certain module/plugin exists in geoserver
99 | async def check_modules(self, name) -> Union[bool, GSResponse]:
100 | Client = self.http_client
101 | try:
102 | response = await Client.get("about/status.json")
103 | response.raise_for_status() # Raises an HTTPError for bad responses (4xx and 5xx)
104 |
105 | # Extract and check the modules
106 | modules = [
107 | item["name"].lower() for item in response.json()["statuss"]["status"]
108 | ]
109 | if name.lower() in modules:
110 | return True
111 | else:
112 | # Raise exception if the plugin is not found
113 | raise GSModuleNotFound(f"'{name}' plugin not found")
114 |
115 | except httpx.HTTPStatusError as e:
116 | # Handle HTTP errors (e.g., 4xx, 5xx)
117 | return self.response_recognise(e.response.status_code)
118 | except httpx.RequestError as e:
119 | # Handle other request errors (e.g., network problems)
120 | return self.response_recognise(e.response.status_code)
121 | except GSModuleNotFound as e:
122 | # Handle Module not found exception
123 | return GSResponse(code=412, response=str(e))
124 |
125 | # Get all workspaces
126 | async def get_all_workspaces(self) -> Union[WorkspacesModel, GSResponse]:
127 | Client = self.http_client
128 | responses = await Client.get("workspaces")
129 | if responses.status_code == 200:
130 | return WorkspacesModel.model_validate(responses.json())
131 | else:
132 | results = self.response_recognise(responses.status_code)
133 | return results
134 |
135 | # Get specific workspaces
136 | async def get_workspace(self, workspace: str) -> Union[WorkspaceModel, GSResponse]:
137 | Client = self.http_client
138 | responses = await Client.get(f"workspaces/{workspace}")
139 | if responses.status_code == 200:
140 | return WorkspaceModel.model_validate(responses.json())
141 | else:
142 | results = self.response_recognise(responses.status_code)
143 | return results
144 |
145 | # Create workspace
146 | async def create_workspace(
147 | self, name: str, default: bool = False, Isolated: bool = False
148 | ) -> GSResponse:
149 | Client = self.http_client
150 | payload: NewWorkspace = NewWorkspace(
151 | workspace=NewWorkspaceInfo(name=name, isolated=Isolated)
152 | )
153 | responses = await Client.post(
154 | f"workspaces?default={default}",
155 | data=payload.model_dump_json(),
156 | headers=self.head,
157 | )
158 | results = self.response_recognise(responses.status_code)
159 | return results
160 |
161 | # Get vector stores in specific workspaces
162 | async def get_vector_stores_in_workspaces(self, workspace: str) -> DataStoresModel:
163 | Client = self.http_client
164 | responses = await Client.get(f"workspaces/{workspace}/datastores")
165 | if responses.status_code == 200:
166 | return DataStoresModel.model_validate(responses.json())
167 | else:
168 | results = self.response_recognise(responses.status_code)
169 | return results
170 |
171 | # Get raster stores in specific workspaces
172 | async def get_raster_stores_in_workspaces(
173 | self, workspace: str
174 | ) -> CoveragesStoresModel:
175 | Client = self.http_client
176 | responses = await Client.get(f"workspaces/{workspace}/coveragestores")
177 | if responses.status_code == 200:
178 | return CoveragesStoresModel.model_validate(responses.json())
179 | else:
180 | results = self.response_recognise(responses.status_code)
181 | return results
182 |
183 | # Get vector store information in specific workspaces
184 | async def get_vector_store(self, workspace: str, store: str) -> DataStoreModel:
185 | url = f"workspaces/{workspace}/datastores/{store}.json"
186 | Client = self.http_client
187 | responses = await Client.get(url)
188 | if responses.status_code == 200:
189 | return DataStoreModel.model_validate(responses.json())
190 | else:
191 | results = self.response_recognise(responses.status_code)
192 | return results
193 |
194 | # Get raster store information in specific workspaces
195 | async def get_raster_store(self, workspace: str, store: str) -> CoveragesStoreModel:
196 | url = f"workspaces/{workspace}/coveragestores/{store}.json"
197 | Client = self.http_client
198 | responses = await Client.get(url)
199 | if responses.status_code == 200:
200 | return CoveragesStoreModel.model_validate(responses.json())
201 | else:
202 | results = self.response_recognise(responses.status_code)
203 | return results
204 |
205 | # Get all styles in GS
206 | async def get_all_styles(self) -> AllStylesModel:
207 | Client = self.http_client
208 | responses = await Client.get("styles")
209 | if responses.status_code == 200:
210 | return AllStylesModel.model_validate(responses.json())
211 | else:
212 | results = self.response_recognise(responses.status_code)
213 | return results
214 |
215 | # Get specific style in GS
216 | async def get_style(self, style: str) -> StyleModel:
217 | Client = self.http_client
218 | responses = await Client.get(f"styles/{style}.json")
219 | if responses.status_code == 200:
220 | return StyleModel.model_validate(responses.json())
221 | else:
222 | results = self.response_recognise(responses.status_code)
223 | return results
224 |
225 | # Add postgres db
226 | async def create_pg_store(
227 | self,
228 | name: str,
229 | workspace: str,
230 | host: str,
231 | port: int,
232 | username: str,
233 | password: str,
234 | database: str,
235 | ) -> GSResponse:
236 | payload = MainCreateDataStoreModel(
237 | dataStore=CreateDataStoreModel(
238 | name=name,
239 | connectionParameters=CreateStoreItem(
240 | host=host,
241 | port=port,
242 | database=database,
243 | user=username,
244 | passwd=password,
245 | dbtype="postgis",
246 | ).model_dump(exclude_none=True),
247 | )
248 | )
249 | Client = self.http_client
250 | responses = await Client.post(
251 | f"workspaces/{workspace}/datastores/",
252 | data=payload.model_dump_json(),
253 | headers=self.head,
254 | )
255 | results = self.response_recognise(responses.status_code)
256 | return results
257 |
258 | async def create_file_store(self, workspace: str, store: str, file, service_type):
259 | service: AddDataStoreProtocol = CreateFileStore()
260 |
261 | if service_type == "shapefile":
262 | service = ShapefileStore(
263 | client=self.http_client,
264 | service=service,
265 | logger=std_out_logger("Shapefile"),
266 | file=file,
267 | )
268 | elif service_type == "gpkg":
269 | service = GPKGfileStore(
270 | client=self.http_client,
271 | service=service,
272 | logger=std_out_logger("GeoPackage"),
273 | file=file,
274 | )
275 | else:
276 | raise ValueError(f"Service type {service_type} not supported")
277 | responses = await service.addFile(self.http_client, workspace, store)
278 | return self.response_recognise(responses)
279 |
280 | if service_type == "shapefile":
281 | service = ShapefileStore(
282 | client=self.http_client,
283 | service=service,
284 | logger=std_out_logger("Shapefile"),
285 | file=file,
286 | )
287 | elif service_type == "gpkg":
288 | service = GPKGfileStore(
289 | service=service, logger=std_out_logger("GeoPackage"), file=file
290 | )
291 | else:
292 | raise ValueError(f"Service type {service_type} not supported")
293 | await service.addFile(self.http_client, workspace, store)
294 |
295 | # Get all layers
296 | async def get_all_layers(
297 | self, workspace: Optional[str] = None
298 | ) -> Union[LayersModel, GSResponse]:
299 | Client = self.http_client
300 | if workspace:
301 | responses = await Client.get(f"/workspaces/{workspace}/layers")
302 | else:
303 | responses = await Client.get("layers")
304 | if responses.status_code == 200:
305 | return LayersModel.model_validate(responses.json())
306 | else:
307 | results = self.response_recognise(responses.status_code)
308 | return results
309 |
310 | # Get specific layer
311 | async def get_layer(
312 | self, workspace: str, layer: str
313 | ) -> Union[LayerModel, GSResponse]:
314 | Client = self.http_client
315 | responses = await Client.get(f"layers/{workspace}:{layer}")
316 | if responses.status_code == 200:
317 | return LayerModel.model_validate(responses.json())
318 | else:
319 | results = self.response_recognise(responses.status_code)
320 | return results
321 |
322 | # Delete specific layer
323 | async def delete_layer(self, workspace: str, layer: str) -> GSResponse:
324 | Client = self.http_client
325 | responses = await Client.delete(f"layers/{workspace}:{layer}")
326 | results = self.response_recognise(responses.status_code)
327 | return results
328 |
329 | # Get all layer groups
330 | async def get_all_layer_groups(
331 | self, workspace: Optional[str] = None
332 | ) -> Union[LayerGroupsModel, GSResponse]:
333 | Client = self.http_client
334 | if workspace:
335 | responses = await Client.get(f"workspaces/{workspace}/layergroups")
336 | else:
337 | responses = await Client.get("layergroups")
338 | if responses.status_code == 200:
339 | return LayerGroupsModel.model_validate(responses.json())
340 | else:
341 | results = self.response_recognise(responses.status_code)
342 | return results
343 |
344 | # Get all geofence rules
345 | async def get_all_geofence_rules(self) -> Union[RulesResponse, GSResponse]:
346 | Client = self.http_client
347 | # Check if the geofence plugin exists
348 | module_check = await self.check_modules("geofence")
349 | # If the module check fails, return the GSResponse directly
350 | if isinstance(module_check, GSResponse):
351 | return module_check
352 | responses = await Client.get(
353 | "geofence/rules/", headers={"Accept": "application/json"}
354 | )
355 | if responses.status_code == 200:
356 | return RulesResponse.model_validate(responses.json())
357 | else:
358 | results = self.response_recognise(responses.status_code)
359 | return results
360 |
361 | # Get geofence rule by id
362 | async def get_geofence_rule(self, id: int) -> Union[Rule, GSResponse]:
363 | Client = self.http_client
364 | # Check if the geofence plugin exists
365 | module_check = await self.check_modules("geofence")
366 | # If the module check fails, return the GSResponse directly
367 | if isinstance(module_check, GSResponse):
368 | return module_check
369 | responses = await Client.get(
370 | f"geofence/rules/id/{id}", headers={"Accept": "application/json"}
371 | )
372 | if responses.status_code == 200:
373 | return Rule.model_validate(responses.json())
374 | else:
375 | results = self.response_recognise(responses.status_code)
376 | return results
377 |
378 | # Create geofence on geoserver
379 | async def create_geofence(self, rule: Rule) -> GSResponse:
380 | PostingRule = NewRule(Rule=rule)
381 | # Check if the geofence plugin exists
382 | module_check = await self.check_modules("geofence")
383 | # If the module check fails, return the GSResponse directly
384 | if isinstance(module_check, GSResponse):
385 | return module_check
386 | Client = self.http_client
387 | responses = await Client.post(
388 | "geofence/rules",
389 | content=PostingRule.model_dump_json(),
390 | headers=self.head,
391 | )
392 | results = self.response_recognise(responses.status_code)
393 | return results
394 |
--------------------------------------------------------------------------------
/src/geoserverx/_sync/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/src/geoserverx/_sync/__init__.py
--------------------------------------------------------------------------------
/src/geoserverx/models/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/src/geoserverx/models/__init__.py
--------------------------------------------------------------------------------
/src/geoserverx/models/coverages_layer.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional, Union
2 |
3 | from pydantic import BaseModel, Field
4 |
5 |
6 | class NativeCRS(BaseModel):
7 | class Config:
8 | populate_by_name = True
9 |
10 | crsclass: Optional[str] = Field(..., alias="@class")
11 | dollar: Optional[str] = Field(..., alias="$")
12 |
13 |
14 | class Namespace(BaseModel):
15 | name: Optional[str] = Field(None, description="The name of the namespace.")
16 | href: Optional[str] = Field(None, description="URL to the namespace.")
17 |
18 |
19 | class Keywords(BaseModel):
20 | string: Optional[List[str]] = Field(
21 | None,
22 | description="List of keyword values with internationalization and vocabulary",
23 | )
24 |
25 |
26 | class MetadataLinkItem(BaseModel):
27 | type: Optional[str] = Field(None, description="The MIME type")
28 | metadataType: Optional[str] = Field(
29 | None, description='The type of metadata, e.g. "FGDC"'
30 | )
31 | content: Optional[str] = Field(None, description="The link URL")
32 |
33 |
34 | class Metadatalinks(BaseModel):
35 | metadataLink: Optional[List[MetadataLinkItem]] = Field(
36 | None, description="A collection of metadata links for the resource."
37 | )
38 |
39 |
40 | class MetadataLinkItem1(BaseModel):
41 | type: Optional[str] = Field(None, description="The MIME type")
42 | content: Optional[str] = Field(None, description="The link URL")
43 |
44 |
45 | class DataLinks(BaseModel):
46 | metadataLink: Optional[List[MetadataLinkItem1]] = Field(
47 | None, description="A collection of data links for the resource."
48 | )
49 |
50 |
51 | class NativeBoundingBox(BaseModel):
52 | minx: Optional[float] = Field(None, description="The min x coordinate")
53 | maxx: Optional[float] = Field(None, description="The max x coordinate")
54 | miny: Optional[float] = Field(None, description="The min y coordinate")
55 | maxy: Optional[float] = Field(None, description="The max y coordinate")
56 | crs: Optional[NativeCRS]
57 |
58 |
59 | class LatLonBoundingBox(BaseModel):
60 | minx: Optional[float] = Field(None, description="The min x coordinate")
61 | maxx: Optional[float] = Field(None, description="The max x coordinate")
62 | miny: Optional[float] = Field(None, description="The min y coordinate")
63 | maxy: Optional[float] = Field(None, description="The max y coordinate")
64 | crs: Optional[str] = Field(
65 | None, description="The coordinate reference system object of the bounding box."
66 | )
67 |
68 |
69 | class Store(BaseModel):
70 | class Config:
71 | populate_by_name = True
72 |
73 | storeclass: Optional[str] = Field(alias="@class")
74 | name: Optional[str] = Field(None, description="The name of the store")
75 | href: Optional[str] = Field(None, description="URL to the data store")
76 |
77 |
78 | class ResponseSRS(BaseModel):
79 | string: Optional[List[str]] = Field(None, description="The value of the srs")
80 |
81 |
82 | class AttributeItem(BaseModel):
83 | name: Optional[str] = Field(None, description="Name of the attribute.")
84 | minOccurs: Optional[int] = Field(
85 | None, description="Minimum number of occurrences of the attribute."
86 | )
87 | maxOccurs: Optional[int] = Field(
88 | None, description="Maximum number of occurrences of the attribute."
89 | )
90 |
91 | nillable: Optional[bool] = Field(
92 | None,
93 | description="Flag indicating if null is an acceptable value for the attribute.",
94 | )
95 | binding: Optional[str] = Field(
96 | None, description="The java class that values of this attribute are bound to."
97 | )
98 | length: Optional[int] = Field(
99 | None,
100 | description="Returns the length of this attribute. It's usually non null only for string and numeric types\"",
101 | )
102 |
103 |
104 | class Attributes(BaseModel):
105 | attribute: Optional[List[AttributeItem]] = Field(
106 | None, description="The derived set of attributes for the feature type."
107 | )
108 |
109 |
110 | class Range(BaseModel):
111 | max: Optional[Union[str, float]] = Field(None, description="max range value")
112 | min: Optional[Union[str, float]] = Field(None, description="min range value")
113 |
114 |
115 | class CoverageDimensionItem(BaseModel):
116 | description: Optional[str] = Field(
117 | None, description="description of the raster dimension"
118 | )
119 | name: Optional[str] = Field(None, description="name of the dimension")
120 | range: Optional[Range] = Field(None, description="dimension range")
121 |
122 |
123 | class Dimensions(BaseModel):
124 | coverageDimension: Optional[List[CoverageDimensionItem]] = None
125 |
126 |
127 | class Range1(BaseModel):
128 | high: Optional[str] = Field(None, description="max range values")
129 | low: Optional[str] = Field(None, description="min range values")
130 |
131 |
132 | class Transform(BaseModel):
133 | scaleX: Optional[float] = Field(None, description="scale value to apply in X")
134 | scaleY: Optional[float] = Field(None, description="scale value to apply in Y")
135 | shearX: Optional[float] = Field(None, description="shear value to apply in X")
136 | shearY: Optional[float] = Field(None, description="shear value to apply in Y")
137 | translateX: Optional[float] = Field(None, description="translation to apply in X")
138 | translateY: Optional[float] = Field(None, description="translation to apply in Y")
139 |
140 |
141 | class InterpolationMethods(BaseModel):
142 | string: Optional[List[str]] = None
143 |
144 |
145 | class SupportedFormatsString(BaseModel):
146 | string: Optional[List[str]] = None
147 |
148 |
149 | class RequestSRSString(BaseModel):
150 | string: Optional[List[str]] = None
151 |
152 |
153 | class InterpolationMethodsString(BaseModel):
154 | string: Optional[List[str]] = None
155 |
156 |
157 | class Grid(BaseModel):
158 | class Config:
159 | populate_by_name = True
160 |
161 | dimension: Optional[str] = Field(alias="@dimension")
162 | crs: Optional[str] = Field(None, description="target coordinate system")
163 | range: Optional[Range1] = Field(None, description="range of the raster plan")
164 | transform: Optional[Transform] = Field(
165 | None, description="transformation definition"
166 | )
167 | interpolationMethods: Optional[InterpolationMethods] = Field(
168 | None, description="available interpolations methods for this coverage"
169 | )
170 |
171 |
172 | class DimensionInfo(BaseModel):
173 | defaultValue: Optional[str]
174 | enabled: Optional[bool]
175 |
176 |
177 | class MetadataEntry(BaseModel):
178 | class Config:
179 | populate_by_name = True
180 |
181 | key: Optional[str] = Field(alias="@key")
182 | dollar: Optional[str] = Field(..., alias="$")
183 | dimensionInfo: Optional[DimensionInfo]
184 |
185 |
186 | class EntryParameters(BaseModel):
187 | entry: List
188 |
189 |
190 | class MetadataEntryList(BaseModel):
191 | entry: MetadataEntry
192 |
193 |
194 | class CoverageInfo(BaseModel):
195 | name: Optional[str] = Field(
196 | None,
197 | description='The name of the resource. This name corresponds to the "published" name of the resource.',
198 | )
199 | nativeName: Optional[str] = Field(
200 | None,
201 | description="The native name of the resource. This name corresponds to the physical resource that feature type is derived from -- a shapefile name, a database table, etc...",
202 | )
203 | namespace: Optional[Namespace] = Field(
204 | None,
205 | description="The namespace URI of the resource. Example would be an application schema namespace URI.",
206 | )
207 | title: Optional[str] = Field(
208 | None,
209 | description="The title of the resource. This is usually something that is meant to be displayed in a user interface.",
210 | )
211 | abstract: Optional[str] = Field(
212 | None,
213 | description="A description of the resource. This is usually something that is meant to be displayed in a user interface.",
214 | )
215 | defaultInterpolationMethod: Optional[str] = Field(
216 | None,
217 | description="Default resampling (interpolation) method that will be used for this coverage.",
218 | )
219 | keywords: Optional[Keywords] = Field(
220 | None, description="A collection of keywords associated with the resource."
221 | )
222 | metadatalinks: Optional[Metadatalinks] = Field(
223 | None, description="Wraps a collection of metadata links for the resource."
224 | )
225 | dataLinks: Optional[DataLinks] = Field(
226 | None, description="Wraps a collection of data links for the resource."
227 | )
228 | nativeCRS: Optional[NativeCRS]
229 | srs: Optional[str] = Field(
230 | None,
231 | description="Returns the identifier of coordinate reference system of the resource.",
232 | )
233 | nativeBoundingBox: Optional[NativeBoundingBox] = Field(
234 | None, description="Returns the bounds of the resource in its declared CRS."
235 | )
236 | latLonBoundingBox: Optional[LatLonBoundingBox] = Field(
237 | None,
238 | description='The bounds of the resource in lat / lon. This value represents a "fixed value" and is not calculated on the underlying dataset.',
239 | )
240 | enabled: Optional[bool] = True
241 | advertised: Optional[bool] = True
242 | projectionPolicy: Optional[str]
243 | metadata: Optional[MetadataEntryList] = Field(
244 | None, description="A list of key/value metadata pairs."
245 | )
246 | store: Optional[Store]
247 | cqlFilter: Optional[str] = Field(
248 | None, description="The ECQL string used as default feature type filter"
249 | )
250 | maxFeatures: Optional[int] = Field(
251 | None,
252 | description="A cap on the number of features that a query against this type can return.",
253 | )
254 | numDecimals: Optional[float] = Field(
255 | None,
256 | description="The number of decimal places to use when encoding floating point numbers from data of this feature type.",
257 | )
258 | responseSRS: Optional[ResponseSRS] = Field(
259 | None,
260 | description="The SRSs that the WFS service will advertise in the capabilities document for this feature type (overriding the global WFS settings).",
261 | )
262 | overridingServiceSRS: Optional[bool] = Field(
263 | None,
264 | description="True if this feature type info is overriding the WFS global SRS list",
265 | )
266 | skipNumberMatched: Optional[bool] = Field(
267 | None,
268 | description="True if this feature type info is overriding the counting of numberMatched.",
269 | )
270 | circularArcPresent: Optional[bool] = None
271 | linearizationTolerance: Optional[float] = Field(
272 | None,
273 | description="Tolerance used to linearize this feature type, as an absolute value expressed in the geometries own CRS",
274 | )
275 | attributes: Optional[Attributes] = Field(
276 | None,
277 | description="Wrapper for the derived set of attributes for the feature type.",
278 | )
279 | dimensions: Optional[Dimensions] = Field(None, description="raster dimensions")
280 | grid: Optional[Grid] = Field(
281 | None,
282 | description="contains information about how to translate from the raster plan to a coordinate reference system",
283 | )
284 | supportedFormats: Optional[SupportedFormatsString]
285 | interpolationMethods: Optional[InterpolationMethodsString]
286 | requestSRS: Optional[RequestSRSString]
287 | parameters: EntryParameters
288 | serviceConfiguration: Optional[bool]
289 | simpleConversionEnabled: Optional[bool]
290 |
291 |
292 | class CoverageModel(BaseModel):
293 | coverage: CoverageInfo
294 |
295 |
296 | class UpdateCoverage:
297 | def __init__(self) -> None:
298 | pass
299 |
300 | def update_coverage_info(
301 | self, coverage_info: CoverageInfo, advertised: Optional[bool] = True
302 | ) -> CoverageInfo:
303 | if advertised:
304 | coverage_info.advertised = advertised
305 |
306 | return coverage_info
307 |
--------------------------------------------------------------------------------
/src/geoserverx/models/coverages_store.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, List, Literal, Optional, Union
2 |
3 | from pydantic import BaseModel
4 |
5 | from geoserverx.models.workspace import WorkspaceInBulk
6 |
7 |
8 | class CoveragesStoreInBulk(BaseModel):
9 | name: str = ...
10 | href: str = ...
11 |
12 |
13 | class CoveragesStoresDict(BaseModel):
14 | coverageStore: List[CoveragesStoreInBulk] = ...
15 |
16 |
17 | class CoveragesStoresModel(BaseModel):
18 | coverageStores: Union[CoveragesStoresDict, Literal[""]] = ""
19 |
20 |
21 | class CoveragesStoreModelDetail(BaseModel):
22 | name: str = ...
23 | description: str = None
24 | type: str = ...
25 | enabled: bool = ...
26 | workspace: WorkspaceInBulk = ...
27 | _default: bool = ...
28 | url: str = ...
29 | coverages: str = ...
30 | dateCreated: Optional[str]
31 | metadata: Optional[Dict] = None
32 |
33 |
34 | class CoveragesStoreModel(BaseModel):
35 | coverageStore: CoveragesStoreModelDetail
36 |
--------------------------------------------------------------------------------
/src/geoserverx/models/data_store.py:
--------------------------------------------------------------------------------
1 | from typing import List, Literal, Optional, Union
2 |
3 | from pydantic import BaseModel, Field
4 |
5 | from .workspace import WorkspaceInBulk
6 |
7 |
8 | class DataStoreInBulk(BaseModel):
9 | name: str = ...
10 | href: str = ...
11 |
12 |
13 | class DataStoreDict(BaseModel):
14 | dataStore: List[DataStoreInBulk]
15 |
16 |
17 | class DataStoresModel(BaseModel):
18 | dataStores: Union[DataStoreDict, Literal[""]] = ""
19 |
20 |
21 | class DatastoreConnection(BaseModel):
22 | key: str = Field(..., alias="@key")
23 | path: str = Field(..., alias="$")
24 |
25 | class Config:
26 | populate_by_name = True
27 |
28 |
29 | class EntryItem(BaseModel):
30 | entry: List[DatastoreConnection]
31 |
32 |
33 | class DatastoreItem(BaseModel):
34 | name: str
35 | connectionParameters: EntryItem
36 |
37 |
38 | class DataStoreModelDetails(BaseModel):
39 | name: str = ...
40 | description: str = None
41 | enabled: bool = ...
42 | workspace: WorkspaceInBulk = ...
43 | connectionParameters: EntryItem = ...
44 | _default: bool = ...
45 | dateCreated: Optional[str]
46 | dateModified: Optional[str]
47 | featureTypes: str
48 |
49 |
50 | class DataStoreModel(BaseModel):
51 | dataStore: DataStoreModelDetails = {}
52 |
53 |
54 | class CreateStoreItem(BaseModel):
55 | host: str
56 | port: int
57 | database: str
58 | user: str
59 | passwd: str
60 | dbtype: str
61 |
62 |
63 | class CreateDataStoreModel(BaseModel):
64 | name: str
65 | connectionParameters: CreateStoreItem
66 |
67 |
68 | class MainCreateDataStoreModel(BaseModel):
69 | dataStore: CreateDataStoreModel
70 |
--------------------------------------------------------------------------------
/src/geoserverx/models/featuretypes_layer.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional, Union
2 |
3 | from pydantic import BaseModel, Field
4 |
5 |
6 | class Namespace(BaseModel):
7 | name: Optional[str] = Field(None, description="The name of the namespace.")
8 | href: Optional[str] = Field(None, description="URL to the namespace.")
9 |
10 |
11 | class Keywords(BaseModel):
12 | string: Optional[List[str]] = Field(
13 | None,
14 | description="List of keyword values with internationalization and vocabulary",
15 | )
16 |
17 |
18 | class MetadataLinkItem(BaseModel):
19 | type: Optional[str] = Field(None, description="The MIME type")
20 | metadataType: Optional[str] = Field(
21 | None, description='The type of metadata, e.g. "FGDC"'
22 | )
23 | content: Optional[str] = Field(None, description="The link URL")
24 |
25 |
26 | class Metadatalinks(BaseModel):
27 | metadataLink: Optional[List[MetadataLinkItem]] = Field(
28 | None, description="A collection of metadata links for the resource."
29 | )
30 |
31 |
32 | class MetadataLinkItem1(BaseModel):
33 | type: Optional[str] = Field(None, description="The MIME type")
34 | content: Optional[str] = Field(None, description="The link URL")
35 |
36 |
37 | class DataLinks(BaseModel):
38 | metadataLink: Optional[List[MetadataLinkItem1]] = Field(
39 | None, description="A collection of data links for the resource."
40 | )
41 |
42 |
43 | class CRSentry(BaseModel):
44 | class Config:
45 | populate_by_name = True
46 |
47 | key: Optional[str] = Field(alias="@class")
48 | dollar: Optional[str] = Field(..., alias="$")
49 |
50 |
51 | class CRSetnryDict(BaseModel):
52 | entry: CRSentry
53 |
54 |
55 | class NativeBoundingBox(BaseModel):
56 | minx: Optional[float] = Field(None, description="The min x coordinate")
57 | maxx: Optional[float] = Field(None, description="The max x coordinate")
58 | miny: Optional[float] = Field(None, description="The min y coordinate")
59 | maxy: Optional[float] = Field(None, description="The max y coordinate")
60 | crs: Optional[Union[str, CRSentry]] = Field(
61 | None, description="The coordinate reference system object of the bounding box."
62 | )
63 |
64 |
65 | class LatLonBoundingBox(BaseModel):
66 | minx: Optional[float] = Field(None, description="The min x coordinate")
67 | maxx: Optional[float] = Field(None, description="The max x coordinate")
68 | miny: Optional[float] = Field(None, description="The min y coordinate")
69 | maxy: Optional[float] = Field(None, description="The max y coordinate")
70 | crs: Optional[str] = Field(
71 | None, description="The coordinate reference system object of the bounding box."
72 | )
73 |
74 |
75 | class Store(BaseModel):
76 | class Config:
77 | populate_by_name = True
78 |
79 | key: Optional[str] = Field(alias="@class")
80 | name: Optional[str] = Field(None, description="The name of the store")
81 | href: Optional[str] = Field(None, description="URL to the data store")
82 |
83 |
84 | class ResponseSRS(BaseModel):
85 | string: Optional[str] = Field(None, description="The value of the srs")
86 |
87 |
88 | class AttributeItem(BaseModel):
89 | name: Optional[str] = Field(None, description="Name of the attribute.")
90 | minOccurs: Optional[int] = Field(
91 | None, description="Minimum number of occurrences of the attribute."
92 | )
93 | maxOccurs: Optional[int] = Field(
94 | None, description="Maximum number of occurrences of the attribute."
95 | )
96 | nillable: Optional[bool] = Field(
97 | None,
98 | description="Flag indicating if null is an acceptable value for the attribute.",
99 | )
100 | binding: Optional[str] = Field(
101 | None, description="The java class that values of this attribute are bound to."
102 | )
103 | length: Optional[int]
104 |
105 |
106 | class Attributes(BaseModel):
107 | attribute: Optional[List[AttributeItem]] = Field(
108 | None, description="The derived set of attributes for the feature type."
109 | )
110 |
111 |
112 | class MetadataEntryItem(BaseModel):
113 | class Config:
114 | populate_by_name = True
115 |
116 | key: Optional[str] = Field(alias="@key")
117 | dollar: Optional[str] = Field(..., alias="$")
118 |
119 |
120 | class MetadataEntryList(BaseModel):
121 | entry: List[MetadataEntryItem]
122 |
123 |
124 | class FeatureTypeInfo(BaseModel):
125 | name: Optional[str] = Field(
126 | None,
127 | description='The name of the resource. This name corresponds to the "published" name of the resource.',
128 | )
129 | nativeName: Optional[str] = Field(
130 | None,
131 | description="The native name of the resource. This name corresponds to the physical resource that feature type is derived from -- a shapefile name, a database table, etc...",
132 | )
133 | namespace: Optional[Namespace] = Field(
134 | None,
135 | description="The namespace URI of the resource. Example would be an application schema namespace URI.",
136 | )
137 | title: Optional[str] = Field(
138 | None,
139 | description="The title of the resource. This is usually something that is meant to be displayed in a user interface.",
140 | )
141 | abstract: Optional[str] = Field(
142 | None,
143 | description="A description of the resource. This is usually something that is meant to be displayed in a user interface.",
144 | )
145 | keywords: Optional[Keywords] = Field(
146 | None, description="A collection of keywords associated with the resource."
147 | )
148 | srs: Optional[str] = Field(
149 | None,
150 | description="Returns the identifier of coordinate reference system of the resource.",
151 | )
152 | metadatalinks: Optional[Metadatalinks]
153 | dataLinks: Optional[DataLinks]
154 |
155 | nativeBoundingBox: Optional[NativeBoundingBox] = Field(
156 | None, description="Returns the bounds of the resource in its declared CRS."
157 | )
158 | latLonBoundingBox: Optional[LatLonBoundingBox] = Field(
159 | None,
160 | description='The bounds of the resource in lat / lon. This value represents a "fixed value" and is not calculated on the underlying dataset.',
161 | )
162 | projectionPolicy: str
163 | metadata: Optional[MetadataEntryList] = Field(
164 | None, description="A list of key/value metadata pairs."
165 | )
166 | store: Optional[Store] = Field(
167 | None, description="The store the resource is a part of."
168 | )
169 | nativeCRS: Optional[Union[str, CRSentry]] = Field(
170 | None, description="String for Native CRS"
171 | )
172 | cqlFilter: Optional[str] = Field(
173 | None, description="The ECQL string used as default feature type filter"
174 | )
175 | maxFeatures: Optional[int] = Field(
176 | None,
177 | description="A cap on the number of features that a query against this type can return.",
178 | )
179 | numDecimals: Optional[int] = Field(
180 | None,
181 | description="The number of decimal places to use when encoding floating point numbers from data of this feature type.",
182 | )
183 | responseSRS: Optional[ResponseSRS] = Field(
184 | None,
185 | description="The SRSs that the WFS service will advertise in the capabilities document for this feature type (overriding the global WFS settings).",
186 | )
187 | overridingServiceSRS: Optional[bool] = Field(
188 | None,
189 | description="True if this feature type info is overriding the WFS global SRS list",
190 | )
191 | skipNumberMatched: Optional[bool] = Field(
192 | None,
193 | description="True if this feature type info is overriding the counting of numberMatched.",
194 | )
195 | circularArcPresent: Optional[bool] = None
196 | linearizationTolerance: Optional[float] = Field(
197 | None,
198 | description="Tolerance used to linearize this feature type, as an absolute value expressed in the geometries own CRS",
199 | )
200 | attributes: Optional[Attributes] = Field(
201 | None,
202 | description="Wrapper for the derived set of attributes for the feature type.",
203 | )
204 | enabled: Optional[bool]
205 | advertised: Optional[bool]
206 | serviceConfiguration: Optional[bool]
207 | simpleConversionEnabled: Optional[bool]
208 | padWithZeros: Optional[bool]
209 | forcedDecimal: Optional[bool]
210 | overridingServiceSRS: Optional[bool]
211 | skipNumberMatched: Optional[bool]
212 | circularArcPresent: Optional[bool]
213 | encodeMeasures: Optional[bool]
214 |
215 |
216 | class FeatureTypesModel(BaseModel):
217 | featureType: FeatureTypeInfo
218 |
--------------------------------------------------------------------------------
/src/geoserverx/models/geofence.py:
--------------------------------------------------------------------------------
1 | from typing import List, Literal, Optional
2 |
3 | from pydantic import BaseModel
4 |
5 |
6 | class Attribute(BaseModel):
7 | name: str
8 | dataType: str
9 | accessType: str
10 |
11 |
12 | class LayerDetails(BaseModel):
13 | layerType: Optional[str] = None
14 | defaultStyle: Optional[str] = None
15 | cqlFilterRead: Optional[str] = None
16 | cqlFilterWrite: Optional[str] = None
17 | allowedArea: Optional[str] = None
18 | spatialFilterType: Optional[str] = None
19 | catalogMode: Optional[str] = None
20 | allowedStyles: List[str] = []
21 | attributes: List[Attribute]
22 |
23 |
24 | class Rule(BaseModel):
25 | priority: int
26 | userName: Optional[str] = None
27 | roleName: Optional[str] = None
28 | addressRange: Optional[str] = None
29 | workspace: Optional[str] = None
30 | layer: Optional[str] = None
31 | service: Optional[Literal["GWC", "WMS", "WCS", "WFS"]] = None
32 | request: Optional[str] = None
33 | subfield: Optional[str] = None
34 | access: Literal["ALLOW", "DENY", "LIMIT"] = "ALLOW"
35 | limits: Optional[str] = None
36 | layerDetails: Optional[LayerDetails] = None
37 |
38 |
39 | class GetRule(Rule):
40 | id: Optional[int] = None
41 |
42 |
43 | class RulesResponse(BaseModel):
44 | count: int
45 | rules: List[GetRule]
46 |
47 |
48 | class NewRule(BaseModel):
49 | Rule: Rule
50 |
--------------------------------------------------------------------------------
/src/geoserverx/models/gs_response.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pydantic import BaseModel
4 |
5 |
6 | class GSResponse(BaseModel):
7 | code: Optional[int]
8 | response: str = ...
9 |
10 |
11 | class HttpxError(BaseModel):
12 | response: str
13 |
--------------------------------------------------------------------------------
/src/geoserverx/models/layer_group.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 | from typing import List, Literal, Optional, Union
3 |
4 | from pydantic import BaseModel, Field
5 |
6 |
7 | class LayerGroupElement(BaseModel):
8 | name: str = ...
9 | href: str = ...
10 |
11 |
12 | class LayerGroupList(BaseModel):
13 | layerGroup: List[LayerGroupElement] = ...
14 |
15 |
16 | class LayerGroupsModel(BaseModel):
17 | layerGroups: Union[LayerGroupList, Literal[""]]
18 |
19 |
20 | class Published(BaseModel):
21 | type: str = Field(..., alias="@type")
22 | name: str = ...
23 | href: str = ...
24 |
25 |
26 | class Publishables(BaseModel):
27 | published: Published = ...
28 |
29 |
30 | class Style(BaseModel):
31 | name: str = ...
32 | href: str = ...
33 |
34 |
35 | class Styles(BaseModel):
36 | style: Style = ...
37 |
38 |
39 | class Bounds(BaseModel):
40 | minx: float = ...
41 | miny: float = ...
42 | maxx: float = ...
43 | maxy: float = ...
44 | crs: str = ...
45 |
46 |
47 | class ModeEnum(Enum):
48 | single = "SINGLE"
49 | opaque_container = "OPAQUE_CONTAINER"
50 | named = "NAMED"
51 | container = "CONTAINER"
52 | eo = "EO"
53 |
54 |
55 | class WorkspaceModel(BaseModel):
56 | name: str = None
57 |
58 |
59 | class BaseLayerGroup(BaseModel):
60 | name: str = ...
61 |
62 |
63 | class SingleLayerGroup(BaseLayerGroup):
64 | mode: ModeEnum
65 | internationalTitle: str = ""
66 | internationalAbstract: str = ""
67 | publishables: Publishables
68 | styles: Styles
69 | bounds: Bounds
70 | dateCreated: str = ...
71 |
72 |
73 | class SingleLayerGroupModel(BaseModel):
74 | layerGroup: SingleLayerGroup
75 |
76 |
77 | class LayerListModel(BaseModel):
78 | layer: List[str] = []
79 |
80 |
81 | class LayerGroupModel(BaseModel):
82 | name: str
83 | mode: ModeEnum
84 | title: str
85 | layers: LayerListModel
86 | abstractTxt: Optional[str] = None
87 | workspace: Optional[WorkspaceModel] = None
88 |
89 |
90 | class LayerGroupPayload(BaseModel):
91 | layerGroup: LayerGroupModel
92 |
93 |
94 | class LayerGroupStylesModel(BaseModel):
95 | style: List[str] = []
96 |
97 |
98 | class LayerGroupKeywordsModel(BaseModel):
99 | keyword: List[str] = []
100 |
--------------------------------------------------------------------------------
/src/geoserverx/models/layers.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional, Union
2 |
3 | from pydantic import BaseModel, Field
4 |
5 |
6 | class LayerInBulk(BaseModel):
7 | name: str = ...
8 | href: str = ...
9 |
10 |
11 | class LayerDict(BaseModel):
12 | layer: List[LayerInBulk]
13 |
14 |
15 | class LayersModel(BaseModel):
16 | layers: Union[LayerDict, str] = ""
17 |
18 |
19 | class DefaultStyleOfLayer(BaseModel):
20 | name: str = ...
21 | href: str = ...
22 |
23 |
24 | class ExtraStyles(BaseModel):
25 | class_name: str = Field(..., alias="@class")
26 | style: List[DefaultStyleOfLayer]
27 |
28 |
29 | class LayerResource(BaseModel):
30 | class_name: str = Field(..., alias="@class")
31 | name: str = ...
32 | href: str = ...
33 |
34 |
35 | class LayerAttribution(BaseModel):
36 | logoWidth: float = ...
37 | logoHeight: float = ...
38 |
39 |
40 | class SingleLayer(BaseModel):
41 | name: str = ...
42 | path: Optional[str]
43 | type: str = ...
44 | defaultStyle: DefaultStyleOfLayer = ...
45 | styles: Optional[ExtraStyles] = None
46 | resource: LayerResource = ...
47 | attribution: LayerAttribution
48 | dateCreated: Optional[str] = None
49 | opaque: Optional[bool]
50 | queryable: Optional[bool]
51 |
52 |
53 | class LayerModel(BaseModel):
54 | layer: SingleLayer = ...
55 |
56 |
57 | # class NewWorkspaceInfo(BaseModel):
58 | # name: str = ...
59 | # isolated: bool = None
60 |
61 |
62 | # class NewWorkspace(BaseModel):
63 | # workspace: NewWorkspaceInfo = ...
64 |
--------------------------------------------------------------------------------
/src/geoserverx/models/style.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | from pydantic import BaseModel
4 |
5 |
6 | class langVersion(BaseModel):
7 | version: str = ...
8 |
9 |
10 | class SingleStyle(BaseModel):
11 | name: str = ...
12 | format: str = ...
13 | languageVersion: langVersion = ...
14 | filename: str = ...
15 |
16 |
17 | class StyleModel(BaseModel):
18 | style: SingleStyle
19 |
20 |
21 | class allStyleList(BaseModel):
22 | name: str
23 | href: str
24 |
25 |
26 | class allStyle(BaseModel):
27 | style: List[allStyleList]
28 |
29 |
30 | class AllStylesModel(BaseModel):
31 | styles: allStyle
32 |
--------------------------------------------------------------------------------
/src/geoserverx/models/workspace.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import BaseModel
4 |
5 |
6 | class WorkspaceInBulk(BaseModel):
7 | name: str = ...
8 | href: str = ...
9 |
10 |
11 | class workspaceDict(BaseModel):
12 | workspace: List[WorkspaceInBulk]
13 |
14 |
15 | class WorkspacesModel(BaseModel):
16 | workspaces: workspaceDict = ""
17 |
18 |
19 | class SingleWorkspace(BaseModel):
20 | name: str = ...
21 | isolated: bool = ...
22 | dateCreated: Optional[str]
23 | dataStores: str = ...
24 | coverageStores: str = ...
25 | wmsStores: str = ...
26 | wmtsStores: str = ...
27 |
28 |
29 | class WorkspaceModel(BaseModel):
30 | workspace: SingleWorkspace = ...
31 |
32 |
33 | class NewWorkspaceInfo(BaseModel):
34 | name: str = ...
35 | isolated: bool = None
36 |
37 |
38 | class NewWorkspace(BaseModel):
39 | workspace: NewWorkspaceInfo = ...
40 |
--------------------------------------------------------------------------------
/src/geoserverx/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/src/geoserverx/utils/__init__.py
--------------------------------------------------------------------------------
/src/geoserverx/utils/auth.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass
2 |
3 | from geoserverx.utils.errors import GeoServerXError
4 |
5 |
6 | @dataclass
7 | class GeoServerXAuth:
8 | username: str = "admin"
9 | password: str = "geoserver"
10 | url = "http://127.0.0.1:8080/geoserver/rest/"
11 |
12 | def __post_init__(self):
13 | if not self.username and not self.password and not self.url:
14 | raise GeoServerXError(0, "Username, Password and URL is missing")
15 | elif not self.username:
16 | raise GeoServerXError(0, "Username is missing")
17 | elif not self.password:
18 | raise GeoServerXError(0, "password is missing")
19 | elif not self.url:
20 | raise GeoServerXError(0, "URL is missing")
21 |
--------------------------------------------------------------------------------
/src/geoserverx/utils/custom_exceptions.py:
--------------------------------------------------------------------------------
1 | class GSModuleNotFound(Exception):
2 | def __init__(self, message="Module not found", status_code=412):
3 | self.message = message
4 | self.status_code = status_code
5 | super().__init__(self.message)
6 |
--------------------------------------------------------------------------------
/src/geoserverx/utils/enums.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 | from geoserverx.models.gs_response import GSResponse
4 |
5 |
6 | class GSResponseEnum(Enum):
7 | _404 = GSResponse(code=404, response="Result not found")
8 | _403 = GSResponse(code=403, response="Forbidden Request")
9 | _401 = GSResponse(code=401, response="Unauthorized request")
10 | _500 = GSResponse(code=500, response="Internal Server error")
11 | _201 = GSResponse(code=201, response="Data added successfully")
12 | _200 = GSResponse(code=200, response="Executed successfully")
13 | _204 = GSResponse(code=204, response="No Content")
14 | _400 = GSResponse(code=400, response="Bad Request")
15 | _409 = GSResponse(code=409, response="Same data found")
16 | _503 = GSResponse(code=503, response="Can't connect to Geoserver")
17 |
18 |
19 | class HTTPXErrorEnum(Enum):
20 | runtime = "Client not found! Please check client parameters"
21 | requesterr = "Client Credentials are incorrect"
22 |
--------------------------------------------------------------------------------
/src/geoserverx/utils/errors.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass, field
2 | from typing import Optional
3 |
4 |
5 | @dataclass
6 | class GeoServerXError(Exception):
7 | status_code: int
8 | status_message: Optional[str] = field(default=None)
9 |
--------------------------------------------------------------------------------
/src/geoserverx/utils/http_client.py:
--------------------------------------------------------------------------------
1 | from httpx import AsyncClient # noqa: F401
2 | from httpx import Client as BaseClient # noqa: F401
3 |
4 |
5 | class SyncClient(BaseClient):
6 | def aclose(self) -> None:
7 | self.close()
8 |
--------------------------------------------------------------------------------
/src/geoserverx/utils/logger.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import sys
3 |
4 |
5 | def std_out_logger(name: str) -> logging.Logger:
6 | logger = logging.getLogger(name)
7 | logger.setLevel(logging.DEBUG)
8 | handler = logging.StreamHandler(sys.stdout)
9 | formatter = logging.Formatter("[%(name)s] [%(levelname)s] %(message)s")
10 | handler.setFormatter(formatter)
11 | logger.addHandler(handler)
12 | return logger
13 |
--------------------------------------------------------------------------------
/src/geoserverx/utils/services/async_datastore.py:
--------------------------------------------------------------------------------
1 | import json
2 | from logging import Logger
3 | from typing import Protocol
4 |
5 |
6 | class AddDataStoreProtocol(Protocol):
7 | """
8 | Represents functionality of sending a file to the server.
9 | """
10 |
11 | async def addFile(
12 | self,
13 | client,
14 | workspace,
15 | store,
16 | method,
17 | store_payload,
18 | layer_payload,
19 | store_header,
20 | layer_header,
21 | ): ...
22 |
23 |
24 | class CreateFileStore:
25 | async def addFile(
26 | self,
27 | client,
28 | workspace,
29 | store,
30 | method,
31 | store_payload,
32 | layer_payload,
33 | store_header,
34 | layer_header,
35 | ):
36 | Client = client
37 | # async with client as Client:
38 | store_responses = await Client.post(
39 | f"workspaces/{workspace}/datastores/",
40 | data=store_payload,
41 | headers=store_header,
42 | )
43 | # async with client as Client:
44 | await Client.put(
45 | f"workspaces/{workspace}/datastores/{store}/file.{method}",
46 | content=layer_payload,
47 | headers=layer_header,
48 | )
49 | results = store_responses.status_code
50 | # await client.aclose()
51 |
52 | return results
53 |
54 |
55 | class ShapefileStore:
56 | def __init__(
57 | self, service: AddDataStoreProtocol, logger: Logger, file, client
58 | ) -> None:
59 | self.inner = service
60 | self.logger = logger
61 | self.file = file
62 | self.client = client
63 |
64 | async def addFile(self, client, workspace, store):
65 | store_payload: str = json.dumps(
66 | {
67 | "dataStore": {
68 | "name": store,
69 | "connectionParameters": {
70 | "entry": [{"@key": "url", "$": "file:" + self.file}]
71 | },
72 | }
73 | }
74 | )
75 | # self.logger.debug(f"Shapefile store payload: {store_payload}")
76 | result = await self.inner.addFile(
77 | self.client,
78 | workspace,
79 | store,
80 | "shp",
81 | store_payload,
82 | {"Content-Type": "application/json"},
83 | {"Content-Type": "application/zip"},
84 | )
85 | return result
86 |
87 |
88 | class GPKGfileStore:
89 | def __init__(
90 | self, service: AddDataStoreProtocol, logger: Logger, file, client
91 | ) -> None:
92 | self.inner = service
93 | self.logger = logger
94 | self.file = file
95 | self.client = client
96 |
97 | async def addFile(self, client, workspace, store):
98 | store_payload: str = json.dumps(
99 | {
100 | "dataStore": {
101 | "name": store,
102 | "connectionParameters": {
103 | "entry": [
104 | {"@key": "database", "$": f"file:{self.file}"},
105 | {"@key": "dbtype", "$": "geopkg"},
106 | ]
107 | },
108 | }
109 | }
110 | )
111 | # self.logger.debug(f"GeoPackage store payload: {store_payload}")
112 | result = await self.inner.addFile(
113 | self.client,
114 | workspace,
115 | store,
116 | "gpkg",
117 | store_payload,
118 | {"Content-Type": "application/json"},
119 | )
120 | return result
121 |
--------------------------------------------------------------------------------
/src/geoserverx/utils/services/datastore.py:
--------------------------------------------------------------------------------
1 | import json
2 | from logging import Logger
3 | from typing import Protocol
4 |
5 |
6 | class AddDataStoreProtocol(Protocol):
7 | """
8 | Represents functionality of sending a file to the server.
9 | """
10 |
11 | def addFile(
12 | self,
13 | client,
14 | workspace,
15 | store,
16 | method,
17 | store_payload,
18 | layer_payload,
19 | store_header,
20 | layer_header,
21 | ): ...
22 |
23 |
24 | class CreateFileStore:
25 | def addFile(
26 | self,
27 | client,
28 | workspace,
29 | store,
30 | method,
31 | store_payload,
32 | layer_payload,
33 | store_header,
34 | layer_header,
35 | ):
36 | store_responses = client.post(
37 | f"workspaces/{workspace}/datastores/",
38 | content=store_payload,
39 | headers=store_header,
40 | )
41 | client.put(
42 | f"workspaces/{workspace}/datastores/{store}/file.{method}",
43 | content=layer_payload,
44 | headers=layer_header,
45 | )
46 | result = store_responses.status_code
47 | return result
48 |
49 |
50 | class ShapefileStore:
51 | def __init__(self, service: AddDataStoreProtocol, logger: Logger, file) -> None:
52 | self.inner = service
53 | self.logger = logger
54 | self.file = file
55 | self.result = None
56 |
57 | def addFile(self, client, workspace, store):
58 | store_payload: str = json.dumps(
59 | {
60 | "dataStore": {
61 | "name": store,
62 | "connectionParameters": {
63 | "entry": [{"@key": "url", "$": f"file:{self.file}"}]
64 | },
65 | }
66 | }
67 | )
68 | # self.logger.debug(f"Shapefile store payload: {store_payload}")
69 | layer_payload = self.file
70 | response = self.inner.addFile(
71 | client,
72 | workspace,
73 | store,
74 | "shp",
75 | store_payload,
76 | layer_payload,
77 | {"Content-Type": "application/json"},
78 | {"Content-Type": "application/zip"},
79 | )
80 | self.result = response
81 | return self.result
82 |
83 |
84 | class GPKGfileStore:
85 | def __init__(self, service: AddDataStoreProtocol, logger: Logger, file) -> None:
86 | self.inner = service
87 | self.logger = logger
88 | self.file = file
89 | self.result = None
90 |
91 | def addFile(self, client, workspace, store):
92 | store_payload: str = json.dumps(
93 | {
94 | "dataStore": {
95 | "name": store,
96 | "connectionParameters": {
97 | "entry": [
98 | {"@key": "database", "$": f"file:{self.file}"},
99 | {"@key": "dbtype", "$": "geopkg"},
100 | ]
101 | },
102 | }
103 | }
104 | )
105 | # self.logger.debug(f"GeoPackage store payload: {store_payload}")
106 | layer_payload = self.file
107 | response = self.inner.addFile(
108 | client,
109 | workspace,
110 | store,
111 | "gpkg",
112 | store_payload,
113 | layer_payload,
114 | {"Content-Type": "application/json"},
115 | {"Content-Type": "application/json"},
116 | )
117 | self.result = response
118 | return self.result
119 |
--------------------------------------------------------------------------------
/tests/_async/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/tests/_async/__init__.py
--------------------------------------------------------------------------------
/tests/_sync/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geobeyond/geoserverx/64349483b643d4c662da9d358286728360a38015/tests/_sync/__init__.py
--------------------------------------------------------------------------------
/tests/cli/test_cli.py:
--------------------------------------------------------------------------------
1 | import httpx
2 | from typer.testing import CliRunner
3 |
4 | from geoserverx.cli.cli import app
5 |
6 | runner = CliRunner()
7 |
8 | baseUrl = "http://127.0.0.1:8080/geoserver/rest/"
9 |
10 |
11 | # Test - get_all_workspaces
12 | def test_get_all_workspaces_validation(bad_workspaces_connection, respx_mock):
13 | respx_mock.get(f"{baseUrl}workspaces").mock(
14 | return_value=httpx.Response(404, json=bad_workspaces_connection)
15 | )
16 | result = runner.invoke(app, ["workspaces"])
17 | assert "404" in result.stdout
18 |
19 |
20 | def test_get_all_workspaces_success(good_workspaces_connection, respx_mock):
21 | respx_mock.get(f"{baseUrl}workspaces").mock(
22 | return_value=httpx.Response(200, json=good_workspaces_connection)
23 | )
24 | result = runner.invoke(app, ["workspaces"])
25 | assert "pydad" in result.stdout
26 |
27 |
28 | def test_get_all_workspaces_NetworkError(respx_mock):
29 | respx_mock.get(f"{baseUrl}workspaces").mock(side_effect=httpx.ConnectError)
30 | result = runner.invoke(app, ["workspaces"])
31 | assert "Error in connecting to Geoserver" in result.stdout
32 |
33 |
34 | # Test - get_workspace
35 | def test_get_workspace_validation(bad_workspace_connection, respx_mock):
36 | respx_mock.get(f"{baseUrl}workspaces/sfsf").mock(
37 | return_value=httpx.Response(404, json=bad_workspace_connection)
38 | )
39 | result = runner.invoke(app, ["workspace", "--workspace", "sfsf"])
40 | assert "Result not found" in result.stdout
41 |
42 |
43 | def test_get_workspace_success(good_workspace_connection, respx_mock):
44 | respx_mock.get(f"{baseUrl}workspaces/pydad").mock(
45 | return_value=httpx.Response(200, json=good_workspace_connection)
46 | )
47 | result = runner.invoke(app, ["workspace", "--workspace", "pydad"])
48 | assert "pydad" in result.stdout
49 |
50 |
51 | def test_get_workspace_ConnectError(respx_mock):
52 | respx_mock.get(f"{baseUrl}workspaces/pydad").mock(side_effect=httpx.ConnectError)
53 | result = runner.invoke(app, ["workspace", "--workspace", "pydad"])
54 | assert "Error in connecting to Geoserver" in result.stdout
55 |
56 |
57 | # Test - get_vector_stores_in_workspaces
58 | def test_get_vector_stores_in_workspaces_validation(
59 | invalid_datastores_model_connection, respx_mock
60 | ):
61 | respx_mock.get(f"{baseUrl}workspaces/sfsf/datastores").mock(
62 | return_value=httpx.Response(404, json=invalid_datastores_model_connection)
63 | )
64 | result = runner.invoke(app, ["vector-st-wp", "--workspace", "sfsf"])
65 | assert "Result not found" in result.stdout
66 |
67 |
68 | def test_get_vector_stores_in_workspaces_success(
69 | good_datastores_model_connection, respx_mock
70 | ):
71 | respx_mock.get(f"{baseUrl}workspaces/sfsf/datastores").mock(
72 | return_value=httpx.Response(200, json=good_datastores_model_connection)
73 | )
74 | result = runner.invoke(app, ["vector-st-wp", "--workspace", "sfsf"])
75 | assert "jumper" in result.stdout
76 |
77 |
78 | def test_get_vector_stores_in_workspaces_ConnectError(respx_mock):
79 | respx_mock.get(f"{baseUrl}workspaces/sfsf/datastores").mock(
80 | side_effect=httpx.ConnectError
81 | )
82 | result = runner.invoke(app, ["vector-st-wp", "--workspace", "sfsf"])
83 | assert "Error in connecting to Geoserver" in result.stdout
84 |
85 |
86 | # Test - get_raster_stores_in_workspaces
87 | def test_get_raster_stores_in_workspaces_validation(
88 | invalid_coverages_stores_model_connection, respx_mock
89 | ):
90 | respx_mock.get(f"{baseUrl}workspaces/sfsf/coveragestores").mock(
91 | return_value=httpx.Response(404, json=invalid_coverages_stores_model_connection)
92 | )
93 | result = runner.invoke(app, ["raster-st-wp", "--workspace", "sfsf"])
94 | assert "Result not found" in result.stdout
95 |
96 |
97 | def test_get_raster_stores_in_workspaces_success(
98 | good_coverages_stores_model_connection, respx_mock
99 | ):
100 | respx_mock.get(f"{baseUrl}workspaces/sfsf/coveragestores").mock(
101 | return_value=httpx.Response(200, json=good_coverages_stores_model_connection)
102 | )
103 | result = runner.invoke(app, ["raster-st-wp", "--workspace", "sfsf"])
104 | assert "RGB_125" in result.stdout
105 |
106 |
107 | def test_get_raster_stores_in_workspaces_ConnectError(respx_mock):
108 | respx_mock.get(f"{baseUrl}workspaces/sfsf/coveragestores").mock(
109 | side_effect=httpx.ConnectError
110 | )
111 | result = runner.invoke(app, ["raster-st-wp", "--workspace", "sfsf"])
112 | assert "Error in connecting to Geoserver" in result.stdout
113 |
114 |
115 | # Test - get_vector_store
116 | def test_get_vector_store_validation(invalid_datastore_model_connection, respx_mock):
117 | respx_mock.get(f"{baseUrl}workspaces/sfsf/datastores/jumper.json").mock(
118 | return_value=httpx.Response(404, json=invalid_datastore_model_connection)
119 | )
120 | result = runner.invoke(
121 | app, ["vector-store", "--workspace", "sfsf", "--store", "jumper"]
122 | )
123 | assert "Result not found" in result.stdout
124 |
125 |
126 | def test_get_vector_store_success(good_datastore_model_connection, respx_mock):
127 | respx_mock.get(f"{baseUrl}workspaces/sfsf/datastores/jumper.json").mock(
128 | return_value=httpx.Response(200, json=good_datastore_model_connection)
129 | )
130 | result = runner.invoke(
131 | app, ["vector-store", "--workspace", "sfsf", "--store", "jumper"]
132 | )
133 | assert "jumper" in result.stdout
134 |
135 |
136 | def test_get_vector_store_ConnectError(respx_mock):
137 | respx_mock.get(f"{baseUrl}workspaces/sfsf/datastores/jumper.json").mock(
138 | side_effect=httpx.ConnectError
139 | )
140 | result = runner.invoke(
141 | app, ["vector-store", "--workspace", "sfsf", "--store", "jumper"]
142 | )
143 | assert "Error in connecting to Geoserver" in result.stdout
144 |
145 |
146 | # Test - get_raster_store
147 | def test_get_raster_store_validation(
148 | invalid_coverages_store_model_connection, respx_mock
149 | ):
150 | respx_mock.get(f"{baseUrl}workspaces/cite/coveragestores/RGB_125.json").mock(
151 | return_value=httpx.Response(404, json=invalid_coverages_store_model_connection)
152 | )
153 | result = runner.invoke(
154 | app, ["raster-store", "--workspace", "cite", "--store", "RGB_125"]
155 | )
156 | assert "Result not found" in result.stdout
157 |
158 |
159 | def test_get_raster_store_success(good_coverages_store_model_connection, respx_mock):
160 | respx_mock.get(f"{baseUrl}workspaces/cite/coveragestores/RGB_125.json").mock(
161 | return_value=httpx.Response(200, json=good_coverages_store_model_connection)
162 | )
163 | result = runner.invoke(
164 | app, ["raster-store", "--workspace", "cite", "--store", "RGB_125"]
165 | )
166 | assert "RGB_125" in result.stdout
167 |
168 |
169 | def test_get_raster_store_ConnectError(respx_mock):
170 | respx_mock.get(f"{baseUrl}workspaces/cite/coveragestores/RGB_125.json").mock(
171 | side_effect=httpx.ConnectError
172 | )
173 | result = runner.invoke(
174 | app, ["raster-store", "--workspace", "cite", "--store", "RGB_125"]
175 | )
176 | assert "Error in connecting to Geoserver" in result.stdout
177 |
178 |
179 | # Test - get_all_styles
180 | def test_get_all_styles_validation(invalid_all_styles_model_connection, respx_mock):
181 | respx_mock.get(f"{baseUrl}styles").mock(
182 | return_value=httpx.Response(404, json=invalid_all_styles_model_connection)
183 | )
184 | result = runner.invoke(app, ["styles"])
185 | assert "Result not found" in result.stdout
186 |
187 |
188 | def test_get_all_styles_success(good_all_styles_model_connection, respx_mock):
189 | respx_mock.get(f"{baseUrl}styles").mock(
190 | return_value=httpx.Response(200, json=good_all_styles_model_connection)
191 | )
192 | result = runner.invoke(app, ["styles"])
193 | assert "CUSD 2020 Census" in result.stdout
194 |
195 |
196 | def test_get_all_styles_ConnectError(respx_mock):
197 | respx_mock.get(f"{baseUrl}styles").mock(side_effect=httpx.ConnectError)
198 | result = runner.invoke(app, ["styles"])
199 | assert "Error in connecting to Geoserver" in result.stdout
200 |
201 |
202 | # Test - get_style
203 | def test_get_style_validation(invalid_style_model_connection, respx_mock):
204 | respx_mock.get(f"{baseUrl}styles/burg.json").mock(
205 | return_value=httpx.Response(404, json=invalid_style_model_connection)
206 | )
207 | result = runner.invoke(app, ["style", "--style", "burg"])
208 | assert "Result not found" in result.stdout
209 |
210 |
211 | def test_get_style_success(good_style_model_connection, respx_mock):
212 | respx_mock.get(f"{baseUrl}styles/burg.json").mock(
213 | return_value=httpx.Response(200, json=good_style_model_connection)
214 | )
215 | result = runner.invoke(app, ["style", "--style", "burg"])
216 | assert "burg" in result.stdout
217 |
218 |
219 | def test_get_style_ConnectError(respx_mock):
220 | respx_mock.get(f"{baseUrl}styles/burg.json").mock(side_effect=httpx.ConnectError)
221 | result = runner.invoke(app, ["style", "--style", "burg"])
222 | assert "Error in connecting to Geoserver" in result.stdout
223 |
224 |
225 | # Test - create_workspace
226 | def test_create_workspace_validation(invalid_new_workspace_connection, respx_mock):
227 | respx_mock.post(f"{baseUrl}workspaces?default=False").mock(
228 | return_value=httpx.Response(404, json=invalid_new_workspace_connection)
229 | )
230 | result = runner.invoke(
231 | app,
232 | ["create-workspace", "--workspace", "burg", "--no-default", "--no-isolated"],
233 | )
234 | assert "Result not found" in result.stdout
235 |
236 |
237 | def test_create_workspace_success(good_new_workspace_connection, respx_mock):
238 | respx_mock.post(f"{baseUrl}workspaces?default=False").mock(
239 | return_value=httpx.Response(201, json=good_new_workspace_connection)
240 | )
241 | result = runner.invoke(
242 | app, ["create-workspace", "--workspace", "pydad", "--no-default", "--isolated"]
243 | )
244 | assert "Data added successfully" in result.stdout
245 |
246 |
247 | def test_create_workspace_ConnectError(respx_mock):
248 | respx_mock.post(f"{baseUrl}workspaces?default=False").mock(
249 | side_effect=httpx.ConnectError
250 | )
251 | result = runner.invoke(
252 | app, ["create-workspace", "--workspace", "pydad", "--no-default", "--isolated"]
253 | )
254 | assert "Error in connecting to Geoserver" in result.stdout
255 |
256 |
257 | # Test - pg_store
258 | def test_pg_store_validation(invalid_new_pg_store_connection, respx_mock):
259 | respx_mock.post(f"{baseUrl}workspaces/cesium/datastores/").mock(
260 | return_value=httpx.Response(404, json=invalid_new_pg_store_connection)
261 | )
262 | result = runner.invoke(
263 | app,
264 | [
265 | "create-pg-store",
266 | "--name",
267 | "pgg",
268 | "--workspace",
269 | "cesium",
270 | "--dbname",
271 | "postgres",
272 | "--dbpwd",
273 | "postgres",
274 | ],
275 | )
276 | assert "Result not found" in result.stdout
277 |
278 |
279 | def test_pg_store_success(good_new_workspace_connection, respx_mock):
280 | respx_mock.post(f"{baseUrl}workspaces/cesium/datastores/").mock(
281 | return_value=httpx.Response(201, json=good_new_workspace_connection)
282 | )
283 | result = runner.invoke(
284 | app,
285 | [
286 | "create-pg-store",
287 | "--name",
288 | "pgg",
289 | "--workspace",
290 | "cesium",
291 | "--dbname",
292 | "postgres",
293 | "--dbpwd",
294 | "postgres",
295 | ],
296 | )
297 | assert "Data added successfully" in result.stdout
298 |
299 |
300 | def test_pg_store_ConnectError(respx_mock):
301 | respx_mock.post(f"{baseUrl}workspaces/cesium/datastores/").mock(
302 | side_effect=httpx.ConnectError
303 | )
304 | result = runner.invoke(
305 | app,
306 | [
307 | "create-pg-store",
308 | "--name",
309 | "pgg",
310 | "--workspace",
311 | "cesium",
312 | "--dbname",
313 | "postgres",
314 | "--dbpwd",
315 | "postgres",
316 | ],
317 | )
318 | assert "Error in connecting to Geoserver" in result.stdout
319 |
320 |
321 | # Test - get_all_layers
322 | def test_get_all_layers_validation(bad_layers_connection, respx_mock):
323 | respx_mock.get(f"{baseUrl}layers").mock(
324 | return_value=httpx.Response(404, json=bad_layers_connection)
325 | )
326 | result = runner.invoke(app, ["layers"])
327 | assert "404" in result.stdout
328 |
329 |
330 | def test_get_all_layers_success(good_layers_connection, respx_mock):
331 | respx_mock.get(f"{baseUrl}layers").mock(
332 | return_value=httpx.Response(200, json=good_layers_connection)
333 | )
334 | result = runner.invoke(app, ["layers"])
335 | assert "tiger:giant_polygon" in result.stdout
336 |
337 |
338 | def test_get_all_layers_NetworkError(respx_mock):
339 | respx_mock.get(f"{baseUrl}layers").mock(side_effect=httpx.ConnectError)
340 | result = runner.invoke(app, ["layers"])
341 | assert "Error in connecting to Geoserver" in result.stdout
342 |
343 |
344 | # Test - get_layer
345 | def test_get_layer_validation(bad_layer_connection, respx_mock):
346 | respx_mock.get(f"{baseUrl}layers/tiger:poi").mock(
347 | return_value=httpx.Response(404, json=bad_layer_connection)
348 | )
349 | result = runner.invoke(app, ["layer", "--workspace", "tiger", "--layer", "poi"])
350 | assert "404" in result.stdout
351 |
352 |
353 | def test_get_layer_success(good_layer_connection, respx_mock):
354 | respx_mock.get(f"{baseUrl}layers/tiger:poi").mock(
355 | return_value=httpx.Response(200, json=good_layer_connection)
356 | )
357 | result = runner.invoke(app, ["layer", "--workspace", "tiger", "--layer", "poi"])
358 | assert "poi" in result.stdout
359 |
360 |
361 | def test_get_layer_NetworkError(respx_mock):
362 | respx_mock.get(f"{baseUrl}layers/tiger:poi").mock(side_effect=httpx.ConnectError)
363 | result = runner.invoke(app, ["layer", "--workspace", "tiger", "--layer", "poi"])
364 | assert "Error in connecting to Geoserver" in result.stdout
365 |
366 |
367 | # Test - get_all_layer_groups
368 | def test_get_all_layer_groups_validation(bad_layer_groups_connection, respx_mock):
369 | respx_mock.get(f"{baseUrl}workspaces/ne/layergroups").mock(
370 | return_value=httpx.Response(404, json=bad_layer_groups_connection)
371 | )
372 | result = runner.invoke(app, ["layer-groups", "--workspace", "ne"])
373 | assert "404" in result.stdout
374 |
375 |
376 | def test_get_all_layer_groups_success(good_layer_groups_connection, respx_mock):
377 | respx_mock.get(f"{baseUrl}workspaces/ne/layergroups").mock(
378 | return_value=httpx.Response(200, json=good_layer_groups_connection)
379 | )
380 | result = runner.invoke(app, ["layer-groups", "--workspace", "ne"])
381 | assert "tg" in result.stdout
382 |
383 |
384 | def test_get_all_layer_groups_NetworkError(respx_mock):
385 | respx_mock.get(f"{baseUrl}workspaces/ne/layergroups").mock(
386 | side_effect=httpx.ConnectError
387 | )
388 | result = runner.invoke(app, ["layer-groups", "--workspace", "ne"])
389 | assert "Error in connecting to Geoserver" in result.stdout
390 |
391 |
392 | # Test - all_geofence_rules
393 | def test_all_geofence_rules_validation(bad_all_geofence_rules_connection, respx_mock):
394 | respx_mock.get(f"{baseUrl}about/status.json").mock(
395 | return_value=httpx.Response(
396 | 200, json={"statuss": {"status": [{"name": "geofence"}]}}
397 | )
398 | )
399 | respx_mock.get(
400 | f"{baseUrl}geofence/rules/", headers={"Accept": "application/json"}
401 | ).mock(return_value=httpx.Response(404, json=bad_all_geofence_rules_connection))
402 | result = runner.invoke(app, ["geofence-rules"])
403 | assert "404" in result.stdout
404 |
405 |
406 | def test_all_geofence_rules_success(good_all_geofence_rules_connection, respx_mock):
407 | respx_mock.get(f"{baseUrl}about/status.json").mock(
408 | return_value=httpx.Response(
409 | 200, json={"statuss": {"status": [{"name": "geofence"}]}}
410 | )
411 | )
412 | respx_mock.get(
413 | f"{baseUrl}geofence/rules/", headers={"Accept": "application/json"}
414 | ).mock(return_value=httpx.Response(200, json=good_all_geofence_rules_connection))
415 | result = runner.invoke(app, ["geofence-rules"])
416 | assert "2" in result.stdout
417 |
418 |
419 | def test_all_geofence_rules_NetworkError(respx_mock):
420 | respx_mock.get(f"{baseUrl}about/status.json").mock(
421 | return_value=httpx.Response(
422 | 200, json={"statuss": {"status": [{"name": "geofence"}]}}
423 | )
424 | )
425 | respx_mock.get(
426 | f"{baseUrl}geofence/rules/", headers={"Accept": "application/json"}
427 | ).mock(side_effect=httpx.ConnectError)
428 | result = runner.invoke(app, ["geofence-rules"])
429 | assert "Error in connecting to Geoserver" in result.stdout
430 |
--------------------------------------------------------------------------------
/tests/test_models.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pydantic import ValidationError
3 |
4 | from geoserverx.models.coverages_store import (
5 | CoveragesStoreInBulk,
6 | CoveragesStoreModel,
7 | CoveragesStoresDict,
8 | CoveragesStoresModel,
9 | )
10 | from geoserverx.models.data_store import (
11 | DatastoreConnection,
12 | DataStoreDict,
13 | DataStoreInBulk,
14 | DatastoreItem,
15 | DataStoreModel,
16 | DataStoresModel,
17 | EntryItem,
18 | )
19 | from geoserverx.models.geofence import RulesResponse
20 | from geoserverx.models.layer_group import LayerGroupsModel
21 | from geoserverx.models.style import (
22 | AllStylesModel,
23 | SingleStyle,
24 | StyleModel,
25 | allStyle,
26 | allStyleList,
27 | )
28 | from geoserverx.models.workspace import (
29 | NewWorkspace,
30 | WorkspaceInBulk,
31 | WorkspaceModel,
32 | WorkspacesModel,
33 | workspaceDict,
34 | )
35 |
36 |
37 | # Testing DataStoreInBulk
38 | def test_datastoreinbulk_connection(good_datastore_in_bulk_connection):
39 | ds_connection = DataStoreInBulk(**good_datastore_in_bulk_connection)
40 | assert ds_connection.name == "just"
41 | assert ds_connection.href == "https://www.linkedin.com/notifications/"
42 |
43 |
44 | def test_datastoreinbulk_failure(bad_datastore_in_bulk_connection):
45 | with pytest.raises(ValidationError):
46 | DataStoreInBulk(**bad_datastore_in_bulk_connection)
47 |
48 |
49 | # Testing DataStoreDict
50 | def test_datastoredict_connection(good_datastore_dict_connection):
51 | ds_connection = DataStoreDict(**good_datastore_dict_connection)
52 | assert ds_connection.dataStore[0].name == "just"
53 |
54 |
55 | def test_datastoredict_failure(bad_datastore_dict_connection):
56 | with pytest.raises(ValidationError):
57 | DataStoreDict(**bad_datastore_dict_connection)
58 |
59 |
60 | # Testing DataStoresModel
61 | def test_datastoresmodel_connection(good_datastores_model_connection):
62 | ds_connection = DataStoresModel(**good_datastores_model_connection)
63 | assert ds_connection.dataStores.dataStore[0].name == "jumper"
64 |
65 |
66 | def test_datastoresmodel_failure(bad_datastores_model_connection):
67 | with pytest.raises(ValidationError):
68 | DataStoresModel(**bad_datastores_model_connection)
69 |
70 |
71 | # Testing DatastoreConnection
72 | def test_datastoreconnection_connection(good_datastore_connection_connection):
73 | ds_connection = DatastoreConnection(**good_datastore_connection_connection)
74 | assert ds_connection.key == "just"
75 |
76 |
77 | def test_datastoreconnection_failure(bad_datastore_connection_connection):
78 | with pytest.raises(ValidationError):
79 | DatastoreConnection(**bad_datastore_connection_connection)
80 |
81 |
82 | # Testing EntryItem
83 | def test_entryitem_connection(good_entry_item_connection):
84 | ds_connection = EntryItem(**good_entry_item_connection)
85 | assert ds_connection.entry[0].key == "just"
86 |
87 |
88 | def test_entryitem_failure(bad_entry_item_connection):
89 | with pytest.raises(ValidationError):
90 | EntryItem(**bad_entry_item_connection)
91 |
92 |
93 | # Testing DatastoreItem
94 | def test_datastoreitem_connection(good_datastore_item_connection):
95 | ds_connection = DatastoreItem(**good_datastore_item_connection)
96 | assert ds_connection.connectionParameters.entry[0].key == "just"
97 |
98 |
99 | def test_datastoreitem_failure(bad_datastore_item_connection):
100 | with pytest.raises(ValidationError):
101 | DatastoreItem(**bad_datastore_item_connection)
102 |
103 |
104 | # Testing DataStoreModel
105 | def test_datastoremodel_connection(good_datastore_model_connection):
106 | ds_connection = DataStoreModel(**good_datastore_model_connection)
107 | assert ds_connection.dataStore.name == "jumper"
108 |
109 |
110 | def test_datastoremodel_failure(bad_datastore_model_connection):
111 | with pytest.raises(ValidationError):
112 | DataStoreModel(**bad_datastore_model_connection)
113 |
114 |
115 | # Testing CoveragesStoreInBulk
116 | def test_coveragesstoreinbulk_connection(good_coverages_store_in_bulk_connection):
117 | ds_connection = CoveragesStoreInBulk(**good_coverages_store_in_bulk_connection)
118 | assert ds_connection.name == "just"
119 | assert ds_connection.href == "https://www.linkedin.com/notifications/"
120 |
121 |
122 | def test_coveragesstoreinbulk_failure(bad_coverages_store_in_bulk_connection):
123 | with pytest.raises(ValidationError):
124 | CoveragesStoreInBulk(**bad_coverages_store_in_bulk_connection)
125 |
126 |
127 | # Testing CoveragesStoresDict
128 | def test_coveragesstoresdict_connection(good_coverages_stores_dict_connection):
129 | ds_connection = CoveragesStoresDict(**good_coverages_stores_dict_connection)
130 | assert ds_connection.coverageStore[0].name == "just"
131 |
132 |
133 | def test_coveragesstoresdict_failure(bad_coverages_stores_dict_connection):
134 | with pytest.raises(ValidationError):
135 | CoveragesStoresDict(**bad_coverages_stores_dict_connection)
136 |
137 |
138 | # Testing CoveragesStoresModel
139 | def test_coveragesstoresmodel_connection(good_coverages_stores_model_connection):
140 | ds_connection = CoveragesStoresModel(**good_coverages_stores_model_connection)
141 | assert ds_connection.coverageStores.coverageStore[0].name == "RGB_125"
142 |
143 |
144 | def test_coveragesstoresmodel_failure(bad_coverages_stores_model_connection):
145 | with pytest.raises(ValidationError):
146 | CoveragesStoresModel(**bad_coverages_stores_model_connection)
147 |
148 |
149 | # Testing CoveragesStoreModel
150 | def test_coveragesstoremodel_connection(good_coverages_store_model_connection):
151 | ds_connection = CoveragesStoreModel(**good_coverages_store_model_connection)
152 | assert ds_connection.coverageStore.name == "RGB_125"
153 |
154 |
155 | def test_coveragesstoremodel_failure(bad_coverages_store_model_connection):
156 | with pytest.raises(ValidationError):
157 | CoveragesStoreModel(**bad_coverages_store_model_connection)
158 |
159 |
160 | # Testing SingleStyle
161 | def test_singlestyle_connection(good_single_style_dict_connection):
162 | ds_connection = SingleStyle(**good_single_style_dict_connection)
163 | assert ds_connection.name == "burg"
164 |
165 |
166 | def test_singlestyle_failure(bad_single_style_dict_connection):
167 | with pytest.raises(ValidationError):
168 | SingleStyle(**bad_single_style_dict_connection)
169 |
170 |
171 | # Testing StyleModel
172 | def test_stylemodel_connection(good_style_model_connection):
173 | ds_connection = StyleModel(**good_style_model_connection)
174 | assert ds_connection.style.name == "burg"
175 |
176 |
177 | def test_stylemodel_failure(bad_style_model_connection):
178 | with pytest.raises(ValidationError):
179 | StyleModel(**bad_style_model_connection)
180 |
181 |
182 | # Testing allStyleList
183 | def test_allstylelist_connection(good_all_style_list_connection):
184 | ds_connection = allStyleList(**good_all_style_list_connection)
185 | assert ds_connection.name == "CUSD 2020 Census Blocks"
186 |
187 |
188 | def test_allstylelist_failure(bad_all_style_list_connection):
189 | with pytest.raises(ValidationError):
190 | allStyleList(**bad_all_style_list_connection)
191 |
192 |
193 | # Testing allStyle
194 | def test_allstyle_connection(good_all_style_dict_connection):
195 | ds_connection = allStyle(**good_all_style_dict_connection)
196 | assert ds_connection.style[0].name == "CUSD 2020 Census Blocks"
197 |
198 |
199 | def test_allstyle_failure(bad_all_style_dict_connection):
200 | with pytest.raises(ValidationError):
201 | allStyle(**bad_all_style_dict_connection)
202 |
203 |
204 | # Testing AllStylesModel
205 | def test_allstylesmodel_connection(good_all_styles_model_connection):
206 | ds_connection = AllStylesModel(**good_all_styles_model_connection)
207 | assert ds_connection.styles.style[0].name == "CUSD 2020 Census Blocks"
208 |
209 |
210 | def test_allstylesmodel_failure(bad_all_styles_model_connection):
211 | with pytest.raises(ValidationError):
212 | AllStylesModel(**bad_all_styles_model_connection)
213 |
214 |
215 | # Testing WorkspaceInBulk
216 | def test_workspaceinbulk_connection(good_workspace_in_bulk_connection):
217 | ds_connection = WorkspaceInBulk(**good_workspace_in_bulk_connection)
218 | assert ds_connection.name == "pydad"
219 |
220 |
221 | def test_workspaceinbulk_failure(bad_workspace_in_bulk_connection):
222 | with pytest.raises(ValidationError):
223 | WorkspaceInBulk(**bad_workspace_in_bulk_connection)
224 |
225 |
226 | # Testing workspaceDict
227 | def test_workspacedict_connection(good_workspace_dict_connection):
228 | ds_connection = workspaceDict(**good_workspace_dict_connection)
229 | assert ds_connection.workspace[0].name == "pydad"
230 |
231 |
232 | def test_workspacedict_failure(bad_workspace_dict_connection):
233 | with pytest.raises(ValidationError):
234 | workspaceDict(**bad_workspace_dict_connection)
235 |
236 |
237 | # Testing WorkspacesModel
238 | def test_workspacesmodel_connection(good_workspaces_model_connection):
239 | ds_connection = WorkspacesModel(**good_workspaces_model_connection)
240 | assert ds_connection.workspaces.workspace[0].name == "pydad"
241 |
242 |
243 | def test_workspacesmodel_failure(bad_workspaces_model_connection):
244 | with pytest.raises(ValidationError):
245 | WorkspacesModel(**bad_workspaces_model_connection)
246 |
247 |
248 | # Testing WorkspaceModel
249 | def test_workspacemodel_connection(good_workspace_model_connection):
250 | ds_connection = WorkspaceModel(**good_workspace_model_connection)
251 | assert ds_connection.workspace.name == "pydad"
252 |
253 |
254 | def test_workspacemodel_failure(bad_workspace_model_connection):
255 | with pytest.raises(ValidationError):
256 | WorkspaceModel(**bad_workspace_model_connection)
257 |
258 |
259 | # Testing NewWorkspace
260 | def test_newworkspace_connection(good_new_workspace_connection):
261 | ds_connection = NewWorkspace(**good_new_workspace_connection)
262 | assert ds_connection.workspace.name == "pydad"
263 |
264 |
265 | def test_newworkspace_failure(bad_new_workspace_connection):
266 | with pytest.raises(ValidationError):
267 | NewWorkspace(**bad_new_workspace_connection)
268 |
269 |
270 | # Testing LayerGroupsModel
271 | def test_layergroupsmodel_connection(good_layer_groups_connection):
272 | ds_connection = LayerGroupsModel(**good_layer_groups_connection)
273 | assert ds_connection.layerGroups.layerGroup[0].name == "tg"
274 |
275 |
276 | def test_layergroupsmodel_failure(bad_layer_groups_connection):
277 | with pytest.raises(ValidationError):
278 | LayerGroupsModel(**bad_layer_groups_connection)
279 |
280 |
281 | # Testing LayerGroupsModel
282 | def test_RulesResponse_connection(good_all_geofence_rules_connection):
283 | ds_connection = RulesResponse(**good_all_geofence_rules_connection)
284 | assert ds_connection.count == 2
285 |
286 |
287 | def test_RulesResponse_failure(bad_all_geofence_rules_connection):
288 | with pytest.raises(ValidationError):
289 | RulesResponse(**bad_all_geofence_rules_connection)
290 |
--------------------------------------------------------------------------------