Coverage for gws-app/gws/plugin/qfieldcloud/action.py: 98%
40 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-23 20:06 +0200
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-23 20:06 +0200
1"""QField Cloud API action."""
3from typing import Optional
5import gws
6import gws.base.auth
7import gws.base.action
8import gws.lib.mime
9import gws.lib.jsonx
11from . import core, action_base, action_handler
14@gws.ext.config.action('qfieldcloud')
15class Config(gws.ConfigWithAccess):
16 """Endpoint for QField clients that emulates the QFieldCloud API."""
18 projects: list[core.ProjectConfig]
19 """Projects offered to QField clients."""
20 auth: Optional[gws.base.auth.method.Config]
21 """Token authentication method for QField clients."""
22 deltaLifeTime: gws.Duration = '30d'
23 """How long uploaded changes are kept on the server for status requests. (added in 8.5)"""
26@gws.ext.props.action('qfieldcloud')
27class Props(gws.base.action.Props):
28 pass
31@gws.ext.object.action('qfieldcloud')
32class Object(action_base.BaseAction):
33 """QField Cloud API action.
35 Emulates the QFieldCloud API for the QField app: authenticates clients by
36 token, lists the configured QField projects, creates packages in background
37 jobs and applies deltas and file uploads from the devices.
38 """
40 @gws.ext.command.raw('qfieldcloudApi')
41 def raw_request(self, req: gws.WebRequester, p: gws.Request) -> gws.ContentResponse:
42 """Handle a QFieldCloud API request."""
43 try:
44 return self.get_handler().handle(req, p)
45 except gws.NotFoundError as exc:
46 return _error_response(404, 'object_not_found', exc)
47 except gws.AuthenticationError as exc:
48 return _error_response(401, 'authentication_failed', exc)
49 except gws.ForbiddenError as exc:
50 return _error_response(403, 'permission_denied', exc)
51 except gws.BadRequestError as exc:
52 return _error_response(400, 'validation_error', exc)
53 except core.DeltaApplyError as exc:
54 return _error_response(409, 'delta_apply_failed', exc, str(exc))
56 def get_handler(self) -> action_handler.Handler:
57 """Return a new request handler. Override to use a custom handler.
59 Returns:
60 Request handler.
61 """
62 return action_handler.Handler(self)
65##
68def _error_response(status: int, code: str, exc: Exception, message: str = '') -> gws.ContentResponse:
69 """Log an error and return a JSON error response, with an optional message shown in QField."""
70 gws.log.warning(f'qfieldcloudApi: {status} {code} cause={exc!r}')
71 js = {'code': code}
72 if message:
73 js['message'] = message
74 return gws.ContentResponse(
75 status=status,
76 content=gws.lib.jsonx.to_string(js),
77 mimeType=gws.lib.mime.JSON,
78 )