glpi-python-client 0.6.4__py3-none-any.whl → 0.6.6__py3-none-any.whl

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.
@@ -40,6 +40,7 @@ from glpi_python_client.models import (
40
40
  DeleteDocument,
41
41
  DeleteEntity,
42
42
  DeleteFollowup,
43
+ DeleteGroup,
43
44
  DeleteKBArticle,
44
45
  DeleteKBArticleComment,
45
46
  DeleteKBCategory,
@@ -60,6 +61,7 @@ from glpi_python_client.models import (
60
61
  GetDocumentItem,
61
62
  GetEntity,
62
63
  GetFollowup,
64
+ GetGroup,
63
65
  GetKBArticle,
64
66
  GetKBArticleComment,
65
67
  GetKBArticleRevision,
@@ -96,6 +98,7 @@ from glpi_python_client.models import (
96
98
  PatchDocument,
97
99
  PatchEntity,
98
100
  PatchFollowup,
101
+ PatchGroup,
99
102
  PatchKBArticle,
100
103
  PatchKBArticleComment,
101
104
  PatchKBCategory,
@@ -114,6 +117,7 @@ from glpi_python_client.models import (
114
117
  PostDocument,
115
118
  PostEntity,
116
119
  PostFollowup,
120
+ PostGroup,
117
121
  PostKBArticle,
118
122
  PostKBArticleComment,
119
123
  PostKBCategory,
@@ -133,7 +137,7 @@ from glpi_python_client.rsql import (
133
137
  date_window,
134
138
  )
135
139
 
136
- __version__ = "0.6.4"
140
+ __version__ = "0.6.6"
137
141
 
138
142
  __all__ = [
139
143
  "AsyncGlpiClient",
@@ -145,6 +149,7 @@ __all__ = [
145
149
  "DeleteDocument",
146
150
  "DeleteEntity",
147
151
  "DeleteFollowup",
152
+ "DeleteGroup",
148
153
  "DeleteKBArticle",
149
154
  "DeleteKBArticleComment",
150
155
  "DeleteKBCategory",
@@ -165,6 +170,7 @@ __all__ = [
165
170
  "GetDocumentItem",
166
171
  "GetEntity",
167
172
  "GetFollowup",
173
+ "GetGroup",
168
174
  "GetKBArticle",
169
175
  "GetKBArticleComment",
170
176
  "GetKBArticleRevision",
@@ -212,6 +218,7 @@ __all__ = [
212
218
  "PatchDocument",
213
219
  "PatchEntity",
214
220
  "PatchFollowup",
221
+ "PatchGroup",
215
222
  "PatchKBArticle",
216
223
  "PatchKBArticleComment",
217
224
  "PatchKBCategory",
@@ -230,6 +237,7 @@ __all__ = [
230
237
  "PostDocument",
231
238
  "PostEntity",
232
239
  "PostFollowup",
240
+ "PostGroup",
233
241
  "PostKBArticle",
234
242
  "PostKBArticleComment",
235
243
  "PostKBCategory",
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  from glpi_python_client._async.clients.api.administration import (
13
13
  EntityMixin,
14
+ GroupMixin,
14
15
  UserMixin,
15
16
  )
16
17
  from glpi_python_client._async.clients.api.assets import ComputerMixin
@@ -49,6 +50,7 @@ __all__ = [
49
50
  "DocumentMixin",
50
51
  "EntityMixin",
51
52
  "FollowupMixin",
53
+ "GroupMixin",
52
54
  "KBArticleCommentMixin",
53
55
  "KBArticleMixin",
54
56
  "KBArticleRevisionMixin",
@@ -1,6 +1,6 @@
1
1
  """GLPI ``/Administration`` mixins for the GLPI client.
2
2
 
3
- The submodules expose the user and entity mixins used by this tree's
3
+ The submodules expose the user, entity and group mixins used by this tree's
4
4
  client -- :class:`glpi_python_client.AsyncGlpiClient` on the async
5
5
  surface, :class:`glpi_python_client.GlpiClient` on the generated one.
6
6
  """
@@ -8,6 +8,7 @@ surface, :class:`glpi_python_client.GlpiClient` on the generated one.
8
8
  from __future__ import annotations
9
9
 
10
10
  from glpi_python_client._async.clients.api.administration._entity import EntityMixin
11
+ from glpi_python_client._async.clients.api.administration._group import GroupMixin
11
12
  from glpi_python_client._async.clients.api.administration._user import UserMixin
12
13
 
13
- __all__ = ["EntityMixin", "UserMixin"]
14
+ __all__ = ["EntityMixin", "GroupMixin", "UserMixin"]
@@ -0,0 +1,218 @@
1
+ """GLPI ``/Administration/Group`` mixin.
2
+
3
+ The mixin exposes search, fetch, create, update, and delete helpers for the
4
+ GLPI group resource. Every call sends the client's ``GLPI-Entity`` header
5
+ (and the profile and recursion headers that travel with it) except where a
6
+ search is told to skip it with ``skip_entity=True``.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from collections.abc import AsyncIterator
12
+
13
+ from glpi_python_client._async.clients.commons._constants import GROUP_ENDPOINT, GlpiId
14
+ from glpi_python_client._async.clients.commons._transport import TransportMixin
15
+ from glpi_python_client.models.api_schema.administration._group import (
16
+ DeleteGroup,
17
+ GetGroup,
18
+ PatchGroup,
19
+ PostGroup,
20
+ )
21
+
22
+
23
+ class GroupMixin(TransportMixin):
24
+ """CRUD helpers for ``/Administration/Group``."""
25
+
26
+ async def search_groups(
27
+ self,
28
+ rsql_filter: str = "",
29
+ *,
30
+ limit: int = 50,
31
+ start: int = 0,
32
+ skip_entity: bool = False,
33
+ ) -> list[GetGroup]:
34
+ """Search GLPI groups with an optional RSQL filter.
35
+
36
+ Parameters
37
+ ----------
38
+ rsql_filter : str, optional
39
+ Raw RSQL filter forwarded as the ``filter`` query parameter,
40
+ for example ``"name==Support"``. Empty by default.
41
+ limit : int, optional
42
+ Maximum number of records returned by the GLPI server.
43
+ start : int, optional
44
+ Zero-based offset of the first record returned.
45
+ skip_entity : bool, optional
46
+ When ``True`` the ``GLPI-Entity`` header is omitted so the
47
+ search spans every entity the caller has access to.
48
+
49
+ Returns
50
+ -------
51
+ list[GetGroup]
52
+ Groups matching the filter.
53
+ """
54
+
55
+ params: dict[str, object] = {"limit": limit, "start": start}
56
+ if rsql_filter:
57
+ params["filter"] = rsql_filter
58
+ return await self._resource_list(
59
+ GROUP_ENDPOINT, GetGroup, params=params, skip_entity=skip_entity
60
+ )
61
+
62
+ async def iter_search_groups(
63
+ self,
64
+ rsql_filter: str = "",
65
+ *,
66
+ batch_size: int = 50,
67
+ skip_entity: bool = False,
68
+ ) -> AsyncIterator[list[GetGroup]]:
69
+ """Yield successive pages of GLPI groups until exhausted.
70
+
71
+ Pagination advances the ``start`` offset after each batch and stops
72
+ when the server returns fewer items than ``batch_size``.
73
+
74
+ Parameters
75
+ ----------
76
+ rsql_filter : str, optional
77
+ Raw RSQL filter forwarded as the ``filter`` query parameter.
78
+ batch_size : int, optional
79
+ Number of records requested per page (default 50).
80
+ skip_entity : bool, optional
81
+ When ``True`` the ``GLPI-Entity`` header is omitted so the
82
+ search spans every entity the caller has access to.
83
+
84
+ Yields
85
+ ------
86
+ list[GetGroup]
87
+ One page of groups per iteration. The last yielded batch may
88
+ be shorter than ``batch_size``.
89
+ """
90
+
91
+ start = 0
92
+ while True:
93
+ batch = await self.search_groups(
94
+ rsql_filter,
95
+ limit=batch_size,
96
+ start=start,
97
+ skip_entity=skip_entity,
98
+ )
99
+ if batch:
100
+ yield batch
101
+ if len(batch) < batch_size:
102
+ break
103
+ start += batch_size
104
+
105
+ async def get_group(self, group_id: GlpiId) -> GetGroup:
106
+ """Fetch one GLPI group by identifier.
107
+
108
+ Parameters
109
+ ----------
110
+ group_id : GlpiId
111
+ Numeric identifier of the group to retrieve.
112
+
113
+ Returns
114
+ -------
115
+ GetGroup
116
+ Validated group payload.
117
+
118
+ Raises
119
+ ------
120
+ GlpiStatusError
121
+ If the GLPI server returns a non-success HTTP status.
122
+ """
123
+
124
+ return await self._resource_get(
125
+ f"{GROUP_ENDPOINT}/{group_id}",
126
+ GetGroup,
127
+ failure_message=f"Failed to get group {group_id}",
128
+ )
129
+
130
+ async def create_group(self, group: PostGroup) -> int:
131
+ """Create one GLPI group.
132
+
133
+ Parameters
134
+ ----------
135
+ group : PostGroup
136
+ Request body describing the group to create.
137
+
138
+ Returns
139
+ -------
140
+ int
141
+ Identifier assigned by the GLPI server.
142
+
143
+ Raises
144
+ ------
145
+ GlpiStatusError
146
+ If the GLPI server returns a non-success HTTP status.
147
+ GlpiProtocolError
148
+ If the create response is missing the ``id`` field.
149
+ """
150
+
151
+ return await self._resource_create(
152
+ GROUP_ENDPOINT,
153
+ group,
154
+ failure_message="Failed to create group",
155
+ missing_message="GLPI group create response did not include an ID",
156
+ log_message_factory=lambda new_id: f"GLPI API created group {new_id}",
157
+ )
158
+
159
+ async def update_group(self, group_id: GlpiId, group: PatchGroup) -> None:
160
+ """Update one GLPI group with a partial body.
161
+
162
+ Parameters
163
+ ----------
164
+ group_id : GlpiId
165
+ Numeric identifier of the group to update.
166
+ group : PatchGroup
167
+ Partial request body.
168
+
169
+ Returns
170
+ -------
171
+ None
172
+
173
+ Raises
174
+ ------
175
+ GlpiStatusError
176
+ If the GLPI server returns a non-success HTTP status.
177
+ """
178
+
179
+ await self._resource_update(
180
+ f"{GROUP_ENDPOINT}/{group_id}",
181
+ group,
182
+ failure_message=f"Failed to update group {group_id}",
183
+ log_message=f"GLPI API updated group {group_id}",
184
+ )
185
+
186
+ async def delete_group(
187
+ self, group_id: GlpiId, *, force: bool | None = None
188
+ ) -> None:
189
+ """Delete one GLPI group by identifier.
190
+
191
+ Parameters
192
+ ----------
193
+ group_id : GlpiId
194
+ Numeric identifier of the group to delete.
195
+ force : bool | None, optional
196
+ When ``True`` the group is permanently deleted instead of
197
+ being moved to the trash.
198
+
199
+ Returns
200
+ -------
201
+ None
202
+
203
+ Raises
204
+ ------
205
+ GlpiStatusError
206
+ If the GLPI server returns a non-success HTTP status.
207
+ """
208
+
209
+ await self._resource_delete(
210
+ f"{GROUP_ENDPOINT}/{group_id}",
211
+ failure_message=f"Failed to delete group {group_id}",
212
+ log_message=f"GLPI API deleted group {group_id}",
213
+ force=force,
214
+ delete_model_cls=DeleteGroup,
215
+ )
216
+
217
+
218
+ __all__ = ["GroupMixin"]
@@ -25,6 +25,7 @@ from glpi_python_client._async.clients.api import (
25
25
  DocumentMixin,
26
26
  EntityMixin,
27
27
  FollowupMixin,
28
+ GroupMixin,
28
29
  KBArticleCommentMixin,
29
30
  KBArticleMixin,
30
31
  KBArticleRevisionMixin,
@@ -59,6 +60,7 @@ class AsyncGlpiClient(
59
60
  ComputerMixin,
60
61
  UserMixin,
61
62
  EntityMixin,
63
+ GroupMixin,
62
64
  LocationMixin,
63
65
  ContractTypeMixin,
64
66
  KBCategoryMixin,
@@ -19,6 +19,7 @@ COMPUTER_ENDPOINT = "Assets/Computer"
19
19
  # administration/
20
20
  USER_ENDPOINT = "Administration/User"
21
21
  ENTITY_ENDPOINT = "Administration/Entity"
22
+ GROUP_ENDPOINT = "Administration/Group"
22
23
 
23
24
  # dropdowns/
24
25
  CONTRACT_TYPE_ENDPOINT = "Dropdowns/ContractType"
@@ -52,6 +53,7 @@ __all__ = [
52
53
  "DOCUMENT_ENDPOINT",
53
54
  "ENTITY_ENDPOINT",
54
55
  "FOLLOWUP_SUFFIX",
56
+ "GROUP_ENDPOINT",
55
57
  "KB_ARTICLE_ENDPOINT",
56
58
  "KB_CATEGORY_ENDPOINT",
57
59
  "KB_COMMENT_SUFFIX",
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  from glpi_python_client._sync.clients.api.administration import (
13
13
  EntityMixin,
14
+ GroupMixin,
14
15
  UserMixin,
15
16
  )
16
17
  from glpi_python_client._sync.clients.api.assets import ComputerMixin
@@ -49,6 +50,7 @@ __all__ = [
49
50
  "DocumentMixin",
50
51
  "EntityMixin",
51
52
  "FollowupMixin",
53
+ "GroupMixin",
52
54
  "KBArticleCommentMixin",
53
55
  "KBArticleMixin",
54
56
  "KBArticleRevisionMixin",
@@ -1,6 +1,6 @@
1
1
  """GLPI ``/Administration`` mixins for the GLPI client.
2
2
 
3
- The submodules expose the user and entity mixins used by this tree's
3
+ The submodules expose the user, entity and group mixins used by this tree's
4
4
  client -- :class:`glpi_python_client.AsyncGlpiClient` on the async
5
5
  surface, :class:`glpi_python_client.GlpiClient` on the generated one.
6
6
  """
@@ -8,6 +8,7 @@ surface, :class:`glpi_python_client.GlpiClient` on the generated one.
8
8
  from __future__ import annotations
9
9
 
10
10
  from glpi_python_client._sync.clients.api.administration._entity import EntityMixin
11
+ from glpi_python_client._sync.clients.api.administration._group import GroupMixin
11
12
  from glpi_python_client._sync.clients.api.administration._user import UserMixin
12
13
 
13
- __all__ = ["EntityMixin", "UserMixin"]
14
+ __all__ = ["EntityMixin", "GroupMixin", "UserMixin"]
@@ -0,0 +1,218 @@
1
+ """GLPI ``/Administration/Group`` mixin.
2
+
3
+ The mixin exposes search, fetch, create, update, and delete helpers for the
4
+ GLPI group resource. Every call sends the client's ``GLPI-Entity`` header
5
+ (and the profile and recursion headers that travel with it) except where a
6
+ search is told to skip it with ``skip_entity=True``.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from collections.abc import Iterator
12
+
13
+ from glpi_python_client._sync.clients.commons._constants import GROUP_ENDPOINT, GlpiId
14
+ from glpi_python_client._sync.clients.commons._transport import TransportMixin
15
+ from glpi_python_client.models.api_schema.administration._group import (
16
+ DeleteGroup,
17
+ GetGroup,
18
+ PatchGroup,
19
+ PostGroup,
20
+ )
21
+
22
+
23
+ class GroupMixin(TransportMixin):
24
+ """CRUD helpers for ``/Administration/Group``."""
25
+
26
+ def search_groups(
27
+ self,
28
+ rsql_filter: str = "",
29
+ *,
30
+ limit: int = 50,
31
+ start: int = 0,
32
+ skip_entity: bool = False,
33
+ ) -> list[GetGroup]:
34
+ """Search GLPI groups with an optional RSQL filter.
35
+
36
+ Parameters
37
+ ----------
38
+ rsql_filter : str, optional
39
+ Raw RSQL filter forwarded as the ``filter`` query parameter,
40
+ for example ``"name==Support"``. Empty by default.
41
+ limit : int, optional
42
+ Maximum number of records returned by the GLPI server.
43
+ start : int, optional
44
+ Zero-based offset of the first record returned.
45
+ skip_entity : bool, optional
46
+ When ``True`` the ``GLPI-Entity`` header is omitted so the
47
+ search spans every entity the caller has access to.
48
+
49
+ Returns
50
+ -------
51
+ list[GetGroup]
52
+ Groups matching the filter.
53
+ """
54
+
55
+ params: dict[str, object] = {"limit": limit, "start": start}
56
+ if rsql_filter:
57
+ params["filter"] = rsql_filter
58
+ return self._resource_list(
59
+ GROUP_ENDPOINT, GetGroup, params=params, skip_entity=skip_entity
60
+ )
61
+
62
+ def iter_search_groups(
63
+ self,
64
+ rsql_filter: str = "",
65
+ *,
66
+ batch_size: int = 50,
67
+ skip_entity: bool = False,
68
+ ) -> Iterator[list[GetGroup]]:
69
+ """Yield successive pages of GLPI groups until exhausted.
70
+
71
+ Pagination advances the ``start`` offset after each batch and stops
72
+ when the server returns fewer items than ``batch_size``.
73
+
74
+ Parameters
75
+ ----------
76
+ rsql_filter : str, optional
77
+ Raw RSQL filter forwarded as the ``filter`` query parameter.
78
+ batch_size : int, optional
79
+ Number of records requested per page (default 50).
80
+ skip_entity : bool, optional
81
+ When ``True`` the ``GLPI-Entity`` header is omitted so the
82
+ search spans every entity the caller has access to.
83
+
84
+ Yields
85
+ ------
86
+ list[GetGroup]
87
+ One page of groups per iteration. The last yielded batch may
88
+ be shorter than ``batch_size``.
89
+ """
90
+
91
+ start = 0
92
+ while True:
93
+ batch = self.search_groups(
94
+ rsql_filter,
95
+ limit=batch_size,
96
+ start=start,
97
+ skip_entity=skip_entity,
98
+ )
99
+ if batch:
100
+ yield batch
101
+ if len(batch) < batch_size:
102
+ break
103
+ start += batch_size
104
+
105
+ def get_group(self, group_id: GlpiId) -> GetGroup:
106
+ """Fetch one GLPI group by identifier.
107
+
108
+ Parameters
109
+ ----------
110
+ group_id : GlpiId
111
+ Numeric identifier of the group to retrieve.
112
+
113
+ Returns
114
+ -------
115
+ GetGroup
116
+ Validated group payload.
117
+
118
+ Raises
119
+ ------
120
+ GlpiStatusError
121
+ If the GLPI server returns a non-success HTTP status.
122
+ """
123
+
124
+ return self._resource_get(
125
+ f"{GROUP_ENDPOINT}/{group_id}",
126
+ GetGroup,
127
+ failure_message=f"Failed to get group {group_id}",
128
+ )
129
+
130
+ def create_group(self, group: PostGroup) -> int:
131
+ """Create one GLPI group.
132
+
133
+ Parameters
134
+ ----------
135
+ group : PostGroup
136
+ Request body describing the group to create.
137
+
138
+ Returns
139
+ -------
140
+ int
141
+ Identifier assigned by the GLPI server.
142
+
143
+ Raises
144
+ ------
145
+ GlpiStatusError
146
+ If the GLPI server returns a non-success HTTP status.
147
+ GlpiProtocolError
148
+ If the create response is missing the ``id`` field.
149
+ """
150
+
151
+ return self._resource_create(
152
+ GROUP_ENDPOINT,
153
+ group,
154
+ failure_message="Failed to create group",
155
+ missing_message="GLPI group create response did not include an ID",
156
+ log_message_factory=lambda new_id: f"GLPI API created group {new_id}",
157
+ )
158
+
159
+ def update_group(self, group_id: GlpiId, group: PatchGroup) -> None:
160
+ """Update one GLPI group with a partial body.
161
+
162
+ Parameters
163
+ ----------
164
+ group_id : GlpiId
165
+ Numeric identifier of the group to update.
166
+ group : PatchGroup
167
+ Partial request body.
168
+
169
+ Returns
170
+ -------
171
+ None
172
+
173
+ Raises
174
+ ------
175
+ GlpiStatusError
176
+ If the GLPI server returns a non-success HTTP status.
177
+ """
178
+
179
+ self._resource_update(
180
+ f"{GROUP_ENDPOINT}/{group_id}",
181
+ group,
182
+ failure_message=f"Failed to update group {group_id}",
183
+ log_message=f"GLPI API updated group {group_id}",
184
+ )
185
+
186
+ def delete_group(
187
+ self, group_id: GlpiId, *, force: bool | None = None
188
+ ) -> None:
189
+ """Delete one GLPI group by identifier.
190
+
191
+ Parameters
192
+ ----------
193
+ group_id : GlpiId
194
+ Numeric identifier of the group to delete.
195
+ force : bool | None, optional
196
+ When ``True`` the group is permanently deleted instead of
197
+ being moved to the trash.
198
+
199
+ Returns
200
+ -------
201
+ None
202
+
203
+ Raises
204
+ ------
205
+ GlpiStatusError
206
+ If the GLPI server returns a non-success HTTP status.
207
+ """
208
+
209
+ self._resource_delete(
210
+ f"{GROUP_ENDPOINT}/{group_id}",
211
+ failure_message=f"Failed to delete group {group_id}",
212
+ log_message=f"GLPI API deleted group {group_id}",
213
+ force=force,
214
+ delete_model_cls=DeleteGroup,
215
+ )
216
+
217
+
218
+ __all__ = ["GroupMixin"]
@@ -25,6 +25,7 @@ from glpi_python_client._sync.clients.api import (
25
25
  DocumentMixin,
26
26
  EntityMixin,
27
27
  FollowupMixin,
28
+ GroupMixin,
28
29
  KBArticleCommentMixin,
29
30
  KBArticleMixin,
30
31
  KBArticleRevisionMixin,
@@ -59,6 +60,7 @@ class GlpiClient(
59
60
  ComputerMixin,
60
61
  UserMixin,
61
62
  EntityMixin,
63
+ GroupMixin,
62
64
  LocationMixin,
63
65
  ContractTypeMixin,
64
66
  KBCategoryMixin,
@@ -19,6 +19,7 @@ COMPUTER_ENDPOINT = "Assets/Computer"
19
19
  # administration/
20
20
  USER_ENDPOINT = "Administration/User"
21
21
  ENTITY_ENDPOINT = "Administration/Entity"
22
+ GROUP_ENDPOINT = "Administration/Group"
22
23
 
23
24
  # dropdowns/
24
25
  CONTRACT_TYPE_ENDPOINT = "Dropdowns/ContractType"
@@ -52,6 +53,7 @@ __all__ = [
52
53
  "DOCUMENT_ENDPOINT",
53
54
  "ENTITY_ENDPOINT",
54
55
  "FOLLOWUP_SUFFIX",
56
+ "GROUP_ENDPOINT",
55
57
  "KB_ARTICLE_ENDPOINT",
56
58
  "KB_CATEGORY_ENDPOINT",
57
59
  "KB_COMMENT_SUFFIX",
@@ -179,6 +179,30 @@ def _plain_text_html(text: str) -> str:
179
179
  return "<p>" + "<br>".join(lines) + "</p>"
180
180
 
181
181
 
182
+ #: What a browser strips from the front of a URL, and drops from inside one.
183
+ _URL_LEADING = "".join(chr(c) for c in range(0x21))
184
+ _URL_INNER = str.maketrans("", "", "\t\r\n")
185
+
186
+ #: The elements inside which the reader writes no image, so offers none.
187
+ _UNFORMATTED = ("pre", "code", "kbd", "samp")
188
+
189
+
190
+ def _is_data_uri(src: str) -> bool:
191
+ """Whether a browser reads ``src`` as a ``data:`` URI.
192
+
193
+ The scheme is case-insensitive, leading C0 controls and spaces are
194
+ stripped, and tabs and line breaks are dropped wherever they are.
195
+ """
196
+
197
+ return src.lstrip(_URL_LEADING).translate(_URL_INNER).casefold().startswith("data:")
198
+
199
+
200
+ def _image_id(el: Tag) -> str | None:
201
+ """The trimmed ``id`` of an element, or ``None`` when it has none."""
202
+
203
+ return str(el.get("id") or "").strip() or None
204
+
205
+
182
206
  def _soup(html: str) -> BeautifulSoup:
183
207
  """Parse ``html`` and drop what a browser does not display."""
184
208
 
@@ -402,6 +426,12 @@ class Link:
402
426
  enclosing_href : str or None
403
427
  For an ``<img>`` inside a link, that link's ``href``; ``None``
404
428
  otherwise. The image is read before the link around it.
429
+ image_id : str or None
430
+ For an ``<img>``, the value of its ``id`` attribute, trimmed, or
431
+ ``None`` when it has none or the element is a link. GLPI sets it to
432
+ the tag of the Document it created for an image it has not rewritten
433
+ into a document link, and that Document carries the same value as its
434
+ tag, so a caller can find it.
405
435
  """
406
436
 
407
437
  href: str
@@ -410,6 +440,7 @@ class Link:
410
440
  image: bool = False
411
441
  document_id: int | None = None
412
442
  enclosing_href: str | None = None
443
+ image_id: str | None = None
413
444
 
414
445
 
415
446
  #: A ``rewrite_link`` callback: called with each link and image a read meets,
@@ -664,6 +695,7 @@ class _Converter(MarkdownConverter):
664
695
  enclosing_href=(
665
696
  None if around is None else str(around.get("href") or "")
666
697
  ),
698
+ image_id=_image_id(el),
667
699
  )
668
700
  )
669
701
  if isinstance(answer, str):
@@ -1021,6 +1053,33 @@ class GlpiContentConverter:
1021
1053
  document_id = int(values[0])
1022
1054
  return document_id if document_id > 0 else None
1023
1055
 
1056
+ @staticmethod
1057
+ def data_image_ids(html: str) -> tuple[str, ...]:
1058
+ """Return the ``id`` of every image a body keeps as a ``data:`` URI.
1059
+
1060
+ Each ``<img>`` whose ``src`` a browser reads as a ``data:`` URI and
1061
+ that has a non-empty ``id`` contributes it, in document order and
1062
+ without repeats. The base64 is not decoded. An image inside ``<pre>``,
1063
+ ``<code>``, ``<kbd>`` or ``<samp>`` is left out: the reader shows it
1064
+ as no image, so it never offers it to ``rewrite_link`` either.
1065
+
1066
+ GLPI sets a tag on the Documents it creates from an upload or an
1067
+ editor paste, images or not. For an image it has not rewritten into a
1068
+ document link, that tag is the ``id`` of the ``<img>``
1069
+ (:attr:`Link.image_id`, :attr:`GetDocument.tag`). A caller calls this
1070
+ first, to decide whether it needs to read any Document at all.
1071
+ """
1072
+
1073
+ if "<" not in html:
1074
+ return ()
1075
+ ids = (
1076
+ _image_id(img)
1077
+ for img in _soup(html).find_all("img")
1078
+ if _is_data_uri(str(img.get("src") or ""))
1079
+ and img.find_parent(_UNFORMATTED) is None
1080
+ )
1081
+ return tuple(dict.fromkeys(i for i in ids if i is not None))
1082
+
1024
1083
  @staticmethod
1025
1084
  def document_image(
1026
1085
  document_id: int,
@@ -22,12 +22,16 @@ from glpi_python_client.models.api_schema._common import (
22
22
  )
23
23
  from glpi_python_client.models.api_schema.administration import (
24
24
  DeleteEntity,
25
+ DeleteGroup,
25
26
  DeleteUser,
26
27
  GetEntity,
28
+ GetGroup,
27
29
  GetUser,
28
30
  PatchEntity,
31
+ PatchGroup,
29
32
  PatchUser,
30
33
  PostEntity,
34
+ PostGroup,
31
35
  PostUser,
32
36
  )
33
37
  from glpi_python_client.models.api_schema.assets import (
@@ -141,6 +145,7 @@ __all__ = [
141
145
  "DeleteDocument",
142
146
  "DeleteEntity",
143
147
  "DeleteFollowup",
148
+ "DeleteGroup",
144
149
  "DeleteKBArticle",
145
150
  "DeleteKBArticleComment",
146
151
  "DeleteKBCategory",
@@ -161,6 +166,7 @@ __all__ = [
161
166
  "GetDocumentItem",
162
167
  "GetEntity",
163
168
  "GetFollowup",
169
+ "GetGroup",
164
170
  "GetKBArticle",
165
171
  "GetKBArticleComment",
166
172
  "GetKBArticleRevision",
@@ -197,6 +203,7 @@ __all__ = [
197
203
  "PatchDocument",
198
204
  "PatchEntity",
199
205
  "PatchFollowup",
206
+ "PatchGroup",
200
207
  "PatchKBArticle",
201
208
  "PatchKBArticleComment",
202
209
  "PatchKBCategory",
@@ -215,6 +222,7 @@ __all__ = [
215
222
  "PostDocument",
216
223
  "PostEntity",
217
224
  "PostFollowup",
225
+ "PostGroup",
218
226
  "PostKBArticle",
219
227
  "PostKBArticleComment",
220
228
  "PostKBCategory",
@@ -1,7 +1,8 @@
1
1
  """Administration entity schemas mirroring the ``/Administration`` endpoints.
2
2
 
3
- This subpackage exposes the per-verb Pydantic models for ``User`` and
4
- ``Entity``. Each module follows the same ``Get<Name>``/``Post<Name>``/
3
+ This subpackage exposes the per-verb Pydantic models for ``User``,
4
+ ``Entity`` and ``Group``. Each module follows the same
5
+ ``Get<Name>``/``Post<Name>``/
5
6
  ``Patch<Name>``/``Delete<Name>`` naming convention.
6
7
  """
7
8
 
@@ -11,6 +12,12 @@ from glpi_python_client.models.api_schema.administration._entity import (
11
12
  PatchEntity,
12
13
  PostEntity,
13
14
  )
15
+ from glpi_python_client.models.api_schema.administration._group import (
16
+ DeleteGroup,
17
+ GetGroup,
18
+ PatchGroup,
19
+ PostGroup,
20
+ )
14
21
  from glpi_python_client.models.api_schema.administration._user import (
15
22
  DeleteUser,
16
23
  GetUser,
@@ -20,11 +27,15 @@ from glpi_python_client.models.api_schema.administration._user import (
20
27
 
21
28
  __all__ = [
22
29
  "DeleteEntity",
30
+ "DeleteGroup",
23
31
  "DeleteUser",
24
32
  "GetEntity",
33
+ "GetGroup",
25
34
  "GetUser",
26
35
  "PatchEntity",
36
+ "PatchGroup",
27
37
  "PatchUser",
28
38
  "PostEntity",
39
+ "PostGroup",
29
40
  "PostUser",
30
41
  ]
@@ -0,0 +1,150 @@
1
+ """GLPI ``Group`` schemas for the ``/Administration/Group`` endpoints.
2
+
3
+ The field layout mirrors ``components.schemas.Group`` from the GLPI OpenAPI
4
+ contract. Read-only contract fields are not declared on the request models;
5
+ a caller that supplies one anyway sends it through ``extra_payload`` and the
6
+ server decides.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from datetime import datetime
12
+
13
+ from glpi_python_client.models._base import GlpiModel
14
+ from glpi_python_client.models.api_schema._common import IdNameRef
15
+
16
+
17
+ class GetGroup(GlpiModel):
18
+ """Response shape returned by ``GET /Administration/Group`` endpoints.
19
+
20
+ Mirrors ``components.schemas.Group``. All fields are optional because
21
+ the contract does not advertise a ``required`` array.
22
+
23
+ Parameters
24
+ ----------
25
+ id : int | None, optional
26
+ Native GLPI identifier (``readOnly``).
27
+ name : str | None, optional
28
+ Name of the group.
29
+ comment : str | None, optional
30
+ Free-form comment.
31
+ completename : str | None, optional
32
+ Full hierarchical path of the group (``readOnly``).
33
+ parent : IdNameRef | None, optional
34
+ Parent group in the hierarchy.
35
+ level : int | None, optional
36
+ Depth of the group in the hierarchy tree (``readOnly``).
37
+ entity : IdNameRef | None, optional
38
+ Entity the group belongs to.
39
+ code : str | None, optional
40
+ Group code (at most 255 characters).
41
+ ldap_field : str | None, optional
42
+ LDAP user attribute that may contain this group (memberof).
43
+ ldap_value : str | None, optional
44
+ LDAP attribute value to match for membership, usually the DN.
45
+ ldap_group_dn : str | None, optional
46
+ DN of the group in LDAP; only populated in certain cases.
47
+ maybe_requester : bool | None, optional
48
+ Can be a requester.
49
+ maybe_observer : bool | None, optional
50
+ Can be an observer.
51
+ maybe_assigned : bool | None, optional
52
+ Can be assigned (to tickets).
53
+ maybe_assigned_task : bool | None, optional
54
+ Can be assigned to tasks.
55
+ maybe_notified : bool | None, optional
56
+ Can be notified.
57
+ may_contain_items : bool | None, optional
58
+ Can contain items.
59
+ may_contain_users : bool | None, optional
60
+ Can contain users.
61
+ maybe_manager : bool | None, optional
62
+ Can be manager.
63
+ recursive_membership : bool | None, optional
64
+ Whether users of this group are implied members of all child
65
+ groups. Unrelated to entity recursion.
66
+ mfa_enforced : bool | None, optional
67
+ Whether two-factor authentication is enforced for members.
68
+ date_creation : datetime | None, optional
69
+ Creation timestamp.
70
+ date_mod : datetime | None, optional
71
+ Last modification timestamp.
72
+ """
73
+
74
+ id: int | None = None
75
+ name: str | None = None
76
+ comment: str | None = None
77
+ completename: str | None = None
78
+ parent: IdNameRef | None = None
79
+ level: int | None = None
80
+ entity: IdNameRef | None = None
81
+ code: str | None = None
82
+ ldap_field: str | None = None
83
+ ldap_value: str | None = None
84
+ ldap_group_dn: str | None = None
85
+ maybe_requester: bool | None = None
86
+ maybe_observer: bool | None = None
87
+ maybe_assigned: bool | None = None
88
+ maybe_assigned_task: bool | None = None
89
+ maybe_notified: bool | None = None
90
+ may_contain_items: bool | None = None
91
+ may_contain_users: bool | None = None
92
+ maybe_manager: bool | None = None
93
+ recursive_membership: bool | None = None
94
+ mfa_enforced: bool | None = None
95
+ date_creation: datetime | None = None
96
+ date_mod: datetime | None = None
97
+
98
+
99
+ class PostGroup(GlpiModel):
100
+ """Request body for ``POST /Administration/Group``.
101
+
102
+ Read-only contract fields (``id``, ``completename``, ``level``) and the
103
+ server-set timestamps are not declared; see :class:`GetGroup` for the
104
+ meaning of each field.
105
+ """
106
+
107
+ name: str | None = None
108
+ comment: str | None = None
109
+ parent: IdNameRef | None = None
110
+ entity: IdNameRef | None = None
111
+ code: str | None = None
112
+ ldap_field: str | None = None
113
+ ldap_value: str | None = None
114
+ ldap_group_dn: str | None = None
115
+ maybe_requester: bool | None = None
116
+ maybe_observer: bool | None = None
117
+ maybe_assigned: bool | None = None
118
+ maybe_assigned_task: bool | None = None
119
+ maybe_notified: bool | None = None
120
+ may_contain_items: bool | None = None
121
+ may_contain_users: bool | None = None
122
+ maybe_manager: bool | None = None
123
+ recursive_membership: bool | None = None
124
+ mfa_enforced: bool | None = None
125
+
126
+
127
+ class PatchGroup(PostGroup):
128
+ """Request body for ``PATCH /Administration/Group/{id}``.
129
+
130
+ The contract uses the same ``Group`` schema for create and
131
+ partial-update bodies; ``PatchGroup`` is kept distinct so client
132
+ mixins can express the intent of the operation explicitly.
133
+ """
134
+
135
+
136
+ class DeleteGroup(GlpiModel):
137
+ """Query parameters for ``DELETE /Administration/Group/{id}``.
138
+
139
+ Parameters
140
+ ----------
141
+ force : bool | None, optional
142
+ When ``True``, permanently delete the group instead of moving the
143
+ record to the GLPI trash. When ``False`` or :data:`None`, the
144
+ server applies its default soft-delete behaviour.
145
+ """
146
+
147
+ force: bool | None = None
148
+
149
+
150
+ __all__ = ["DeleteGroup", "GetGroup", "PatchGroup", "PostGroup"]
@@ -61,6 +61,20 @@ class GetDocument(GlpiModel):
61
61
  mime: str | None = None
62
62
  sha1sum: str | None = None
63
63
 
64
+ @property
65
+ def tag(self) -> str | None:
66
+ """The tag GLPI gave the Document, or ``None``.
67
+
68
+ GLPI sets a tag on the Documents it creates from an upload or an
69
+ editor paste, images or not. For an image it has not rewritten into
70
+ a document link, it is the ``id`` of the ``<img>`` left in the body
71
+ (see ``GlpiContentConverter.data_image_ids``). Read from
72
+ ``extra_payload``; ``None`` when absent, empty or not a string.
73
+ """
74
+
75
+ value = self.extra_payload.get("tag")
76
+ return value if isinstance(value, str) and value else None
77
+
64
78
 
65
79
  class PostDocument(GlpiModel):
66
80
  """Request body for ``POST /Management/Document``.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: glpi-python-client
3
- Version: 0.6.4
3
+ Version: 0.6.6
4
4
  Summary: A typed Python client for GLPI ITSM APIs.
5
5
  Project-URL: Homepage, https://github.com/baraline/glpi_python_client
6
6
  Project-URL: Documentation, https://glpi-python-client.readthedocs.io/en/latest/
@@ -1,4 +1,4 @@
1
- glpi_python_client/__init__.py,sha256=IjuloET5lqy2KL5c3sgXRYxppjxcVAv7mZUK-DERRNA,5630
1
+ glpi_python_client/__init__.py,sha256=PxyLPvdfRqFJPkX1RhaRjOJruLSmLJgW7M6_d2M634A,5762
2
2
  glpi_python_client/_errors.py,sha256=Bc9UvlxulbdcGvzZIKXnJCTL78sW78R6EFg50P-1dpY,7151
3
3
  glpi_python_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  glpi_python_client/rsql.py,sha256=3u3lH3_-F1bvHbVzxALXTDPoHyoMATP1f4QPTar_sEU,8974
@@ -10,10 +10,11 @@ glpi_python_client/_async/auth/_v1_session.py,sha256=3t1OammbfUm9E-ANynrBFryxjx5
10
10
  glpi_python_client/_async/auth/auth.py,sha256=1vGG5uBNG8lMjcm20-mgcsH2e0GWKGujQo4k6vck1lE,14694
11
11
  glpi_python_client/_async/clients/__init__.py,sha256=LCjaQ_tjb4icz04PvPQdmtfP6PgtymBKhnhA7PuW52I,637
12
12
  glpi_python_client/_async/clients/_base_client.py,sha256=Yq_Stx2BqZjNbvysrEMLYyOrfSYjzihN6alfQpy7pWA,7553
13
- glpi_python_client/_async/clients/client.py,sha256=K8f831VoXYvJrGYrvY1G-xElxlN27ogebkwkvbJkHeU,3847
14
- glpi_python_client/_async/clients/api/__init__.py,sha256=KGuk9Z0j3ed9c35NsaahPMRivSgSRwPxWZLyPoV-e9Q,1742
15
- glpi_python_client/_async/clients/api/administration/__init__.py,sha256=Ep01JuzEeCqyeWpIfj7SnAc5xm7yZrbECDl7DpzYSq4,510
13
+ glpi_python_client/_async/clients/client.py,sha256=4lZbd4wHNRqI2xbzjsff3iy8e4FnMgF-1Ezi0KyBtHM,3879
14
+ glpi_python_client/_async/clients/api/__init__.py,sha256=keInRT6ogmkeWETnNQC6VliBjZZk4ur2hRcIyWSv1_k,1776
15
+ glpi_python_client/_async/clients/api/administration/__init__.py,sha256=raqpFI2U8WDfeSRzN8-PcqhzD3sKP0oTamgj77-Nhxk,614
16
16
  glpi_python_client/_async/clients/api/administration/_entity.py,sha256=qG1SBMwOCZWHjHV4KfswY6bTaTQP9I83mQvOd-Xo70w,6453
17
+ glpi_python_client/_async/clients/api/administration/_group.py,sha256=uqx4Qqt7bWCYZlQayDIL5l_wW8kUg50ngxz3ktD771w,6511
17
18
  glpi_python_client/_async/clients/api/administration/_user.py,sha256=SQ_CM353cFD0BW0GQSQp__HZQlmJsAUuAiOgs-1NoNg,10085
18
19
  glpi_python_client/_async/clients/api/assets/__init__.py,sha256=WFWG5zcRf0kFtz35it3W83xOlqXJ77IcYI51KRSiL08,198
19
20
  glpi_python_client/_async/clients/api/assets/_computer.py,sha256=U-dDeYmY3MaS0BTqJAQThKtKTbvDS7BvshbXheD64rM,13634
@@ -40,7 +41,7 @@ glpi_python_client/_async/clients/api/plugins/__init__.py,sha256=mmY6HngIzna2AG3
40
41
  glpi_python_client/_async/clients/api/plugins/_fields.py,sha256=mGpPutvN8JUDUszdvLEE2kkQnz28rktshHdFhs9A1X4,15485
41
42
  glpi_python_client/_async/clients/commons/__init__.py,sha256=1-V_LgNwIYDyXdpLP2nijCTeGgajTcY-wt0C4aeRE3k,406
42
43
  glpi_python_client/_async/clients/commons/_config.py,sha256=JlUZy_oPQj0ldSbBVWcytMlvROyeCW0JldOpPrBmFMs,13080
43
- glpi_python_client/_async/clients/commons/_constants.py,sha256=VTLjX6Ei6QKcQB7l3Hzfo9lT34v09pdiNhJPDy8vvEk,1772
44
+ glpi_python_client/_async/clients/commons/_constants.py,sha256=Y2uQ0zE_7m0DLSKLg41JEbW4ww23Mk9g-hDbjNZjaZQ,1834
44
45
  glpi_python_client/_async/clients/commons/_filters.py,sha256=eQdrc06RhEuGHtvAqBJy1VrOVHVjeiFh82pvX-HxGw8,4695
45
46
  glpi_python_client/_async/clients/commons/_http.py,sha256=8x9H9eHw-neOYoQ2MAP2JmbVqcpno42pZlzfuhizpmk,15240
46
47
  glpi_python_client/_async/clients/commons/_payloads.py,sha256=7LEzk72WwLcLqxWXQ_rHcjAnpo3WIuq97X768-Zkvt8,3707
@@ -56,10 +57,11 @@ glpi_python_client/_sync/auth/_v1_session.py,sha256=dlnYA2yi_aWI6wTpfnRD3nzRvnw1
56
57
  glpi_python_client/_sync/auth/auth.py,sha256=azWE3bqDswxU-PtO-m_aKb-luSVPRrRXDVuq2aFRz18,14608
57
58
  glpi_python_client/_sync/clients/__init__.py,sha256=DWKGlB1KAgTQvmc_Dycz7rynNo0y_fr5Z4LcTxzn4iQ,623
58
59
  glpi_python_client/_sync/clients/_base_client.py,sha256=-59b4F383zVhcnt_KV8VY3f9nMr0SlRG9tm_d3yKRKU,7551
59
- glpi_python_client/_sync/clients/client.py,sha256=asaLf7l1n5srjZn6M7uDEhaWw5IqbQrkVyQ4BKeED_M,3789
60
- glpi_python_client/_sync/clients/api/__init__.py,sha256=uKQhoYxK16kj7hiktTCiIgikjw5w4QsXVige2Ytv7cU,1733
61
- glpi_python_client/_sync/clients/api/administration/__init__.py,sha256=lv5dUygrbxiBB_8392mjS_gYDcZmac_EbJgBD9DoZk8,508
60
+ glpi_python_client/_sync/clients/client.py,sha256=WTqrTs_0xSUSR8ez__JjHPgDBhsBUtyUFBhgZANNZqo,3821
61
+ glpi_python_client/_sync/clients/api/__init__.py,sha256=_nlTpSQyldJrTVvlifp__DQGF6_vqCl_YPWp6FCa-mM,1767
62
+ glpi_python_client/_sync/clients/api/administration/__init__.py,sha256=3s3Nfawu1FI97Q-WIlz5OgLZL7TszItF6l_JzQHVzaM,611
62
63
  glpi_python_client/_sync/clients/api/administration/_entity.py,sha256=jYvIl1hXcPQuAABa0I0TCKBG6FMHtO8xl55xhG5CRS8,6369
64
+ glpi_python_client/_sync/clients/api/administration/_group.py,sha256=84J78kE35pBhokqrRTMDj2H7yyU8UWvPfdbCcDLR8hY,6427
63
65
  glpi_python_client/_sync/clients/api/administration/_user.py,sha256=8QuL9G_6donw0UNtpQEyxh1gHpjfymnDcGy-7AW7IcA,9989
64
66
  glpi_python_client/_sync/clients/api/assets/__init__.py,sha256=_4-S2nkXQNuFXzZzXoLR3DnykaKTzata9ZPN74ASO8s,197
65
67
  glpi_python_client/_sync/clients/api/assets/_computer.py,sha256=jILfoo7L2FJwL7Q_uFXkpfDBCuPczCAJ_TbAoyiGrj0,13490
@@ -86,7 +88,7 @@ glpi_python_client/_sync/clients/api/plugins/__init__.py,sha256=x9RwOUD34H1bPsJJ
86
88
  glpi_python_client/_sync/clients/api/plugins/_fields.py,sha256=jkGTF0T7eQHzOPcKSqaEmwJx-EgETcgncXyke69HJeg,15368
87
89
  glpi_python_client/_sync/clients/commons/__init__.py,sha256=Ur1hbt0_A9TlRqYi97PmlfsXYS_EMGk0lVjkHx_cyBE,404
88
90
  glpi_python_client/_sync/clients/commons/_config.py,sha256=f2wVtUUdh6rxaf-s84X8Fhfn0zxn0OPJg445dKeCSVw,13056
89
- glpi_python_client/_sync/clients/commons/_constants.py,sha256=VTLjX6Ei6QKcQB7l3Hzfo9lT34v09pdiNhJPDy8vvEk,1772
91
+ glpi_python_client/_sync/clients/commons/_constants.py,sha256=Y2uQ0zE_7m0DLSKLg41JEbW4ww23Mk9g-hDbjNZjaZQ,1834
90
92
  glpi_python_client/_sync/clients/commons/_filters.py,sha256=eQdrc06RhEuGHtvAqBJy1VrOVHVjeiFh82pvX-HxGw8,4695
91
93
  glpi_python_client/_sync/clients/commons/_http.py,sha256=5p8S1J1usIB0WQg5u3h6zovr14an-NoIGNfwAHEOSS4,15239
92
94
  glpi_python_client/_sync/clients/commons/_payloads.py,sha256=7LEzk72WwLcLqxWXQ_rHcjAnpo3WIuq97X768-Zkvt8,3707
@@ -95,15 +97,16 @@ glpi_python_client/_sync/clients/custom/__init__.py,sha256=sxfGlHVulucP7bacWhUMi
95
97
  glpi_python_client/_sync/clients/custom/_statistics.py,sha256=xrD5C99ieROJx60iUomBHacQ_49HpgQL4XjnfREUQvs,38616
96
98
  glpi_python_client/_sync/clients/custom/_ticket_context.py,sha256=LDUvKNr8VpZonF5bDo8QYYwMEsWmayVU28Ra3GonNmE,3004
97
99
  glpi_python_client/content/__init__.py,sha256=TaZPo8WSvVj4U-hSnxhUcqU8JnCKNZYpfQf4pH45aN4,424
98
- glpi_python_client/content/conversion.py,sha256=bglYY5IgoTv2SdS0Gton7WgsyzwwQ5GdU_2dLloL2l0,42860
99
- glpi_python_client/models/__init__.py,sha256=CLvk-1A-88114X7HaS9P6KyfovQjCPRX08J56rJaGls,5454
100
+ glpi_python_client/content/conversion.py,sha256=0dxQW0ojSo1KC8zcTFa476KQa1i3sLWzQ8gTEEVslys,45341
101
+ glpi_python_client/models/__init__.py,sha256=7OxYvXSQc4QdK1VAQb_1BGvgwGc6211gKnSdLWy-qhY,5586
100
102
  glpi_python_client/models/_base.py,sha256=pYg69KHuR5TDED5utr4Knr7HncBnLTxXHJZYf6vEUKA,12183
101
103
  glpi_python_client/models/api_schema/__init__.py,sha256=yTxUYih1Xy7NUogzD7UKp2b8HyE4wdeC_FCKS6ZRVVM,1176
102
104
  glpi_python_client/models/api_schema/_common.py,sha256=wVtaIkUM9iS3jyDb0bpFopEkP_3vP1ax7D8rwHe9QkI,2004
103
105
  glpi_python_client/models/api_schema/_content.py,sha256=DlsEmlXIK_0jyXh3j5PgU6vkwch2o0I8UpUkLq2fqYw,12826
104
106
  glpi_python_client/models/api_schema/enums.py,sha256=bHNCuumOXIZm80n5RqIhCzFqizII-3er41mn2aktF5s,5071
105
- glpi_python_client/models/api_schema/administration/__init__.py,sha256=uTr2rEMYq47T5foUMHm1rtsBmEQvdD1EQw5CZOUN38Y,706
107
+ glpi_python_client/models/api_schema/administration/__init__.py,sha256=CFZ36OIk1PPBzkXkCtOo2C6s7JfaKSp63Nw4-b_coxc,924
106
108
  glpi_python_client/models/api_schema/administration/_entity.py,sha256=KdbxMyPXumGZKmmabDIciF3nFS61pLRp5SBOTFa8slk,3297
109
+ glpi_python_client/models/api_schema/administration/_group.py,sha256=8aWnDupTT9lxGtzMC_dAG8yDLO_NKtFz-OCNHOYESi0,5298
107
110
  glpi_python_client/models/api_schema/administration/_user.py,sha256=MQ6cHKdni98sjm-Whf0GKdGpNjPgGGJpUBgVjlGa19Q,15872
108
111
  glpi_python_client/models/api_schema/assets/__init__.py,sha256=QikGfQwwhjKt3vUd5FYC1F-BeClP5ByK1hahRRumdsA,562
109
112
  glpi_python_client/models/api_schema/assets/_computer.py,sha256=me3px9rkaN_ZataDVLjOer9wsZw_fRoRikxkLzXopsE,10304
@@ -127,7 +130,7 @@ glpi_python_client/models/api_schema/knowledgebase/_revision.py,sha256=KuTb_F85m
127
130
  glpi_python_client/models/api_schema/management/__init__.py,sha256=z5ntUAXiXXhZCEIsTVaTgJdXbg0ZW24TZctarfAqjr0,987
128
131
  glpi_python_client/models/api_schema/management/_contract.py,sha256=m6g__AKWvDQ09xKyUe8RhRS8xE1d1pOBjqOOG-8u6Ck,14045
129
132
  glpi_python_client/models/api_schema/management/_contract_cost.py,sha256=Bp2B3bA5u8CE_oBYY70QIUot8jFLCrX_icXZ4kpYqSE,4966
130
- glpi_python_client/models/api_schema/management/_document.py,sha256=RzKF-rNlz8CsAMa2BAaiqHNEE3-m1mci7vtdARls4uw,4607
133
+ glpi_python_client/models/api_schema/management/_document.py,sha256=kiEkTnOydGHjOyAwTbo020Kei4ffkBDj-9m775gLyfw,5200
131
134
  glpi_python_client/models/api_schema/management/_document_item.py,sha256=Bu9c9YdhSs5hGZ8dRfRxqS4g7VZ1A3EJtNbDLulKpWA,3113
132
135
  glpi_python_client/models/api_schema/plugins/__init__.py,sha256=GA0pr0PeoPOqwjUilrBujh0OqjUfZhzPWgIlgDJcZog,544
133
136
  glpi_python_client/models/api_schema/plugins/_fields.py,sha256=Se-12ZDqk_-on_37YF_hRD5S0e0H6zyha3SXI9xb_9Y,6576
@@ -136,7 +139,7 @@ glpi_python_client/models/custom_schema/_ticket_context.py,sha256=jnlfgho-aDbmA9
136
139
  glpi_python_client/testing/__init__.py,sha256=iLtUyJjyv3fEhy3PN25NxNTbtqDl3SoHLbNium0Z80U,568
137
140
  glpi_python_client/testing/fixtures.py,sha256=HKt6MXHgcmLn4L98pSwp-oGlc9C--_vrABNe5G_iBeg,1286
138
141
  glpi_python_client/testing/utils.py,sha256=JyO3w2d-0kYdtC1hEZ2Tu_YjnCz8nUQ_Ck05-1EhsX4,4404
139
- glpi_python_client-0.6.4.dist-info/METADATA,sha256=EFNn15U6Eqjua7bVLPM22W9N48EEMza3gq3f0--MsJs,8234
140
- glpi_python_client-0.6.4.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
141
- glpi_python_client-0.6.4.dist-info/licenses/LICENSE,sha256=nL8re-TIKkxCoL7OkSBja6EB4NsQR1HoOJgxv_MEhtc,1088
142
- glpi_python_client-0.6.4.dist-info/RECORD,,
142
+ glpi_python_client-0.6.6.dist-info/METADATA,sha256=RiSWOUSsejQZVqs8hWndBh8TB9DPm07vq56KpoluRZo,8234
143
+ glpi_python_client-0.6.6.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
144
+ glpi_python_client-0.6.6.dist-info/licenses/LICENSE,sha256=nL8re-TIKkxCoL7OkSBja6EB4NsQR1HoOJgxv_MEhtc,1088
145
+ glpi_python_client-0.6.6.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.32.4
2
+ Generator: hatchling 1.33.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any