Skip to content

Commit 7ec911d

Browse files
feat(boxsdkgen): Support query APIs (box/box-openapi#610) (#1528)
1 parent 0427df0 commit 7ec911d

21 files changed

Lines changed: 852 additions & 5 deletions

‎.codegen.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "engineHash": "7fa5d4a", "specHash": "131c54a", "version": "4.13.0" }
1+
{ "engineHash": "2cee311", "specHash": "cc375ad", "version": "4.13.0" }

‎box_sdk_gen/client.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@
196196

197197
from box_sdk_gen.managers.notes import NotesManager
198198

199+
from box_sdk_gen.managers.query import QueryManager
200+
199201
from box_sdk_gen.networking.auth import Authentication
200202

201203
from box_sdk_gen.networking.network import NetworkSession
@@ -472,6 +474,7 @@ def __init__(self, auth: Authentication, *, network_session: NetworkSession = No
472474
auth=self.auth, network_session=self.network_session
473475
)
474476
self.notes = NotesManager(auth=self.auth, network_session=self.network_session)
477+
self.query = QueryManager(auth=self.auth, network_session=self.network_session)
475478

476479
def make_request(self, fetch_options: FetchOptions) -> FetchResponse:
477480
"""

‎box_sdk_gen/managers/__init__.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,5 @@
167167
from box_sdk_gen.managers.automate_workflows import *
168168

169169
from box_sdk_gen.managers.notes import *
170+
171+
from box_sdk_gen.managers.query import *

‎box_sdk_gen/managers/query.py‎

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
from typing import Optional
2+
3+
from typing import Dict
4+
5+
from typing import List
6+
7+
from box_sdk_gen.internal.base_object import BaseObject
8+
9+
from box_sdk_gen.internal.utils import to_string
10+
11+
from box_sdk_gen.serialization.json import serialize
12+
13+
from box_sdk_gen.serialization.json import deserialize
14+
15+
from box_sdk_gen.schemas.v2026_r0.query_ancestor_reference_v2026_r0 import (
16+
QueryAncestorReferenceV2026R0,
17+
)
18+
19+
from box_sdk_gen.schemas.v2026_r0.query_insights_group_by_v2026_r0 import (
20+
QueryInsightsGroupByV2026R0,
21+
)
22+
23+
from box_sdk_gen.schemas.v2026_r0.query_order_by_v2026_r0 import QueryOrderByV2026R0
24+
25+
from box_sdk_gen.networking.fetch_options import ResponseFormat
26+
27+
from box_sdk_gen.schemas.v2026_r0.query_insights_metric_definition_v2026_r0 import (
28+
QueryInsightsMetricDefinitionV2026R0,
29+
)
30+
31+
from box_sdk_gen.schemas.v2026_r0.query_results_v2026_r0 import QueryResultsV2026R0
32+
33+
from box_sdk_gen.schemas.v2026_r0.client_error_v2026_r0 import ClientErrorV2026R0
34+
35+
from box_sdk_gen.parameters.v2026_r0.box_version_header_v2026_r0 import (
36+
BoxVersionHeaderV2026R0,
37+
)
38+
39+
from box_sdk_gen.schemas.v2026_r0.query_request_body_v2026_r0 import (
40+
QueryRequestBodyV2026R0,
41+
)
42+
43+
from box_sdk_gen.schemas.v2026_r0.query_insights_v2026_r0 import QueryInsightsV2026R0
44+
45+
from box_sdk_gen.schemas.v2026_r0.query_insights_request_body_v2026_r0 import (
46+
QueryInsightsRequestBodyV2026R0,
47+
)
48+
49+
from box_sdk_gen.box.errors import BoxSDKError
50+
51+
from box_sdk_gen.networking.auth import Authentication
52+
53+
from box_sdk_gen.networking.network import NetworkSession
54+
55+
from box_sdk_gen.networking.fetch_options import FetchOptions
56+
57+
from box_sdk_gen.networking.fetch_response import FetchResponse
58+
59+
from box_sdk_gen.internal.utils import prepare_params
60+
61+
from box_sdk_gen.internal.utils import to_string
62+
63+
from box_sdk_gen.internal.utils import ByteStream
64+
65+
from box_sdk_gen.serialization.json import sd_to_json
66+
67+
from box_sdk_gen.serialization.json import SerializedData
68+
69+
70+
class CreateQueryV2026R0Query(BaseObject):
71+
def __init__(
72+
self,
73+
predicate: str,
74+
*,
75+
params: Optional[Dict] = None,
76+
ancestors: Optional[List[QueryAncestorReferenceV2026R0]] = None,
77+
**kwargs
78+
):
79+
"""
80+
:param predicate: A logical expression used to filter the dataset, similar to an SQL
81+
`WHERE` clause. May include named parameters referenced as
82+
`:placeholder`.
83+
:type predicate: str
84+
:param params: A map of placeholder names (without the `:` prefix) to their values.
85+
Required only when the predicate contains parameter placeholders. The
86+
type of each value must match the type of the field it is compared to., defaults to None
87+
:type params: Optional[Dict], optional
88+
:param ancestors: Restricts results to the specified ancestor entities and their
89+
recursive descendants. The user must have read access to every listed
90+
ancestor., defaults to None
91+
:type ancestors: Optional[List[QueryAncestorReferenceV2026R0]], optional
92+
"""
93+
super().__init__(**kwargs)
94+
self.predicate = predicate
95+
self.params = params
96+
self.ancestors = ancestors
97+
98+
99+
class CreateQueryInsightV2026R0Query(BaseObject):
100+
def __init__(
101+
self,
102+
predicate: str,
103+
*,
104+
params: Optional[Dict] = None,
105+
ancestors: Optional[List[QueryAncestorReferenceV2026R0]] = None,
106+
group_by: Optional[List[QueryInsightsGroupByV2026R0]] = None,
107+
**kwargs
108+
):
109+
"""
110+
:param predicate: A logical expression used to filter the dataset prior to metric
111+
computation, similar to an SQL `WHERE` clause. May include
112+
named parameters referenced as `:placeholder`.
113+
:type predicate: str
114+
:param params: A map of placeholder names (without the `:` prefix) to their values.
115+
Required only when the predicate contains parameter placeholders. The
116+
type of each value must match the type of the field it is compared to., defaults to None
117+
:type params: Optional[Dict], optional
118+
:param ancestors: Restricts results to items contained within any of the specified
119+
ancestors. The user must have access to every listed ancestor. When
120+
omitted, insights are computed across all accessible items., defaults to None
121+
:type ancestors: Optional[List[QueryAncestorReferenceV2026R0]], optional
122+
:param group_by: Defines how data is grouped for insights computation. Currently only a
123+
single grouping field is supported., defaults to None
124+
:type group_by: Optional[List[QueryInsightsGroupByV2026R0]], optional
125+
"""
126+
super().__init__(**kwargs)
127+
self.predicate = predicate
128+
self.params = params
129+
self.ancestors = ancestors
130+
self.group_by = group_by
131+
132+
133+
class QueryManager:
134+
def __init__(
135+
self,
136+
*,
137+
auth: Optional[Authentication] = None,
138+
network_session: NetworkSession = None
139+
):
140+
if network_session is None:
141+
network_session = NetworkSession()
142+
self.auth = auth
143+
self.network_session = network_session
144+
145+
def create_query_v2026_r0(
146+
self,
147+
query: CreateQueryV2026R0Query,
148+
*,
149+
order_by: Optional[List[QueryOrderByV2026R0]] = None,
150+
limit: Optional[int] = None,
151+
fields: Optional[List[str]] = None,
152+
marker: Optional[str] = None,
153+
box_version: BoxVersionHeaderV2026R0 = BoxVersionHeaderV2026R0._2026_0,
154+
extra_headers: Optional[Dict[str, Optional[str]]] = None
155+
) -> QueryResultsV2026R0:
156+
"""
157+
Runs a query to discover Box items using a logical predicate that can filter
158+
159+
across item fields and metadata templates. Results can be sorted, paginated,
160+
161+
162+
and shaped to include additional item or metadata fields.
163+
164+
:param query: The query definition, including the filtering predicate and its optional
165+
parameters and ancestor restrictions.
166+
:type query: CreateQueryV2026R0Query
167+
:param order_by: The sorting criteria for the result set. Entries are applied sequentially
168+
to define multi-level sorting., defaults to None
169+
:type order_by: Optional[List[QueryOrderByV2026R0]], optional
170+
:param limit: The maximum number of results to return. Defaults to `50` when not
171+
provided., defaults to None
172+
:type limit: Optional[int], optional
173+
:param fields: Controls which additional fields are included in each result entry. Each
174+
value must be one of: a fully qualified item field key (for example
175+
`box:item:name`), a metadata template key to hydrate the full template (for
176+
example `enterprise_12345678:project`), or a specific metadata template
177+
field key to hydrate a single field from the template (for example
178+
`enterprise_12345678:project:name`). When omitted, entries include only the
179+
item type and identifier., defaults to None
180+
:type fields: Optional[List[str]], optional
181+
:param marker: An opaque token returned from a previous response, used to continue
182+
retrieval. When provided, all other request parameters must exactly match
183+
those of the original request., defaults to None
184+
:type marker: Optional[str], optional
185+
:param box_version: Version header., defaults to BoxVersionHeaderV2026R0._2026_0
186+
:type box_version: BoxVersionHeaderV2026R0, optional
187+
:param extra_headers: Extra headers that will be included in the HTTP request., defaults to None
188+
:type extra_headers: Optional[Dict[str, Optional[str]]], optional
189+
"""
190+
if extra_headers is None:
191+
extra_headers = {}
192+
request_body: Dict = {
193+
'query': query,
194+
'order_by': order_by,
195+
'limit': limit,
196+
'fields': fields,
197+
'marker': marker,
198+
}
199+
headers_map: Dict[str, str] = prepare_params(
200+
{'box-version': to_string(box_version), **extra_headers}
201+
)
202+
response: FetchResponse = self.network_session.network_client.fetch(
203+
FetchOptions(
204+
url=''.join([self.network_session.base_urls.base_url, '/2.0/query']),
205+
method='POST',
206+
headers=headers_map,
207+
data=serialize(request_body),
208+
content_type='application/json',
209+
response_format=ResponseFormat.JSON,
210+
auth=self.auth,
211+
network_session=self.network_session,
212+
)
213+
)
214+
return deserialize(response.data, QueryResultsV2026R0)
215+
216+
def create_query_insight_v2026_r0(
217+
self,
218+
query: CreateQueryInsightV2026R0Query,
219+
metrics: Dict[str, QueryInsightsMetricDefinitionV2026R0],
220+
*,
221+
box_version: BoxVersionHeaderV2026R0 = BoxVersionHeaderV2026R0._2026_0,
222+
extra_headers: Optional[Dict[str, Optional[str]]] = None
223+
) -> QueryInsightsV2026R0:
224+
"""
225+
Computes aggregated metrics over Box items matching a query predicate.
226+
227+
Filters are applied first, followed by optional grouping, after which the
228+
229+
230+
requested metrics (such as `sum`, `avg`, `min`, `max`, and `count`) are
231+
232+
233+
computed for each resulting group or over the entire filtered dataset.
234+
235+
:param query: The filtering and grouping definition. Filters are applied first, followed
236+
by grouping, before metrics are computed.
237+
:type query: CreateQueryInsightV2026R0Query
238+
:param metrics: A map of user-defined metric aliases to their definitions. A maximum of 10
239+
metrics may be defined. Each alias must be a unique, non-empty string of up
240+
to 256 characters, containing only letters, digits, `_`, `-`, or `.`, and
241+
must not start with a digit, `_`, `-`, or `.`. May be empty to request
242+
only a total count.
243+
:type metrics: Dict[str, QueryInsightsMetricDefinitionV2026R0]
244+
:param box_version: Version header., defaults to BoxVersionHeaderV2026R0._2026_0
245+
:type box_version: BoxVersionHeaderV2026R0, optional
246+
:param extra_headers: Extra headers that will be included in the HTTP request., defaults to None
247+
:type extra_headers: Optional[Dict[str, Optional[str]]], optional
248+
"""
249+
if extra_headers is None:
250+
extra_headers = {}
251+
request_body: Dict = {'query': query, 'metrics': metrics}
252+
headers_map: Dict[str, str] = prepare_params(
253+
{'box-version': to_string(box_version), **extra_headers}
254+
)
255+
response: FetchResponse = self.network_session.network_client.fetch(
256+
FetchOptions(
257+
url=''.join(
258+
[self.network_session.base_urls.base_url, '/2.0/query_insights']
259+
),
260+
method='POST',
261+
headers=headers_map,
262+
data=serialize(request_body),
263+
content_type='application/json',
264+
response_format=ResponseFormat.JSON,
265+
auth=self.auth,
266+
network_session=self.network_session,
267+
)
268+
)
269+
return deserialize(response.data, QueryInsightsV2026R0)

‎box_sdk_gen/schemas/file_full.py‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
from box_sdk_gen.schemas.metadata_full import MetadataFull
3232

33+
from box_sdk_gen.schemas.collection import Collection
34+
3335
from box_sdk_gen.box.errors import BoxSDKError
3436

3537
from box_sdk_gen.internal.utils import DateTime
@@ -413,6 +415,12 @@ class FileFullSharedLinkPermissionOptionsField(str, Enum):
413415
CAN_EDIT = 'can_edit'
414416

415417

418+
class FileFullAllowedSharedLinkAccessLevelsField(str, Enum):
419+
OPEN = 'open'
420+
COMPANY = 'company'
421+
COLLABORATORS = 'collaborators'
422+
423+
416424
class FileFull(File):
417425
_discriminator = 'type', {'file'}
418426

@@ -443,6 +451,13 @@ def __init__(
443451
List[FileFullSharedLinkPermissionOptionsField]
444452
] = None,
445453
is_associated_with_app_item: Optional[bool] = None,
454+
collections: Optional[List[Collection]] = None,
455+
is_download_available: Optional[bool] = None,
456+
download_url: Optional[str] = None,
457+
authenticated_download_url: Optional[str] = None,
458+
allowed_shared_link_access_levels: Optional[
459+
List[FileFullAllowedSharedLinkAccessLevelsField]
460+
] = None,
446461
description: Optional[str] = None,
447462
size: Optional[int] = None,
448463
path_collection: Optional[FilePathCollectionField] = None,
@@ -509,6 +524,36 @@ def __init__(
509524
true even if the context user does not have access to the app item(s)
510525
associated with the file., defaults to None
511526
:type is_associated_with_app_item: Optional[bool], optional
527+
:param collections: The collections that this file belongs to.
528+
529+
For more information, see the
530+
[collections guide](https://developer.box.com/guides/collections)., defaults to None
531+
:type collections: Optional[List[Collection]], optional
532+
:param is_download_available: Whether the file's binary content is eligible to be downloaded.
533+
534+
This is a content-level flag and does not reflect whether the
535+
current user is authorized to download the file. Use
536+
`permissions.can_download`, when available, for that., defaults to None
537+
:type is_download_available: Optional[bool], optional
538+
:param download_url: A pre-authorized, expiring URL for directly downloading the file's
539+
content. Requires authentication and is valid only for the current
540+
session.
541+
542+
This field is only returned for files, not folders or web links., defaults to None
543+
:type download_url: Optional[str], optional
544+
:param authenticated_download_url: A stable API URL for the file content endpoint,
545+
`/2.0/files/{id}/content`. Unlike `download_url`, authorization is
546+
evaluated when the URL is requested with a valid access token.
547+
548+
This field is only returned for files, not folders or web links., defaults to None
549+
:type authenticated_download_url: Optional[str], optional
550+
:param allowed_shared_link_access_levels: The shared link access levels the authenticated user is allowed to
551+
use when creating or updating a shared link for this file.
552+
553+
The list depends on item policy and user authorization, so it may be
554+
narrower than the levels available to the owner. An empty array means
555+
no access level is available to this user., defaults to None
556+
:type allowed_shared_link_access_levels: Optional[List[FileFullAllowedSharedLinkAccessLevelsField]], optional
512557
:param description: The optional description of this file.
513558
If the description exceeds 255 characters, the first 255 characters
514559
are set as a file description and the rest of it is ignored., defaults to None
@@ -595,3 +640,8 @@ def __init__(
595640
self.disposition_at = disposition_at
596641
self.shared_link_permission_options = shared_link_permission_options
597642
self.is_associated_with_app_item = is_associated_with_app_item
643+
self.collections = collections
644+
self.is_download_available = is_download_available
645+
self.download_url = download_url
646+
self.authenticated_download_url = authenticated_download_url
647+
self.allowed_shared_link_access_levels = allowed_shared_link_access_levels

0 commit comments

Comments
 (0)