redblack-python-sdk 0.1.0__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.
@@ -0,0 +1,889 @@
1
+ """Review-set reads, order-add sessions, and order-release sessions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator, Mapping, Sequence
6
+ from dataclasses import dataclass
7
+ from threading import Event
8
+ from typing import Any, Literal, overload
9
+
10
+ from ..models import (
11
+ OrderAddCancelResult,
12
+ OrderAddResult,
13
+ OrderAddSession,
14
+ OrderAddSessionItem,
15
+ OrderCreate,
16
+ OrderCreateResult,
17
+ PendingOrder,
18
+ ReleaseError,
19
+ ReleaseValidationResult,
20
+ ReviewSet,
21
+ ReviewSetReleaseSession,
22
+ )
23
+ from ..models.generated import (
24
+ ReviewSetsCancelReleaseResponse200,
25
+ ReviewSetsCancelReleaseResponse202,
26
+ )
27
+ from ..odata import DEFAULT_PAGE_SIZE, ODataQuery
28
+ from ..pagination import Page, Paginator
29
+ from ..polling import Poller, PollOutcome
30
+ from ._base import (
31
+ ApiResponseProtocol,
32
+ ClientProtocol,
33
+ HttpInfoMode,
34
+ Resource,
35
+ body_mapping,
36
+ body_value,
37
+ enum_value,
38
+ read_member,
39
+ require_positive_identifier,
40
+ )
41
+ from ._exports import WorkflowProtocolError
42
+
43
+
44
+ def _optional_boolean(name: str, value: bool | None) -> None:
45
+ if value is not None and not isinstance(value, bool):
46
+ raise TypeError(f"{name} must be a boolean")
47
+
48
+
49
+ def _set_optional_boolean(body: dict[str, Any], wire_name: str, value: bool | None) -> None:
50
+ _optional_boolean(wire_name, value)
51
+ if value is None:
52
+ return
53
+ if wire_name in body and body[wire_name] != value:
54
+ raise ValueError(f"{wire_name} conflicts with the request body")
55
+ body[wire_name] = value
56
+
57
+
58
+ def _validate_versioned_id(value: object) -> dict[str, int]:
59
+ item = body_mapping(value)
60
+ identifier = item.get("id")
61
+ row_version = item.get("rowVersion", item.get("row_version"))
62
+ if isinstance(identifier, bool) or not isinstance(identifier, int):
63
+ raise TypeError("order id must be an integer")
64
+ require_positive_identifier("order id", identifier)
65
+ if isinstance(row_version, bool) or not isinstance(row_version, int):
66
+ raise TypeError("rowVersion must be an integer")
67
+ if row_version < 0:
68
+ raise ValueError("rowVersion must be greater than or equal to zero")
69
+ return {"id": identifier, "rowVersion": row_version}
70
+
71
+
72
+ @dataclass(frozen=True, slots=True)
73
+ class ReleaseCriteria:
74
+ """Validated release target selection using RedBlack FilterMode values."""
75
+
76
+ mode: int
77
+ filter: str | None = None
78
+ ids: tuple[object, ...] | None = None
79
+
80
+ def __post_init__(self) -> None:
81
+ if isinstance(self.mode, bool) or not isinstance(self.mode, int):
82
+ raise TypeError("release criteria mode must be an integer")
83
+ if self.mode not in {0, 1, 2}:
84
+ raise ValueError("release criteria mode must be 0, 1, or 2")
85
+ if self.mode == 0:
86
+ if not isinstance(self.filter, str) or not self.filter.strip():
87
+ raise ValueError("Filtered release criteria requires a filter")
88
+ if self.ids is not None:
89
+ raise ValueError("Filtered release criteria must not include ids")
90
+ elif self.mode == 1:
91
+ if self.filter is not None:
92
+ raise ValueError("Specific release criteria must not include a filter")
93
+ if not self.ids:
94
+ raise ValueError("Specific release criteria requires ids")
95
+ if len(self.ids) > 500:
96
+ raise ValueError("Specific release criteria is limited to 500 ids")
97
+ for item in self.ids:
98
+ _validate_versioned_id(item)
99
+ elif self.filter is not None or self.ids is not None:
100
+ raise ValueError("All release criteria must not include filter or ids")
101
+
102
+ @classmethod
103
+ def filtered(cls, filter: str) -> ReleaseCriteria:
104
+ return cls(mode=0, filter=filter)
105
+
106
+ @classmethod
107
+ def specific(cls, ids: Sequence[object]) -> ReleaseCriteria:
108
+ return cls(mode=1, ids=tuple(ids))
109
+
110
+ @classmethod
111
+ def all(cls) -> ReleaseCriteria:
112
+ """Explicitly target all pending orders in the review set."""
113
+
114
+ return cls(mode=2)
115
+
116
+ def to_wire(self) -> dict[str, Any]:
117
+ if self.mode == 0:
118
+ return {"mode": 0, "filter": self.filter, "ids": None}
119
+ if self.mode == 1:
120
+ return {
121
+ "mode": 1,
122
+ "filter": None,
123
+ "ids": [_validate_versioned_id(item) for item in self.ids or ()],
124
+ }
125
+ return {"mode": 2, "filter": None, "ids": None}
126
+
127
+
128
+ def _normalize_criteria(value: ReleaseCriteria | Mapping[str, object]) -> dict[str, Any]:
129
+ if isinstance(value, ReleaseCriteria):
130
+ return value.to_wire()
131
+ body = body_mapping(value)
132
+ raw_mode = body.get("mode")
133
+ if isinstance(raw_mode, bool) or not isinstance(raw_mode, int):
134
+ raise TypeError("release criteria mode must be an integer")
135
+ raw_ids = body.get("ids")
136
+ ids: tuple[object, ...] | None
137
+ if raw_ids is None:
138
+ ids = None
139
+ elif isinstance(raw_ids, Sequence) and not isinstance(raw_ids, str | bytes | bytearray):
140
+ ids = tuple(raw_ids)
141
+ else:
142
+ raise TypeError("release criteria ids must be a sequence")
143
+ return ReleaseCriteria(
144
+ mode=raw_mode,
145
+ filter=body.get("filter"),
146
+ ids=ids,
147
+ ).to_wire()
148
+
149
+
150
+ @dataclass(slots=True)
151
+ class OrderAddSessionHandle:
152
+ _client: ClientProtocol
153
+ review_set_id: int
154
+ session_id: int
155
+ latest: OrderAddSession | None = None
156
+
157
+ def __post_init__(self) -> None:
158
+ require_positive_identifier("review_set_id", self.review_set_id)
159
+ require_positive_identifier("session_id", self.session_id)
160
+
161
+ @property
162
+ def _path(self) -> dict[str, int]:
163
+ return {
164
+ "reviewSetId": self.review_set_id,
165
+ "sessionId": self.session_id,
166
+ }
167
+
168
+ def _refresh_with_timeout(self, timeout_budget: float | None) -> OrderAddSession:
169
+ if timeout_budget is None:
170
+ return self.refresh()
171
+ response = self._client.request(
172
+ "ReviewSets_GetOrderAddSession",
173
+ path_params=self._path,
174
+ timeout_budget=timeout_budget,
175
+ )
176
+ self.latest = response.data
177
+ return response.data
178
+
179
+ def refresh(self) -> OrderAddSession:
180
+ response = self._client.request("ReviewSets_GetOrderAddSession", path_params=self._path)
181
+ self.latest = response.data
182
+ return response.data
183
+
184
+ status = refresh
185
+
186
+ @staticmethod
187
+ def _classify(value: object) -> PollOutcome:
188
+ status = enum_value(value, "session_status", "sessionStatus")
189
+ if status == 3:
190
+ return PollOutcome.SUCCEEDED
191
+ if status == 19:
192
+ return PollOutcome.NEEDS_INPUT
193
+ if status in {4, 5, 20}:
194
+ return PollOutcome.FAILED
195
+ return PollOutcome.PENDING
196
+
197
+ def wait(
198
+ self,
199
+ *,
200
+ timeout: float | None = 300.0,
201
+ poll_interval: float = 1.0,
202
+ max_interval: float = 30.0,
203
+ stop_event: Event | None = None,
204
+ raise_on_failure: bool = True,
205
+ ) -> OrderAddSession:
206
+ return Poller(
207
+ refresh=self.refresh,
208
+ classify=self._classify,
209
+ refresh_with_timeout=self._refresh_with_timeout,
210
+ ).wait(
211
+ timeout=timeout,
212
+ poll_interval=poll_interval,
213
+ max_interval=max_interval,
214
+ stop_event=stop_event,
215
+ raise_on_failure=raise_on_failure,
216
+ )
217
+
218
+ def results(
219
+ self,
220
+ *,
221
+ filter: str | None = None,
222
+ order_by: str | None = None,
223
+ top: int | None = None,
224
+ skip: int | None = None,
225
+ count: bool | None = None,
226
+ ) -> Page[OrderAddResult]:
227
+ query = ODataQuery(filter=filter, order_by=order_by, top=top, skip=skip, count=count)
228
+ response = self._client.request(
229
+ "ReviewSets_QueryOrderAddSessionResults",
230
+ path_params=self._path,
231
+ query=query.to_params(),
232
+ )
233
+ return Page.from_data(response.data, query=query, http=response.http)
234
+
235
+ def iter_results(
236
+ self,
237
+ *,
238
+ filter: str | None = None,
239
+ order_by: str | None = None,
240
+ count: bool | None = None,
241
+ start_skip: int = 0,
242
+ page_size: int = DEFAULT_PAGE_SIZE,
243
+ max_items: int | None = None,
244
+ ) -> Iterator[OrderAddResult]:
245
+ base = ODataQuery(filter=filter, order_by=order_by, count=count, skip=start_skip)
246
+ return Paginator(
247
+ fetch_page=lambda query: self.results(
248
+ filter=query.filter,
249
+ order_by=query.order_by,
250
+ top=query.top,
251
+ skip=query.skip,
252
+ count=query.count,
253
+ ),
254
+ query=base,
255
+ page_size=page_size,
256
+ max_items=max_items,
257
+ ).iter_items()
258
+
259
+ def cancel(self) -> OrderAddCancelResult:
260
+ response = self._client.request("ReviewSets_CancelOrderAddSession", path_params=self._path)
261
+ return response.data
262
+
263
+ @property
264
+ def has_failures(self) -> bool:
265
+ error_count = read_member(self.latest, "error_count", "errorCount")
266
+ return (
267
+ isinstance(error_count, int) and not isinstance(error_count, bool) and error_count > 0
268
+ )
269
+
270
+
271
+ @dataclass(slots=True)
272
+ class ReleaseSessionHandle:
273
+ _client: ClientProtocol
274
+ review_set_id: int
275
+ session_id: int
276
+ latest: object | None = None
277
+
278
+ def __post_init__(self) -> None:
279
+ require_positive_identifier("review_set_id", self.review_set_id)
280
+ require_positive_identifier("session_id", self.session_id)
281
+
282
+ @property
283
+ def _path(self) -> dict[str, int]:
284
+ return {
285
+ "reviewSetId": self.review_set_id,
286
+ "sessionId": self.session_id,
287
+ }
288
+
289
+ def _refresh_with_timeout(self, timeout_budget: float | None) -> ReviewSetReleaseSession:
290
+ if timeout_budget is None:
291
+ return self.refresh()
292
+ response = self._client.request(
293
+ "ReviewSets_GetReleaseSession",
294
+ path_params=self._path,
295
+ timeout_budget=timeout_budget,
296
+ )
297
+ self.latest = response.data
298
+ return response.data
299
+
300
+ def refresh(self) -> ReviewSetReleaseSession:
301
+ response = self._client.request("ReviewSets_GetReleaseSession", path_params=self._path)
302
+ self.latest = response.data
303
+ return response.data
304
+
305
+ status = refresh
306
+
307
+ @staticmethod
308
+ def _classify(value: object) -> PollOutcome:
309
+ status = enum_value(value, "session_status", "sessionStatus")
310
+ if status == 5:
311
+ return PollOutcome.SUCCEEDED
312
+ if status in {6, 7}:
313
+ return PollOutcome.FAILED
314
+ return PollOutcome.PENDING
315
+
316
+ def wait(
317
+ self,
318
+ *,
319
+ timeout: float | None = 300.0,
320
+ poll_interval: float = 1.0,
321
+ max_interval: float = 30.0,
322
+ stop_event: Event | None = None,
323
+ raise_on_failure: bool = True,
324
+ ) -> ReviewSetReleaseSession:
325
+ return Poller(
326
+ refresh=self.refresh,
327
+ classify=self._classify,
328
+ refresh_with_timeout=self._refresh_with_timeout,
329
+ ).wait(
330
+ timeout=timeout,
331
+ poll_interval=poll_interval,
332
+ max_interval=max_interval,
333
+ stop_event=stop_event,
334
+ raise_on_failure=raise_on_failure,
335
+ )
336
+
337
+ def validation_results(
338
+ self,
339
+ *,
340
+ filter: str | None = None,
341
+ order_by: str | None = None,
342
+ top: int | None = None,
343
+ skip: int | None = None,
344
+ count: bool | None = None,
345
+ ) -> Page[ReleaseValidationResult]:
346
+ query = ODataQuery(filter=filter, order_by=order_by, top=top, skip=skip, count=count)
347
+ response = self._client.request(
348
+ "ReviewSets_QueryReleaseValidationResults",
349
+ path_params=self._path,
350
+ query=query.to_params(),
351
+ )
352
+ return Page.from_data(response.data, query=query, http=response.http)
353
+
354
+ def errors(
355
+ self,
356
+ *,
357
+ filter: str | None = None,
358
+ order_by: str | None = None,
359
+ top: int | None = None,
360
+ skip: int | None = None,
361
+ count: bool | None = None,
362
+ ) -> Page[ReleaseError]:
363
+ query = ODataQuery(filter=filter, order_by=order_by, top=top, skip=skip, count=count)
364
+ response = self._client.request(
365
+ "ReviewSets_QueryReleaseResults",
366
+ path_params=self._path,
367
+ query=query.to_params(),
368
+ )
369
+ # The documented 204 success deliberately becomes an empty Page.
370
+ return Page.from_data(response.data, query=query, http=response.http)
371
+
372
+ def cancel(
373
+ self,
374
+ ) -> ReviewSetsCancelReleaseResponse200 | ReviewSetsCancelReleaseResponse202:
375
+ response = self._client.request("ReviewSets_CancelRelease", path_params=self._path)
376
+ self.latest = response.data
377
+ return response.data
378
+
379
+ @property
380
+ def has_failures(self) -> bool:
381
+ stats = read_member(self.latest, "release_stats", "releaseStats")
382
+ failed = read_member(stats, "failed")
383
+ return isinstance(failed, int) and not isinstance(failed, bool) and failed > 0
384
+
385
+
386
+ class ReviewSetsResource(Resource[HttpInfoMode]):
387
+ OPERATION_IDS = frozenset(
388
+ {
389
+ "ReviewSets_Query",
390
+ "ReviewSetsPendingOrders_Query",
391
+ "ReviewSets_CreateOrder",
392
+ "ReviewSets_CreateOrderAddSession",
393
+ "ReviewSets_GetOrderAddSession",
394
+ "ReviewSets_QueryOrderAddSessionResults",
395
+ "ReviewSets_CancelOrderAddSession",
396
+ "ReviewSets_ReleaseOrders",
397
+ "ReviewSets_GetReleaseSession",
398
+ "ReviewSets_QueryReleaseValidationResults",
399
+ "ReviewSets_QueryReleaseResults",
400
+ "ReviewSets_CancelRelease",
401
+ }
402
+ )
403
+
404
+ def _collection(
405
+ self,
406
+ operation_id: str,
407
+ *,
408
+ path_params: Mapping[str, object] | None = None,
409
+ filter: str | None = None,
410
+ order_by: str | None = None,
411
+ top: int | None = None,
412
+ skip: int | None = None,
413
+ count: bool | None = None,
414
+ ) -> Page[Any] | ApiResponseProtocol[Page[Any]]:
415
+ return self._page(
416
+ operation_id,
417
+ path_params=path_params,
418
+ odata=ODataQuery(
419
+ filter=filter,
420
+ order_by=order_by,
421
+ top=top,
422
+ skip=skip,
423
+ count=count,
424
+ ),
425
+ )
426
+
427
+ @overload
428
+ def list(
429
+ self: ReviewSetsResource[Literal[False]],
430
+ *,
431
+ filter: str | None = None,
432
+ order_by: str | None = None,
433
+ top: int | None = None,
434
+ skip: int | None = None,
435
+ count: bool | None = None,
436
+ ) -> Page[ReviewSet]: ...
437
+
438
+ @overload
439
+ def list(
440
+ self: ReviewSetsResource[Literal[True]],
441
+ *,
442
+ filter: str | None = None,
443
+ order_by: str | None = None,
444
+ top: int | None = None,
445
+ skip: int | None = None,
446
+ count: bool | None = None,
447
+ ) -> ApiResponseProtocol[Page[ReviewSet]]: ...
448
+
449
+ def list(
450
+ self,
451
+ *,
452
+ filter: str | None = None,
453
+ order_by: str | None = None,
454
+ top: int | None = None,
455
+ skip: int | None = None,
456
+ count: bool | None = None,
457
+ ) -> Page[ReviewSet] | ApiResponseProtocol[Page[ReviewSet]]:
458
+ return self._collection(
459
+ "ReviewSets_Query",
460
+ filter=filter,
461
+ order_by=order_by,
462
+ top=top,
463
+ skip=skip,
464
+ count=count,
465
+ )
466
+
467
+ @overload
468
+ def list_pending_orders(
469
+ self: ReviewSetsResource[Literal[False]],
470
+ review_set_id: int,
471
+ *,
472
+ filter: str | None = None,
473
+ order_by: str | None = None,
474
+ top: int | None = None,
475
+ skip: int | None = None,
476
+ count: bool | None = None,
477
+ ) -> Page[PendingOrder]: ...
478
+
479
+ @overload
480
+ def list_pending_orders(
481
+ self: ReviewSetsResource[Literal[True]],
482
+ review_set_id: int,
483
+ *,
484
+ filter: str | None = None,
485
+ order_by: str | None = None,
486
+ top: int | None = None,
487
+ skip: int | None = None,
488
+ count: bool | None = None,
489
+ ) -> ApiResponseProtocol[Page[PendingOrder]]: ...
490
+
491
+ def list_pending_orders(
492
+ self,
493
+ review_set_id: int,
494
+ *,
495
+ filter: str | None = None,
496
+ order_by: str | None = None,
497
+ top: int | None = None,
498
+ skip: int | None = None,
499
+ count: bool | None = None,
500
+ ) -> Page[PendingOrder] | ApiResponseProtocol[Page[PendingOrder]]:
501
+ require_positive_identifier("review_set_id", review_set_id)
502
+ return self._collection(
503
+ "ReviewSetsPendingOrders_Query",
504
+ path_params={"reviewSetId": review_set_id},
505
+ filter=filter,
506
+ order_by=order_by,
507
+ top=top,
508
+ skip=skip,
509
+ count=count,
510
+ )
511
+
512
+ @overload
513
+ def create_order(
514
+ self: ReviewSetsResource[Literal[False]],
515
+ review_set_id: int,
516
+ order: OrderCreate | Mapping[str, object],
517
+ *,
518
+ ignore_warnings: bool | None = None,
519
+ ) -> OrderCreateResult: ...
520
+
521
+ @overload
522
+ def create_order(
523
+ self: ReviewSetsResource[Literal[True]],
524
+ review_set_id: int,
525
+ order: OrderCreate | Mapping[str, object],
526
+ *,
527
+ ignore_warnings: bool | None = None,
528
+ ) -> ApiResponseProtocol[OrderCreateResult]: ...
529
+
530
+ def create_order(
531
+ self,
532
+ review_set_id: int,
533
+ order: OrderCreate | Mapping[str, object],
534
+ *,
535
+ ignore_warnings: bool | None = None,
536
+ ) -> OrderCreateResult | ApiResponseProtocol[OrderCreateResult]:
537
+ require_positive_identifier("review_set_id", review_set_id)
538
+ payload = body_mapping(order)
539
+ _set_optional_boolean(payload, "ignoreWarnings", ignore_warnings)
540
+ response = self._request(
541
+ "ReviewSets_CreateOrder",
542
+ path_params={"reviewSetId": review_set_id},
543
+ json=payload,
544
+ )
545
+ return self._finish(response, response.data)
546
+
547
+ @overload
548
+ def start_order_add(
549
+ self: ReviewSetsResource[Literal[False]],
550
+ review_set_id: int,
551
+ orders: Sequence[OrderAddSessionItem | Mapping[str, object]],
552
+ *,
553
+ ignore_warnings: bool | None = None,
554
+ ) -> OrderAddSessionHandle: ...
555
+
556
+ @overload
557
+ def start_order_add(
558
+ self: ReviewSetsResource[Literal[True]],
559
+ review_set_id: int,
560
+ orders: Sequence[OrderAddSessionItem | Mapping[str, object]],
561
+ *,
562
+ ignore_warnings: bool | None = None,
563
+ ) -> ApiResponseProtocol[OrderAddSessionHandle]: ...
564
+
565
+ def start_order_add(
566
+ self,
567
+ review_set_id: int,
568
+ orders: Sequence[OrderAddSessionItem | Mapping[str, object]],
569
+ *,
570
+ ignore_warnings: bool | None = None,
571
+ ) -> OrderAddSessionHandle | ApiResponseProtocol[OrderAddSessionHandle]:
572
+ require_positive_identifier("review_set_id", review_set_id)
573
+ if isinstance(orders, str | bytes | bytearray):
574
+ raise TypeError("orders must be a sequence of order requests")
575
+ order_values = list(orders)
576
+ if not 1 <= len(order_values) <= 500:
577
+ raise ValueError("orders must contain between 1 and 500 entries")
578
+ payload: dict[str, Any] = {"orders": [body_value(order) for order in order_values]}
579
+ _set_optional_boolean(payload, "ignoreWarnings", ignore_warnings)
580
+ response = self._request(
581
+ "ReviewSets_CreateOrderAddSession",
582
+ path_params={"reviewSetId": review_set_id},
583
+ json=payload,
584
+ )
585
+ session_id = read_member(response.data, "id")
586
+ if isinstance(session_id, bool) or not isinstance(session_id, int):
587
+ raise WorkflowProtocolError("order-add create response omitted integer id")
588
+ handle = OrderAddSessionHandle(
589
+ self._client,
590
+ review_set_id,
591
+ session_id,
592
+ latest=response.data,
593
+ )
594
+ return self._finish(response, handle)
595
+
596
+ @overload
597
+ def get_order_add_session(
598
+ self: ReviewSetsResource[Literal[False]], review_set_id: int, session_id: int
599
+ ) -> OrderAddSessionHandle: ...
600
+
601
+ @overload
602
+ def get_order_add_session(
603
+ self: ReviewSetsResource[Literal[True]], review_set_id: int, session_id: int
604
+ ) -> ApiResponseProtocol[OrderAddSessionHandle]: ...
605
+
606
+ def get_order_add_session(
607
+ self, review_set_id: int, session_id: int
608
+ ) -> OrderAddSessionHandle | ApiResponseProtocol[OrderAddSessionHandle]:
609
+ require_positive_identifier("review_set_id", review_set_id)
610
+ require_positive_identifier("session_id", session_id)
611
+ response = self._request(
612
+ "ReviewSets_GetOrderAddSession",
613
+ path_params={"reviewSetId": review_set_id, "sessionId": session_id},
614
+ )
615
+ handle = OrderAddSessionHandle(
616
+ self._client, review_set_id, session_id, latest=response.data
617
+ )
618
+ return self._finish(response, handle)
619
+
620
+ @overload
621
+ def list_order_add_results(
622
+ self: ReviewSetsResource[Literal[False]],
623
+ review_set_id: int,
624
+ session_id: int,
625
+ *,
626
+ filter: str | None = None,
627
+ order_by: str | None = None,
628
+ top: int | None = None,
629
+ skip: int | None = None,
630
+ count: bool | None = None,
631
+ ) -> Page[OrderAddResult]: ...
632
+
633
+ @overload
634
+ def list_order_add_results(
635
+ self: ReviewSetsResource[Literal[True]],
636
+ review_set_id: int,
637
+ session_id: int,
638
+ *,
639
+ filter: str | None = None,
640
+ order_by: str | None = None,
641
+ top: int | None = None,
642
+ skip: int | None = None,
643
+ count: bool | None = None,
644
+ ) -> ApiResponseProtocol[Page[OrderAddResult]]: ...
645
+
646
+ def list_order_add_results(
647
+ self,
648
+ review_set_id: int,
649
+ session_id: int,
650
+ *,
651
+ filter: str | None = None,
652
+ order_by: str | None = None,
653
+ top: int | None = None,
654
+ skip: int | None = None,
655
+ count: bool | None = None,
656
+ ) -> Page[OrderAddResult] | ApiResponseProtocol[Page[OrderAddResult]]:
657
+ require_positive_identifier("review_set_id", review_set_id)
658
+ require_positive_identifier("session_id", session_id)
659
+ return self._collection(
660
+ "ReviewSets_QueryOrderAddSessionResults",
661
+ path_params={"reviewSetId": review_set_id, "sessionId": session_id},
662
+ filter=filter,
663
+ order_by=order_by,
664
+ top=top,
665
+ skip=skip,
666
+ count=count,
667
+ )
668
+
669
+ @overload
670
+ def cancel_order_add_session(
671
+ self: ReviewSetsResource[Literal[False]], review_set_id: int, session_id: int
672
+ ) -> OrderAddCancelResult: ...
673
+
674
+ @overload
675
+ def cancel_order_add_session(
676
+ self: ReviewSetsResource[Literal[True]], review_set_id: int, session_id: int
677
+ ) -> ApiResponseProtocol[OrderAddCancelResult]: ...
678
+
679
+ def cancel_order_add_session(
680
+ self, review_set_id: int, session_id: int
681
+ ) -> OrderAddCancelResult | ApiResponseProtocol[OrderAddCancelResult]:
682
+ require_positive_identifier("review_set_id", review_set_id)
683
+ require_positive_identifier("session_id", session_id)
684
+ response = self._request(
685
+ "ReviewSets_CancelOrderAddSession",
686
+ path_params={"reviewSetId": review_set_id, "sessionId": session_id},
687
+ )
688
+ return self._finish(response, response.data)
689
+
690
+ @overload
691
+ def start_release(
692
+ self: ReviewSetsResource[Literal[False]],
693
+ review_set_id: int,
694
+ *,
695
+ criteria: ReleaseCriteria | Mapping[str, object],
696
+ skip_validation: bool | None = None,
697
+ ignore_approval_warnings: bool | None = None,
698
+ allow_partial_release: bool | None = None,
699
+ ) -> ReleaseSessionHandle: ...
700
+
701
+ @overload
702
+ def start_release(
703
+ self: ReviewSetsResource[Literal[True]],
704
+ review_set_id: int,
705
+ *,
706
+ criteria: ReleaseCriteria | Mapping[str, object],
707
+ skip_validation: bool | None = None,
708
+ ignore_approval_warnings: bool | None = None,
709
+ allow_partial_release: bool | None = None,
710
+ ) -> ApiResponseProtocol[ReleaseSessionHandle]: ...
711
+
712
+ def start_release(
713
+ self,
714
+ review_set_id: int,
715
+ *,
716
+ criteria: ReleaseCriteria | Mapping[str, object],
717
+ skip_validation: bool | None = None,
718
+ ignore_approval_warnings: bool | None = None,
719
+ allow_partial_release: bool | None = None,
720
+ ) -> ReleaseSessionHandle | ApiResponseProtocol[ReleaseSessionHandle]:
721
+ require_positive_identifier("review_set_id", review_set_id)
722
+ payload: dict[str, Any] = {"criteria": _normalize_criteria(criteria)}
723
+ _set_optional_boolean(payload, "skipValidation", skip_validation)
724
+ _set_optional_boolean(payload, "ignoreApprovalWarnings", ignore_approval_warnings)
725
+ _set_optional_boolean(payload, "allowPartialRelease", allow_partial_release)
726
+ response = self._request(
727
+ "ReviewSets_ReleaseOrders",
728
+ path_params={"reviewSetId": review_set_id},
729
+ json=payload,
730
+ )
731
+ session_id = read_member(response.data, "id")
732
+ if isinstance(session_id, bool) or not isinstance(session_id, int):
733
+ raise WorkflowProtocolError("release create response omitted integer id")
734
+ handle = ReleaseSessionHandle(
735
+ self._client,
736
+ review_set_id,
737
+ session_id,
738
+ latest=response.data,
739
+ )
740
+ return self._finish(response, handle)
741
+
742
+ @overload
743
+ def get_release_session(
744
+ self: ReviewSetsResource[Literal[False]], review_set_id: int, session_id: int
745
+ ) -> ReleaseSessionHandle: ...
746
+
747
+ @overload
748
+ def get_release_session(
749
+ self: ReviewSetsResource[Literal[True]], review_set_id: int, session_id: int
750
+ ) -> ApiResponseProtocol[ReleaseSessionHandle]: ...
751
+
752
+ def get_release_session(
753
+ self, review_set_id: int, session_id: int
754
+ ) -> ReleaseSessionHandle | ApiResponseProtocol[ReleaseSessionHandle]:
755
+ require_positive_identifier("review_set_id", review_set_id)
756
+ require_positive_identifier("session_id", session_id)
757
+ response = self._request(
758
+ "ReviewSets_GetReleaseSession",
759
+ path_params={"reviewSetId": review_set_id, "sessionId": session_id},
760
+ )
761
+ handle = ReleaseSessionHandle(self._client, review_set_id, session_id, latest=response.data)
762
+ return self._finish(response, handle)
763
+
764
+ @overload
765
+ def list_release_validation_results(
766
+ self: ReviewSetsResource[Literal[False]],
767
+ review_set_id: int,
768
+ session_id: int,
769
+ *,
770
+ filter: str | None = None,
771
+ order_by: str | None = None,
772
+ top: int | None = None,
773
+ skip: int | None = None,
774
+ count: bool | None = None,
775
+ ) -> Page[ReleaseValidationResult]: ...
776
+
777
+ @overload
778
+ def list_release_validation_results(
779
+ self: ReviewSetsResource[Literal[True]],
780
+ review_set_id: int,
781
+ session_id: int,
782
+ *,
783
+ filter: str | None = None,
784
+ order_by: str | None = None,
785
+ top: int | None = None,
786
+ skip: int | None = None,
787
+ count: bool | None = None,
788
+ ) -> ApiResponseProtocol[Page[ReleaseValidationResult]]: ...
789
+
790
+ def list_release_validation_results(
791
+ self,
792
+ review_set_id: int,
793
+ session_id: int,
794
+ *,
795
+ filter: str | None = None,
796
+ order_by: str | None = None,
797
+ top: int | None = None,
798
+ skip: int | None = None,
799
+ count: bool | None = None,
800
+ ) -> Page[ReleaseValidationResult] | ApiResponseProtocol[Page[ReleaseValidationResult]]:
801
+ require_positive_identifier("review_set_id", review_set_id)
802
+ require_positive_identifier("session_id", session_id)
803
+ return self._collection(
804
+ "ReviewSets_QueryReleaseValidationResults",
805
+ path_params={"reviewSetId": review_set_id, "sessionId": session_id},
806
+ filter=filter,
807
+ order_by=order_by,
808
+ top=top,
809
+ skip=skip,
810
+ count=count,
811
+ )
812
+
813
+ @overload
814
+ def list_release_errors(
815
+ self: ReviewSetsResource[Literal[False]],
816
+ review_set_id: int,
817
+ session_id: int,
818
+ *,
819
+ filter: str | None = None,
820
+ order_by: str | None = None,
821
+ top: int | None = None,
822
+ skip: int | None = None,
823
+ count: bool | None = None,
824
+ ) -> Page[ReleaseError]: ...
825
+
826
+ @overload
827
+ def list_release_errors(
828
+ self: ReviewSetsResource[Literal[True]],
829
+ review_set_id: int,
830
+ session_id: int,
831
+ *,
832
+ filter: str | None = None,
833
+ order_by: str | None = None,
834
+ top: int | None = None,
835
+ skip: int | None = None,
836
+ count: bool | None = None,
837
+ ) -> ApiResponseProtocol[Page[ReleaseError]]: ...
838
+
839
+ def list_release_errors(
840
+ self,
841
+ review_set_id: int,
842
+ session_id: int,
843
+ *,
844
+ filter: str | None = None,
845
+ order_by: str | None = None,
846
+ top: int | None = None,
847
+ skip: int | None = None,
848
+ count: bool | None = None,
849
+ ) -> Page[ReleaseError] | ApiResponseProtocol[Page[ReleaseError]]:
850
+ require_positive_identifier("review_set_id", review_set_id)
851
+ require_positive_identifier("session_id", session_id)
852
+ return self._collection(
853
+ "ReviewSets_QueryReleaseResults",
854
+ path_params={"reviewSetId": review_set_id, "sessionId": session_id},
855
+ filter=filter,
856
+ order_by=order_by,
857
+ top=top,
858
+ skip=skip,
859
+ count=count,
860
+ )
861
+
862
+ @overload
863
+ def cancel_release(
864
+ self: ReviewSetsResource[Literal[False]], review_set_id: int, session_id: int
865
+ ) -> ReviewSetsCancelReleaseResponse200 | ReviewSetsCancelReleaseResponse202: ...
866
+
867
+ @overload
868
+ def cancel_release(
869
+ self: ReviewSetsResource[Literal[True]], review_set_id: int, session_id: int
870
+ ) -> ApiResponseProtocol[
871
+ ReviewSetsCancelReleaseResponse200 | ReviewSetsCancelReleaseResponse202
872
+ ]: ...
873
+
874
+ def cancel_release(
875
+ self, review_set_id: int, session_id: int
876
+ ) -> (
877
+ ReviewSetsCancelReleaseResponse200
878
+ | ReviewSetsCancelReleaseResponse202
879
+ | ApiResponseProtocol[
880
+ ReviewSetsCancelReleaseResponse200 | ReviewSetsCancelReleaseResponse202
881
+ ]
882
+ ):
883
+ require_positive_identifier("review_set_id", review_set_id)
884
+ require_positive_identifier("session_id", session_id)
885
+ response = self._request(
886
+ "ReviewSets_CancelRelease",
887
+ path_params={"reviewSetId": review_set_id, "sessionId": session_id},
888
+ )
889
+ return self._finish(response, response.data)