|
1 | 1 | from datetime import datetime
|
2 |
| -from typing import List, Mapping, Optional |
| 2 | +from typing import Dict, List, Mapping, Optional, Union |
3 | 3 |
|
4 | 4 | from ariadne import ObjectType, convert_kwargs_to_snake_case
|
5 | 5 | from graphql import GraphQLResolveInfo
|
6 | 6 |
|
| 7 | +from codecov.commands.exceptions import ValidationError |
7 | 8 | from codecov.db import sync_to_async
|
8 |
| -from graphql_api.types.enums import OrderingDirection |
| 9 | +from graphql_api.types.enums import AssetOrdering, OrderingDirection |
9 | 10 | from services.bundle_analysis import (
|
10 | 11 | AssetReport,
|
11 | 12 | BundleAnalysisMeasurementData,
|
|
25 | 26 | bundle_report_bindable = ObjectType("BundleReport")
|
26 | 27 |
|
27 | 28 |
|
| 29 | +def _find_index_by_cursor(assets: List, cursor: str) -> int: |
| 30 | + try: |
| 31 | + for i, asset in enumerate(assets): |
| 32 | + if asset.id == int(cursor): |
| 33 | + return i |
| 34 | + except ValueError: |
| 35 | + pass |
| 36 | + return -1 |
| 37 | + |
| 38 | + |
28 | 39 | # ============= Bundle Data Bindable =============
|
29 | 40 |
|
30 | 41 |
|
@@ -135,6 +146,70 @@ def resolve_assets(
|
135 | 146 | return list(bundle_report.assets())
|
136 | 147 |
|
137 | 148 |
|
| 149 | +@bundle_report_bindable.field("assetsPaginated") |
| 150 | +def resolve_assets_paginated( |
| 151 | + bundle_report: BundleReport, |
| 152 | + info: GraphQLResolveInfo, |
| 153 | + ordering: AssetOrdering = AssetOrdering.SIZE, |
| 154 | + ordering_direction: OrderingDirection = OrderingDirection.DESC, |
| 155 | + first: Optional[int] = None, |
| 156 | + after: Optional[str] = None, |
| 157 | + last: Optional[int] = None, |
| 158 | + before: Optional[str] = None, |
| 159 | +) -> Union[Dict[str, object], ValidationError]: |
| 160 | + if first is not None and last is not None: |
| 161 | + return ValidationError("First and last can not be used at the same time") |
| 162 | + if after is not None and before is not None: |
| 163 | + return ValidationError("After and before can not be used at the same time") |
| 164 | + |
| 165 | + # All filtered assets before pagination |
| 166 | + assets = list( |
| 167 | + bundle_report.assets( |
| 168 | + ordering=ordering.value, |
| 169 | + ordering_desc=ordering_direction.value == OrderingDirection.DESC.value, |
| 170 | + ) |
| 171 | + ) |
| 172 | + |
| 173 | + total_count, has_next_page, has_previous_page = len(assets), False, False |
| 174 | + start_cursor, end_cursor = None, None |
| 175 | + |
| 176 | + # Apply cursors to edges |
| 177 | + if after is not None: |
| 178 | + after_edge = _find_index_by_cursor(assets, after) |
| 179 | + if after_edge > -1: |
| 180 | + assets = assets[after_edge + 1 :] |
| 181 | + |
| 182 | + if before is not None: |
| 183 | + before_edge = _find_index_by_cursor(assets, before) |
| 184 | + if before_edge > -1: |
| 185 | + assets = assets[:before_edge] |
| 186 | + |
| 187 | + # Slice edges by return size |
| 188 | + if first is not None and first >= 0: |
| 189 | + if len(assets) > first: |
| 190 | + assets = assets[:first] |
| 191 | + has_next_page = True |
| 192 | + |
| 193 | + if last is not None and last >= 0: |
| 194 | + if len(assets) > last: |
| 195 | + assets = assets[len(assets) - last :] |
| 196 | + has_previous_page = True |
| 197 | + |
| 198 | + if assets: |
| 199 | + start_cursor, end_cursor = assets[0].id, assets[-1].id |
| 200 | + |
| 201 | + return { |
| 202 | + "edges": [{"cursor": asset.id, "node": asset} for asset in assets], |
| 203 | + "total_count": total_count, |
| 204 | + "page_info": { |
| 205 | + "has_next_page": has_next_page, |
| 206 | + "has_previous_page": has_previous_page, |
| 207 | + "start_cursor": start_cursor, |
| 208 | + "end_cursor": end_cursor, |
| 209 | + }, |
| 210 | + } |
| 211 | + |
| 212 | + |
138 | 213 | @bundle_report_bindable.field("asset")
|
139 | 214 | def resolve_asset(
|
140 | 215 | bundle_report: BundleReport, info: GraphQLResolveInfo, name: str
|
|
0 commit comments