easyvista-python-client 0.4.2__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/CHANGELOG.md +72 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/PKG-INFO +1 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/content.rst +54 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/user_guide.rst +3 -2
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/vendor-api-reference.md +10 -4
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_async/client.py +107 -8
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_sync/client.py +107 -8
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_version.py +1 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/content/__init__.py +6 -2
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/content/conversion.py +235 -21
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/resources/requests.py +68 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/workflow.py +8 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/pyproject.toml +1 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/skills/easyvista-asset-workflow/SKILL.md +1 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/skills/easyvista-client-setup/SKILL.md +1 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/skills/easyvista-directory/SKILL.md +1 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/skills/easyvista-document-workflow/SKILL.md +1 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/skills/easyvista-instance-discovery/SKILL.md +1 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/skills/easyvista-reporting-and-context/SKILL.md +1 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/skills/easyvista-search-syntax/SKILL.md +1 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/skills/easyvista-ticket-actions/SKILL.md +12 -6
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/skills/easyvista-ticket-workflow/SKILL.md +35 -1
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/.gitignore +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/.pre-commit-config.yaml +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/.readthedocs.yaml +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/CONTRIBUTING.md +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/LICENSE +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/README.md +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/_static/.gitkeep +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/_templates/.gitkeep +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/api_reference.rst +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/conf.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/development.rst +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/index.rst +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/installation.rst +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/publishing.rst +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/sponsoring.rst +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/__init__.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_async/__init__.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_async/_concurrency.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_async/_transport.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_fields.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_html.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_sync/__init__.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_sync/_concurrency.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_sync/_transport.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/_transport.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/config.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/context.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/directory.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/discovery.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/exceptions.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/field_model.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/filters.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/models/__init__.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/models/action.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/models/asset.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/models/common.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/models/department.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/models/document.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/models/employee.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/models/generic.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/models/request.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/pagination.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/py.typed +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/references.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/reporting.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/resources/__init__.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/resources/actions.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/resources/assets.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/resources/departments.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/resources/descriptor.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/resources/discovery.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/resources/documents.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/resources/employees.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/easyvista_python_client/timestamps.py +0 -0
- {easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/skills/README.md +0 -0
|
@@ -15,6 +15,75 @@ is the error. Tags carry no `v` prefix.
|
|
|
15
15
|
|
|
16
16
|
## [Unreleased]
|
|
17
17
|
|
|
18
|
+
## [0.5.0] - 2026-11-10
|
|
19
|
+
|
|
20
|
+
Two more of the vendor's ticket requests, and a guard that names one more
|
|
21
|
+
column. Neither new method takes a status: the package still has no status
|
|
22
|
+
setter.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **`suspend_ticket(rfc_number, *, allow_workflow_effect, comment=None,
|
|
27
|
+
done_by_id=None)`** and **`restart_ticket(...)`** (same signature), the
|
|
28
|
+
vendor's suspend and restart requests
|
|
29
|
+
([suspend](https://docs.easyvista.com/docs/rest-api-suspend-an-incident-request.md),
|
|
30
|
+
[restart](https://docs.easyvista.com/docs/rest-api-reopen-an-incident-request.md)):
|
|
31
|
+
`PUT requests/{rfc_number}` with a `suspended` / `restarted` object. The guard
|
|
32
|
+
already named both bodies `WorkflowEffect.UNKNOWN`, so
|
|
33
|
+
`allow_workflow_effect` is a required keyword and the call is refused before
|
|
34
|
+
any request unless it includes `UNKNOWN`; an allowed request is sent once,
|
|
35
|
+
never retried. `comment` and `done_by_id` are left out of the body when
|
|
36
|
+
`None`. The return value is parsed as `close_ticket`'s is.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **BREAKING:** the workflow guard now names `STATUS_ID_ON_CREATE` on an
|
|
41
|
+
existing action (`update_action`, or `PUT actions/{id}` through `send`), as
|
|
42
|
+
it does `STATUS_ID_ON_TERMINATE`. A write that passed before is refused
|
|
43
|
+
without `allow_workflow_effect=WorkflowEffect.UNKNOWN`. Measured 2026-11-09
|
|
44
|
+
and 2026-11-10 on one instance (tier 4, may not generalise): a write to it
|
|
45
|
+
on an existing action changed where the ticket's status went. Creating an
|
|
46
|
+
action or a task is unchanged: the server overwrites the column on create.
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- **The `reassign_action` documentation about `OWNING_GROUP_ID`.** It said the
|
|
51
|
+
ticket's owning group does not follow a reassigned step. Measured 2026-11-04
|
|
52
|
+
to 2026-11-10 on one instance (several tickets, may not generalise): it
|
|
53
|
+
follows after a few seconds to about two minutes (consistent with a
|
|
54
|
+
server-side job; inferred, not observed), and `LAST_UPDATE` moves with it;
|
|
55
|
+
the first reading was taken too soon. The status still does not move, the
|
|
56
|
+
step is not started and no action row is added.
|
|
57
|
+
|
|
58
|
+
## [0.4.3] - 2026-10-08
|
|
59
|
+
|
|
60
|
+
A patch release of the content reader: a caller can recognise the images a
|
|
61
|
+
memo embeds and decide, link by link, what a read writes. Without the new
|
|
62
|
+
argument a read is unchanged, byte for byte; writing is unchanged, and so is
|
|
63
|
+
everything outside `easyvista_python_client.content`.
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
|
|
67
|
+
- **`from_transport(..., rewrite_link=callback)`.** The callback is called with
|
|
68
|
+
a `Link` for each link and image the read meets outside code -- an image
|
|
69
|
+
before the link around it, with that link's `enclosing_href` -- and its
|
|
70
|
+
answer is written: `None` keeps the reader's output, a `str` is written as
|
|
71
|
+
literal text, a `Link` replaces the target (an empty `href` drops a link and
|
|
72
|
+
keeps its content, or writes an image as its text). The callback is held in
|
|
73
|
+
a context variable, so concurrent reads each use their own. What it raises
|
|
74
|
+
reaches the caller unchanged; a `ValueError` is not taken for markdownify's
|
|
75
|
+
and answered with the text fallback.
|
|
76
|
+
- **`Link` and `RewriteLink`**, exported from `easyvista_python_client.content`.
|
|
77
|
+
- **`EasyvistaContentConverter.document_id_of(src)`** returns the
|
|
78
|
+
`DOCUMENT_ID` of an embedded image's `src`
|
|
79
|
+
(`@@EMBEDDED_IMAGE_PATH@@<DOCUMENT_ID>`, how EasyVista's editor embeds a
|
|
80
|
+
pasted image), and **`document_image(document_id, alt=...)`** the Markdown
|
|
81
|
+
of one, spelled as the reader spells it. A `Link` for such an image carries
|
|
82
|
+
its `document_id`. Measured on one instance on 2026-10-07: an image written
|
|
83
|
+
that way through the API displays, for an attachment pasted in the editor
|
|
84
|
+
and for one uploaded with `add_document`.
|
|
85
|
+
- `EasyvistaContentConverter.EMBEDDED_IMAGE_PREFIX`.
|
|
86
|
+
|
|
18
87
|
## [0.4.2] - 2026-10-07
|
|
19
88
|
|
|
20
89
|
A patch release of the content writer: a link written into a memo opens in a
|
|
@@ -1661,7 +1730,9 @@ Initial public release.
|
|
|
1661
1730
|
status/error code, with non-retryable validation errors (HTTP 590, code 2013).
|
|
1662
1731
|
- `py.typed` marker — the package ships inline type information.
|
|
1663
1732
|
|
|
1664
|
-
[Unreleased]: https://github.com/baraline/easyvista_python_client/compare/0.
|
|
1733
|
+
[Unreleased]: https://github.com/baraline/easyvista_python_client/compare/0.5.0...HEAD
|
|
1734
|
+
[0.5.0]: https://github.com/baraline/easyvista_python_client/compare/0.4.3...0.5.0
|
|
1735
|
+
[0.4.3]: https://github.com/baraline/easyvista_python_client/compare/0.4.2...0.4.3
|
|
1665
1736
|
[0.4.2]: https://github.com/baraline/easyvista_python_client/compare/0.4.1...0.4.2
|
|
1666
1737
|
[0.4.1]: https://github.com/baraline/easyvista_python_client/compare/0.4.0...0.4.1
|
|
1667
1738
|
[0.4.0]: https://github.com/baraline/easyvista_python_client/compare/0.3.0...0.4.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: easyvista-python-client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Typed Python client for the EasyVista Service Manager REST API
|
|
5
5
|
Project-URL: Homepage, https://github.com/baraline/easyvista_python_client
|
|
6
6
|
Project-URL: Documentation, https://easyvista-python-client.readthedocs.io/en/latest/
|
|
@@ -234,6 +234,60 @@ reported as
|
|
|
234
234
|
:class:`~easyvista_python_client.exceptions.EasyvistaContentError`. A block
|
|
235
235
|
quote nested 100,000 levels deep rendered without one (measured 2026-10-02).
|
|
236
236
|
|
|
237
|
+
Embedded images, and rewriting links as they are read
|
|
238
|
+
-----------------------------------------------------
|
|
239
|
+
|
|
240
|
+
EasyVista's editor keeps an image pasted into a memo as an attachment of the
|
|
241
|
+
request, named like ``image_emb_001.png``, and embeds it as
|
|
242
|
+
``<img src="@@EMBEDDED_IMAGE_PATH@@<DOCUMENT_ID>" />``. Without a callback the
|
|
243
|
+
reader writes that as any other image, ````,
|
|
244
|
+
which displays nowhere but in EasyVista. Three helpers let a caller do better.
|
|
245
|
+
|
|
246
|
+
``EasyvistaContentConverter.document_id_of(src)`` returns the ``DOCUMENT_ID``
|
|
247
|
+
an image's ``src`` embeds, or ``None``. ``document_image(document_id, alt=...)``
|
|
248
|
+
returns the Markdown of such an image, spelled exactly as the reader spells
|
|
249
|
+
it, so ``to_transport`` writes the editor's form and ``from_transport`` reads
|
|
250
|
+
it back unchanged. **Tier 4** -- measured on one instance on 2026-10-07: an
|
|
251
|
+
image written that way through the API displays, both for an attachment
|
|
252
|
+
pasted in the editor and for one uploaded with ``add_document``. The memo
|
|
253
|
+
displays it only while the attachment is on the same request.
|
|
254
|
+
|
|
255
|
+
``from_transport(..., rewrite_link=callback)`` calls ``callback`` with a
|
|
256
|
+
:class:`~easyvista_python_client.content.Link` for each link and each image it
|
|
257
|
+
meets outside code, an image before the link around it. A ``Link`` carries the
|
|
258
|
+
``href`` (an image's ``src``), the ``text`` (an image's ``alt``), the
|
|
259
|
+
``title``, whether it is an ``image``, an embedded image's ``document_id``, and
|
|
260
|
+
for an image inside a link that link's ``enclosing_href``. What the callback
|
|
261
|
+
answers is written:
|
|
262
|
+
|
|
263
|
+
* ``None``: what the reader writes without a callback;
|
|
264
|
+
* a ``str``: that text, literally -- escaped as any text the memo displays, so
|
|
265
|
+
it cannot become markup;
|
|
266
|
+
* a ``Link``: a link's ``href`` and ``title`` around the link's content as
|
|
267
|
+
read, or an image's ``href`` as its ``src``, ``text`` as its ``alt`` and
|
|
268
|
+
``title``;
|
|
269
|
+
* a ``Link`` with an empty ``href``: a link is dropped and its content kept, an
|
|
270
|
+
image is written as its ``text``.
|
|
271
|
+
|
|
272
|
+
.. code-block:: python
|
|
273
|
+
|
|
274
|
+
from easyvista_python_client.content import EasyvistaContentConverter, Link
|
|
275
|
+
|
|
276
|
+
def name_attachments(link: Link) -> Link | str | None:
|
|
277
|
+
if link.document_id is not None:
|
|
278
|
+
return f"[attachment {link.document_id}]"
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
EasyvistaContentConverter.from_transport(memo, rewrite_link=name_attachments)
|
|
282
|
+
|
|
283
|
+
The callback is held for the duration of one read, per thread and per task,
|
|
284
|
+
so concurrent reads each use their own, and a read inside a callback has its
|
|
285
|
+
own too. What the callback raises reaches the caller unchanged -- a
|
|
286
|
+
``ValueError`` included, which the reader would otherwise take for one of
|
|
287
|
+
markdownify's and answer by reading the body as its text. An answer of any
|
|
288
|
+
other type raises ``TypeError``. It is not called for a value passed through
|
|
289
|
+
as Markdown (``plain_text_is_markdown=True``), nor for a body read as its text.
|
|
290
|
+
|
|
237
291
|
It is not a sanitiser
|
|
238
292
|
---------------------
|
|
239
293
|
|
|
@@ -473,8 +473,9 @@ rule on your instance can run on any write.
|
|
|
473
473
|
To escalate the open workflow step to another group without ending it, use
|
|
474
474
|
:meth:`~easyvista_python_client.EasyvistaClient.reassign_action` (measured
|
|
475
475
|
2026-10-02, one instance, so it may not generalise: the step stays open and the
|
|
476
|
-
status does not move;
|
|
477
|
-
|
|
476
|
+
status does not move; measured 2026-11-04 to 2026-11-10, several tickets, the
|
|
477
|
+
ticket's owning group follows the step's new group after a few seconds to about
|
|
478
|
+
two minutes).
|
|
478
479
|
|
|
479
480
|
Creating a ticket starts its workflow -- the vendor's create page lists "The
|
|
480
481
|
workflow associated with the ticket is started." among what a create does (tier
|
{easyvista_python_client-0.4.2 → easyvista_python_client-0.5.0}/docs/vendor-api-reference.md
RENAMED
|
@@ -287,10 +287,16 @@ again five seconds later); the step **stayed open** (`END_DATE_UT` empty); the t
|
|
|
287
287
|
status and `END_DATE_UT` **did not move**; the open actions were unchanged; and **no new
|
|
288
288
|
action rows** appeared. On the **first ticket only** the step was recorded as a type-20
|
|
289
289
|
action with `WORKFLOW_ID` set, its `WORKFLOW_ID` was unchanged, `DONE_BY_ID` stayed
|
|
290
|
-
empty and no ticket field changed
|
|
291
|
-
ticket
|
|
292
|
-
|
|
293
|
-
|
|
290
|
+
empty and no ticket field changed. The ticket's own `OWNING_GROUP_ID` was read on
|
|
291
|
+
that ticket only, and had not moved; that reading was taken too soon. Measured
|
|
292
|
+
2026-11-04 to 2026-11-10 on one instance (several tickets, may not generalise): the
|
|
293
|
+
ticket's `OWNING_GROUP_ID` **follows the step's new group** after a delay of a few
|
|
294
|
+
seconds to about two minutes (consistent with a server-side job; inferred, not
|
|
295
|
+
observed), and `LAST_UPDATE` moves with it; the status still does not move, the step
|
|
296
|
+
is not started and no action row is added. Also measured 2026-11-04 on one ticket:
|
|
297
|
+
after `done_by_id` was written on the step, the ticket's `OWNER_ID` followed about
|
|
298
|
+
1 min 45 s later. The person's effect on the step was not measured beyond that one
|
|
299
|
+
`OWNER_ID` observation. Whether the UI wizard's notifications fire is not
|
|
294
300
|
observable from the API.
|
|
295
301
|
|
|
296
302
|
**Business rules can fire on any write** — "On Insert/On Update" of any record
|
|
@@ -610,6 +610,99 @@ class AsyncEasyvistaClient:
|
|
|
610
610
|
)
|
|
611
611
|
return parse(await self._transport.send(spec.allowing(allow_workflow_effect)))
|
|
612
612
|
|
|
613
|
+
async def suspend_ticket(
|
|
614
|
+
self,
|
|
615
|
+
rfc_number: str,
|
|
616
|
+
*,
|
|
617
|
+
allow_workflow_effect: WorkflowEffect | Iterable[WorkflowEffect],
|
|
618
|
+
comment: str | None = None,
|
|
619
|
+
done_by_id: int | None = None,
|
|
620
|
+
) -> Request:
|
|
621
|
+
"""Suspend a ticket -- the vendor's suspend request.
|
|
622
|
+
|
|
623
|
+
Sends ``PUT requests/{rfc}`` with a ``suspended`` body, as the vendor
|
|
624
|
+
documents it:
|
|
625
|
+
https://docs.easyvista.com/docs/rest-api-suspend-an-incident-request.md
|
|
626
|
+
(tier 1, re-read 2026-11-10). The page documents two optional
|
|
627
|
+
parameters, ``comment`` and ``done_by_id`` (an employee id), and no
|
|
628
|
+
target status. Both are left out of the body when ``None``. A ticket
|
|
629
|
+
that is already suspended is refused by the server (400, "The request
|
|
630
|
+
is already suspended. Operation aborted.").
|
|
631
|
+
|
|
632
|
+
Measured 2026-11-04 to 2026-11-10 on one instance (tier 4, may not
|
|
633
|
+
generalise): the ticket moved to the instance's suspended status and
|
|
634
|
+
one already-ended suspension action carrying the comment was added;
|
|
635
|
+
the workflow step stayed open. A status key sent in the body was
|
|
636
|
+
ignored (measured 2026-11-04). The request takes no status, and the
|
|
637
|
+
package has no status setter.
|
|
638
|
+
|
|
639
|
+
``allow_workflow_effect`` is **required**: nothing documents what a
|
|
640
|
+
suspend does to the workflow, so the write is named
|
|
641
|
+
``WorkflowEffect.UNKNOWN`` and refused with
|
|
642
|
+
:class:`~easyvista_python_client.EasyvistaWorkflowEffectRefused`
|
|
643
|
+
before any request is made unless the call allows it. The allowed
|
|
644
|
+
request is sent once, never retried. The return value is parsed like
|
|
645
|
+
:meth:`close_ticket`'s: the vendor answers 201 with an ``HREF`` only, so
|
|
646
|
+
re-read with :meth:`get_ticket` to see the status::
|
|
647
|
+
|
|
648
|
+
client.suspend_ticket(
|
|
649
|
+
rfc,
|
|
650
|
+
allow_workflow_effect=WorkflowEffect.UNKNOWN,
|
|
651
|
+
comment="Waiting for the requester",
|
|
652
|
+
)
|
|
653
|
+
"""
|
|
654
|
+
spec, parse = requests_res.build_suspend_ticket(
|
|
655
|
+
rfc_number,
|
|
656
|
+
comment=comment,
|
|
657
|
+
done_by_id=done_by_id,
|
|
658
|
+
context=self._validation_context,
|
|
659
|
+
)
|
|
660
|
+
return parse(await self._transport.send(spec.allowing(allow_workflow_effect)))
|
|
661
|
+
|
|
662
|
+
async def restart_ticket(
|
|
663
|
+
self,
|
|
664
|
+
rfc_number: str,
|
|
665
|
+
*,
|
|
666
|
+
allow_workflow_effect: WorkflowEffect | Iterable[WorkflowEffect],
|
|
667
|
+
comment: str | None = None,
|
|
668
|
+
done_by_id: int | None = None,
|
|
669
|
+
) -> Request:
|
|
670
|
+
"""Restart a suspended ticket -- the vendor's restart request.
|
|
671
|
+
|
|
672
|
+
Sends ``PUT requests/{rfc}`` with a ``restarted`` body, as the vendor
|
|
673
|
+
documents it:
|
|
674
|
+
https://docs.easyvista.com/docs/rest-api-reopen-an-incident-request.md
|
|
675
|
+
(tier 1, re-read 2026-11-10). The page documents two optional
|
|
676
|
+
parameters, ``comment`` and ``done_by_id`` (an employee id), and no
|
|
677
|
+
target status; its example marks ``comment`` as required while its
|
|
678
|
+
table lists it as optional, so this method leaves it out of the body
|
|
679
|
+
when ``None``. On a ticket that is not suspended the server answers
|
|
680
|
+
400 with "The request is not suspended. Operation aborted.".
|
|
681
|
+
|
|
682
|
+
Measured 2026-11-04 to 2026-11-10 on one instance (tier 4, may not
|
|
683
|
+
generalise): the request added one end-of-suspension action and, in
|
|
684
|
+
the tested case, returned the ticket to the status it was suspended
|
|
685
|
+
from; a restart on a ticket that was not suspended was refused with
|
|
686
|
+
the documented message, as HTTP 590 rather than the documented 400.
|
|
687
|
+
A status key sent in the body was ignored
|
|
688
|
+
(measured 2026-11-04). The request takes no status, and the package
|
|
689
|
+
has no status setter.
|
|
690
|
+
|
|
691
|
+
``allow_workflow_effect`` is **required** and is
|
|
692
|
+
``WorkflowEffect.UNKNOWN``, as for :meth:`suspend_ticket`: refused
|
|
693
|
+
before any request unless allowed, and sent once, never retried. The
|
|
694
|
+
return value is parsed like :meth:`close_ticket`'s (an ``HREF`` only)::
|
|
695
|
+
|
|
696
|
+
client.restart_ticket(rfc, allow_workflow_effect=WorkflowEffect.UNKNOWN)
|
|
697
|
+
"""
|
|
698
|
+
spec, parse = requests_res.build_restart_ticket(
|
|
699
|
+
rfc_number,
|
|
700
|
+
comment=comment,
|
|
701
|
+
done_by_id=done_by_id,
|
|
702
|
+
context=self._validation_context,
|
|
703
|
+
)
|
|
704
|
+
return parse(await self._transport.send(spec.allowing(allow_workflow_effect)))
|
|
705
|
+
|
|
613
706
|
# --- actions -------------------------------------------------------------
|
|
614
707
|
async def create_action(
|
|
615
708
|
self,
|
|
@@ -947,14 +1040,20 @@ class AsyncEasyvistaClient:
|
|
|
947
1040
|
the **first ticket only** the step was recorded as a type-20 action
|
|
948
1041
|
with ``WORKFLOW_ID`` set; its ``WORKFLOW_ID`` was unchanged,
|
|
949
1042
|
``DONE_BY_ID`` stayed empty, and no ticket field changed.
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
with it.
|
|
957
|
-
|
|
1043
|
+
The ticket's own ``OWNING_GROUP_ID`` was read at the time on one
|
|
1044
|
+
ticket only and had not moved; that reading was taken too soon.
|
|
1045
|
+
Measured 2026-11-04 to 2026-11-10 on one instance (several tickets,
|
|
1046
|
+
may not generalise): the ticket's ``OWNING_GROUP_ID`` follows the
|
|
1047
|
+
step's new group after a delay of a few seconds to about two minutes,
|
|
1048
|
+
consistent with a server-side job (inferred, not observed), and
|
|
1049
|
+
``LAST_UPDATE`` moves with it. What still holds: the status does not
|
|
1050
|
+
move, the step is not started and no action row is added. Also
|
|
1051
|
+
measured 2026-11-04 on one ticket: after ``done_by_id`` was written on
|
|
1052
|
+
the step, the ticket's ``OWNER_ID`` followed about 1 min 45 s later.
|
|
1053
|
+
The lower-case key ``group_id`` was the one sent; the upper-case
|
|
1054
|
+
spelling was never needed. The person's effect on the step was not
|
|
1055
|
+
measured beyond that one ``OWNER_ID`` observation. Whether the UI
|
|
1056
|
+
wizard's notifications fire is not observable from the API.
|
|
958
1057
|
|
|
959
1058
|
Not refused by the workflow guard: the group and the person are data
|
|
960
1059
|
the workflow reads, not workflow state. Re-read with :meth:`get_action`
|
|
@@ -610,6 +610,99 @@ class EasyvistaClient:
|
|
|
610
610
|
)
|
|
611
611
|
return parse(self._transport.send(spec.allowing(allow_workflow_effect)))
|
|
612
612
|
|
|
613
|
+
def suspend_ticket(
|
|
614
|
+
self,
|
|
615
|
+
rfc_number: str,
|
|
616
|
+
*,
|
|
617
|
+
allow_workflow_effect: WorkflowEffect | Iterable[WorkflowEffect],
|
|
618
|
+
comment: str | None = None,
|
|
619
|
+
done_by_id: int | None = None,
|
|
620
|
+
) -> Request:
|
|
621
|
+
"""Suspend a ticket -- the vendor's suspend request.
|
|
622
|
+
|
|
623
|
+
Sends ``PUT requests/{rfc}`` with a ``suspended`` body, as the vendor
|
|
624
|
+
documents it:
|
|
625
|
+
https://docs.easyvista.com/docs/rest-api-suspend-an-incident-request.md
|
|
626
|
+
(tier 1, re-read 2026-11-10). The page documents two optional
|
|
627
|
+
parameters, ``comment`` and ``done_by_id`` (an employee id), and no
|
|
628
|
+
target status. Both are left out of the body when ``None``. A ticket
|
|
629
|
+
that is already suspended is refused by the server (400, "The request
|
|
630
|
+
is already suspended. Operation aborted.").
|
|
631
|
+
|
|
632
|
+
Measured 2026-11-04 to 2026-11-10 on one instance (tier 4, may not
|
|
633
|
+
generalise): the ticket moved to the instance's suspended status and
|
|
634
|
+
one already-ended suspension action carrying the comment was added;
|
|
635
|
+
the workflow step stayed open. A status key sent in the body was
|
|
636
|
+
ignored (measured 2026-11-04). The request takes no status, and the
|
|
637
|
+
package has no status setter.
|
|
638
|
+
|
|
639
|
+
``allow_workflow_effect`` is **required**: nothing documents what a
|
|
640
|
+
suspend does to the workflow, so the write is named
|
|
641
|
+
``WorkflowEffect.UNKNOWN`` and refused with
|
|
642
|
+
:class:`~easyvista_python_client.EasyvistaWorkflowEffectRefused`
|
|
643
|
+
before any request is made unless the call allows it. The allowed
|
|
644
|
+
request is sent once, never retried. The return value is parsed like
|
|
645
|
+
:meth:`close_ticket`'s: the vendor answers 201 with an ``HREF`` only, so
|
|
646
|
+
re-read with :meth:`get_ticket` to see the status::
|
|
647
|
+
|
|
648
|
+
client.suspend_ticket(
|
|
649
|
+
rfc,
|
|
650
|
+
allow_workflow_effect=WorkflowEffect.UNKNOWN,
|
|
651
|
+
comment="Waiting for the requester",
|
|
652
|
+
)
|
|
653
|
+
"""
|
|
654
|
+
spec, parse = requests_res.build_suspend_ticket(
|
|
655
|
+
rfc_number,
|
|
656
|
+
comment=comment,
|
|
657
|
+
done_by_id=done_by_id,
|
|
658
|
+
context=self._validation_context,
|
|
659
|
+
)
|
|
660
|
+
return parse(self._transport.send(spec.allowing(allow_workflow_effect)))
|
|
661
|
+
|
|
662
|
+
def restart_ticket(
|
|
663
|
+
self,
|
|
664
|
+
rfc_number: str,
|
|
665
|
+
*,
|
|
666
|
+
allow_workflow_effect: WorkflowEffect | Iterable[WorkflowEffect],
|
|
667
|
+
comment: str | None = None,
|
|
668
|
+
done_by_id: int | None = None,
|
|
669
|
+
) -> Request:
|
|
670
|
+
"""Restart a suspended ticket -- the vendor's restart request.
|
|
671
|
+
|
|
672
|
+
Sends ``PUT requests/{rfc}`` with a ``restarted`` body, as the vendor
|
|
673
|
+
documents it:
|
|
674
|
+
https://docs.easyvista.com/docs/rest-api-reopen-an-incident-request.md
|
|
675
|
+
(tier 1, re-read 2026-11-10). The page documents two optional
|
|
676
|
+
parameters, ``comment`` and ``done_by_id`` (an employee id), and no
|
|
677
|
+
target status; its example marks ``comment`` as required while its
|
|
678
|
+
table lists it as optional, so this method leaves it out of the body
|
|
679
|
+
when ``None``. On a ticket that is not suspended the server answers
|
|
680
|
+
400 with "The request is not suspended. Operation aborted.".
|
|
681
|
+
|
|
682
|
+
Measured 2026-11-04 to 2026-11-10 on one instance (tier 4, may not
|
|
683
|
+
generalise): the request added one end-of-suspension action and, in
|
|
684
|
+
the tested case, returned the ticket to the status it was suspended
|
|
685
|
+
from; a restart on a ticket that was not suspended was refused with
|
|
686
|
+
the documented message, as HTTP 590 rather than the documented 400.
|
|
687
|
+
A status key sent in the body was ignored
|
|
688
|
+
(measured 2026-11-04). The request takes no status, and the package
|
|
689
|
+
has no status setter.
|
|
690
|
+
|
|
691
|
+
``allow_workflow_effect`` is **required** and is
|
|
692
|
+
``WorkflowEffect.UNKNOWN``, as for :meth:`suspend_ticket`: refused
|
|
693
|
+
before any request unless allowed, and sent once, never retried. The
|
|
694
|
+
return value is parsed like :meth:`close_ticket`'s (an ``HREF`` only)::
|
|
695
|
+
|
|
696
|
+
client.restart_ticket(rfc, allow_workflow_effect=WorkflowEffect.UNKNOWN)
|
|
697
|
+
"""
|
|
698
|
+
spec, parse = requests_res.build_restart_ticket(
|
|
699
|
+
rfc_number,
|
|
700
|
+
comment=comment,
|
|
701
|
+
done_by_id=done_by_id,
|
|
702
|
+
context=self._validation_context,
|
|
703
|
+
)
|
|
704
|
+
return parse(self._transport.send(spec.allowing(allow_workflow_effect)))
|
|
705
|
+
|
|
613
706
|
# --- actions -------------------------------------------------------------
|
|
614
707
|
def create_action(
|
|
615
708
|
self,
|
|
@@ -947,14 +1040,20 @@ class EasyvistaClient:
|
|
|
947
1040
|
the **first ticket only** the step was recorded as a type-20 action
|
|
948
1041
|
with ``WORKFLOW_ID`` set; its ``WORKFLOW_ID`` was unchanged,
|
|
949
1042
|
``DONE_BY_ID`` stayed empty, and no ticket field changed.
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
with it.
|
|
957
|
-
|
|
1043
|
+
The ticket's own ``OWNING_GROUP_ID`` was read at the time on one
|
|
1044
|
+
ticket only and had not moved; that reading was taken too soon.
|
|
1045
|
+
Measured 2026-11-04 to 2026-11-10 on one instance (several tickets,
|
|
1046
|
+
may not generalise): the ticket's ``OWNING_GROUP_ID`` follows the
|
|
1047
|
+
step's new group after a delay of a few seconds to about two minutes,
|
|
1048
|
+
consistent with a server-side job (inferred, not observed), and
|
|
1049
|
+
``LAST_UPDATE`` moves with it. What still holds: the status does not
|
|
1050
|
+
move, the step is not started and no action row is added. Also
|
|
1051
|
+
measured 2026-11-04 on one ticket: after ``done_by_id`` was written on
|
|
1052
|
+
the step, the ticket's ``OWNER_ID`` followed about 1 min 45 s later.
|
|
1053
|
+
The lower-case key ``group_id`` was the one sent; the upper-case
|
|
1054
|
+
spelling was never needed. The person's effect on the step was not
|
|
1055
|
+
measured beyond that one ``OWNER_ID`` observation. Whether the UI
|
|
1056
|
+
wizard's notifications fire is not observable from the API.
|
|
958
1057
|
|
|
959
1058
|
Not refused by the workflow guard: the group and the person are data
|
|
960
1059
|
the workflow reads, not workflow state. Re-read with :meth:`get_action`
|
|
@@ -13,6 +13,10 @@ stack left to report it otherwise (see ``docs/content.rst``).
|
|
|
13
13
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
-
from easyvista_python_client.content.conversion import
|
|
16
|
+
from easyvista_python_client.content.conversion import (
|
|
17
|
+
EasyvistaContentConverter,
|
|
18
|
+
Link,
|
|
19
|
+
RewriteLink,
|
|
20
|
+
)
|
|
17
21
|
|
|
18
|
-
__all__ = ["EasyvistaContentConverter"]
|
|
22
|
+
__all__ = ["EasyvistaContentConverter", "Link", "RewriteLink"]
|