1- import base64
21import dataclasses
32import datetime
4- import json
53import logging
64import typing
75from typing import Any , Final , Optional
1210from . import backend_types_sql as bts
1311from . import component_structures as structures
1412from . import errors
15- from . import filter_query_models
13+ from . import filter_query_sql
1614
1715if typing .TYPE_CHECKING :
1816 from cloud_pipelines .orchestration .storage_providers import (
@@ -69,8 +67,6 @@ class ListPipelineJobsResponse:
6967
7068class PipelineRunsApiService_Sql :
7169 _PIPELINE_NAME_EXTRA_DATA_KEY = "pipeline_name"
72- _PAGE_TOKEN_OFFSET_KEY : Final [str ] = "offset"
73- _PAGE_TOKEN_FILTER_KEY : Final [str ] = "filter"
7470 _DEFAULT_PAGE_SIZE : Final [int ] = 10
7571
7672 def create (
@@ -173,22 +169,12 @@ def list(
173169 include_pipeline_names : bool = False ,
174170 include_execution_stats : bool = False ,
175171 ) -> ListPipelineJobsResponse :
176- if filter and filter_query :
177- raise errors .ApiValidationError (
178- "Cannot use both 'filter' and 'filter_query'. Use one or the other."
179- )
180-
181- if filter_query :
182- filter_query_models .FilterQuery .model_validate_json (filter_query )
183- raise NotImplementedError ("filter_query is not yet implemented." )
184-
185- filter_value , offset = _resolve_filter_value (
186- filter = filter ,
187- page_token = page_token ,
188- )
189- where_clauses , next_page_filter_value = _build_filter_where_clauses (
190- filter_value = filter_value ,
172+ where_clauses , offset , next_token = filter_query_sql .build_list_filters (
173+ filter_value = filter ,
174+ filter_query_value = filter_query ,
175+ page_token_value = page_token ,
191176 current_user = current_user ,
177+ page_size = self ._DEFAULT_PAGE_SIZE ,
192178 )
193179
194180 pipeline_runs = list (
@@ -200,14 +186,10 @@ def list(
200186 .limit (self ._DEFAULT_PAGE_SIZE )
201187 ).all ()
202188 )
203- next_page_offset = offset + self ._DEFAULT_PAGE_SIZE
204- next_page_token_dict = {
205- self ._PAGE_TOKEN_OFFSET_KEY : next_page_offset ,
206- self ._PAGE_TOKEN_FILTER_KEY : next_page_filter_value ,
207- }
208- next_page_token = _encode_page_token (next_page_token_dict )
209- if len (pipeline_runs ) < self ._DEFAULT_PAGE_SIZE :
210- next_page_token = None
189+
190+ next_page_token = (
191+ next_token if len (pipeline_runs ) >= self ._DEFAULT_PAGE_SIZE else None
192+ )
211193
212194 return ListPipelineJobsResponse (
213195 pipeline_runs = [
@@ -348,88 +330,6 @@ def delete_annotation(
348330 session .commit ()
349331
350332
351- def _resolve_filter_value (
352- * ,
353- filter : str | None ,
354- page_token : str | None ,
355- ) -> tuple [str | None , int ]:
356- """Decode page_token and return the effective (filter_value, offset).
357-
358- If a page_token is present, its stored filter takes precedence over the
359- raw filter parameter (the token carries the resolved filter forward across pages).
360- """
361- page_token_dict = _decode_page_token (page_token )
362- offset = page_token_dict .get (
363- PipelineRunsApiService_Sql ._PAGE_TOKEN_OFFSET_KEY ,
364- 0 ,
365- )
366- if page_token :
367- filter = page_token_dict .get (
368- PipelineRunsApiService_Sql ._PAGE_TOKEN_FILTER_KEY ,
369- None ,
370- )
371- return filter , offset
372-
373-
374- def _build_filter_where_clauses (
375- * ,
376- filter_value : str | None ,
377- current_user : str | None ,
378- ) -> tuple [list [sql .ColumnElement ], str | None ]:
379- """Parse a filter string into SQLAlchemy WHERE clauses.
380-
381- Returns (where_clauses, next_page_filter_value). The second value is the
382- filter string with shorthand values resolved (e.g. "created_by:me" becomes
383- "created_by:alice@example.com") so it can be embedded in the next page token.
384- """
385- where_clauses : list [sql .ColumnElement ] = []
386- parsed_filter = _parse_filter (filter_value ) if filter_value else {}
387- for key , value in parsed_filter .items ():
388- if key == "_text" :
389- raise NotImplementedError ("Text search is not implemented yet." )
390- elif key == "created_by" :
391- if value == "me" :
392- if current_user is None :
393- current_user = ""
394- value = current_user
395- # TODO: Maybe make this a bit more robust.
396- # We need to change the filter since it goes into the next_page_token.
397- filter_value = filter_value .replace (
398- "created_by:me" , f"created_by:{ current_user } "
399- )
400- if value :
401- where_clauses .append (bts .PipelineRun .created_by == value )
402- else :
403- where_clauses .append (bts .PipelineRun .created_by == None )
404- else :
405- raise NotImplementedError (f"Unsupported filter { filter_value } ." )
406- return where_clauses , filter_value
407-
408-
409- def _decode_page_token (page_token : str ) -> dict [str , Any ]:
410- return json .loads (base64 .b64decode (page_token )) if page_token else {}
411-
412-
413- def _encode_page_token (page_token_dict : dict [str , Any ]) -> str :
414- return (base64 .b64encode (json .dumps (page_token_dict ).encode ("utf8" ))).decode (
415- "utf-8"
416- )
417-
418-
419- def _parse_filter (filter : str ) -> dict [str , str ]:
420- # TODO: Improve
421- parts = filter .strip ().split ()
422- parsed_filter = {}
423- for part in parts :
424- key , sep , value = part .partition (":" )
425- if sep :
426- parsed_filter [key ] = value
427- else :
428- parsed_filter .setdefault ("_text" , "" )
429- parsed_filter ["_text" ] += part
430- return parsed_filter
431-
432-
433333# ========== ExecutionNodeApiService_Sql
434334
435335
0 commit comments