Skip to content

Commit 136e429

Browse files
wchaupietern
andauthored
Full update options for Delta Sharing shares, recipients, and providers (#530)
Co-authored-by: Pieter Noordhuis <[email protected]>
1 parent ef918f1 commit 136e429

File tree

4 files changed

+102
-35
lines changed

4 files changed

+102
-35
lines changed

databricks_cli/unity_catalog/api.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
# limitations under the License.
2323

2424
from databricks_cli.unity_catalog.uc_service import UnityCatalogService
25+
from databricks_cli.unity_catalog.utils import mc_pretty_format
2526

2627

2728
class UnityCatalogApi(object):
@@ -224,9 +225,20 @@ def list_providers(self):
224225
def get_provider(self, name):
225226
return self.client.get_provider(name)
226227

227-
def update_provider(self, name, new_name=None, comment=None, recipient_profile=None):
228-
return self.client.update_provider(name, new_name, comment, recipient_profile)
229-
228+
def update_provider(self, name, new_name=None, comment=None,
229+
recipient_profile=None, provider_spec=None):
230+
# If spec is provided, override all legacy parameters.
231+
if provider_spec is not None:
232+
return self.client.update_provider(name, provider_spec)
233+
_data = {}
234+
if new_name is not None:
235+
_data['name'] = new_name
236+
if recipient_profile is not None:
237+
_data['recipient_profile_str'] = mc_pretty_format(recipient_profile)
238+
if comment is not None:
239+
_data['comment'] = comment
240+
return self.client.update_provider(name, _data)
241+
230242
def delete_provider(self, name):
231243
return self.client.delete_provider(name)
232244

databricks_cli/unity_catalog/delta_sharing_cli.py

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
from databricks_cli.unity_catalog.api import UnityCatalogApi
2929
from databricks_cli.unity_catalog.utils import hide, json_file_help, json_string_help, \
3030
mc_pretty_format
31-
from databricks_cli.utils import eat_exceptions, CONTEXT_SETTINGS, json_cli_base
31+
from databricks_cli.utils import eat_exceptions, CONTEXT_SETTINGS, json_cli_base, \
32+
merge_dicts_shallow
3233

3334

3435
############## Share Commands ##############
@@ -127,6 +128,11 @@ def shared_data_object(name):
127128
short_help='Update a share.')
128129
@click.option('--name', required=True,
129130
help='Name of the share to update.')
131+
@click.option('--new-name', default=None, help='New name of the share.')
132+
@click.option('--comment', default=None, required=False,
133+
help='Free-form text description.')
134+
@click.option('--owner', default=None, required=False,
135+
help='Owner of the share.')
130136
@click.option('--add-table', default=None, multiple=True,
131137
metavar='NAME',
132138
help='Full name of table to add to share (can be specified multiple times).')
@@ -141,20 +147,24 @@ def shared_data_object(name):
141147
@profile_option
142148
@eat_exceptions
143149
@provide_api_client
144-
def update_share_cli(api_client, name, add_table, remove_table, json_file, json):
150+
def update_share_cli(api_client, name, new_name, comment, owner,
151+
add_table, remove_table, json_file, json):
145152
"""
146153
Update a share.
147154
148155
The public specification for the JSON request is in development.
149156
"""
150-
if len(add_table) > 0 or len(remove_table) > 0:
157+
if ((new_name is not None) or (comment is not None) or (owner is not None) or
158+
len(add_table) > 0 or len(remove_table) > 0):
159+
if (json_file is not None) or (json is not None):
160+
raise ValueError('Cannot specify JSON if any other update flags are specified')
151161
updates = []
152162
for a in add_table:
153163
updates.append({'action': 'ADD', 'data_object': shared_data_object(a)})
154164
for r in remove_table:
155165
updates.append({'action': 'REMOVE', 'data_object': shared_data_object(r)})
156-
d = {'updates': updates}
157-
share_json = UnityCatalogApi(api_client).update_share(name, d)
166+
data = {'name': new_name, 'comment': comment, 'owner': owner, 'updates': updates}
167+
share_json = UnityCatalogApi(api_client).update_share(name, data)
158168
click.echo(mc_pretty_format(share_json))
159169
else:
160170
json_cli_base(json_file, json,
@@ -185,10 +195,10 @@ def delete_share_cli(api_client, name):
185195
help='Free-form text description.')
186196
@click.option('--sharing-id', default=None, required=False,
187197
help='The sharing identifier provided by the data recipient offline.')
188-
@click.option('--allowed_ip_address', default=None, required=False, multiple=True,
198+
@click.option('--allowed-ip-address', default=None, required=False, multiple=True,
189199
help=(
190200
'IP address in CIDR notation that is allowed to use delta sharing. '
191-
'Supports multiple options.'))
201+
'(can be specified multiple times).'))
192202
@debug_option
193203
@profile_option
194204
@eat_exceptions
@@ -236,6 +246,16 @@ def get_recipient_cli(api_client, name):
236246
short_help='Update a recipient.')
237247
@click.option('--name', required=True,
238248
help='Name of the recipient who needs to be updated.')
249+
@click.option('--new-name', default=None, help='New name of the recipient.')
250+
@click.option('--comment', default=None, required=False,
251+
help='Free-form text description.')
252+
@click.option('--owner', default=None, required=False,
253+
help='Owner of the recipient.')
254+
@click.option('--allowed-ip-address', default=None, required=False, multiple=True,
255+
help=(
256+
'IP address in CIDR notation that is allowed to use delta sharing '
257+
'(can be specified multiple times). Specify a single empty string to disable '
258+
'IP allowlist.'))
239259
@click.option('--json-file', default=None, type=click.Path(),
240260
help=json_file_help(method='PATCH', path='/recipients/{name}'))
241261
@click.option('--json', default=None, type=JsonClickType(),
@@ -244,14 +264,27 @@ def get_recipient_cli(api_client, name):
244264
@profile_option
245265
@eat_exceptions
246266
@provide_api_client
247-
def update_recipient_cli(api_client, name, json_file, json):
267+
def update_recipient_cli(api_client, name, new_name, comment, owner,
268+
allowed_ip_address, json_file, json):
248269
"""
249270
Update a recipient.
250271
251272
The public specification for the JSON request is in development.
252273
"""
253-
json_cli_base(json_file, json,
254-
lambda json: UnityCatalogApi(api_client).update_recipient(name, json))
274+
if ((new_name is not None) or (comment is not None) or (owner is not None) or
275+
(allowed_ip_address is not None)):
276+
if (json_file is not None) or (json is not None):
277+
raise ValueError('Cannot specify JSON if any other update flags are specified')
278+
data = {'name': new_name, 'comment': comment, 'owner': owner}
279+
if len(allowed_ip_address) > 0:
280+
data['ip_access_list'] = {}
281+
if len(allowed_ip_address) != 1 or allowed_ip_address[0] != '':
282+
data['ip_access_list']['allowed_ip_addresses'] = allowed_ip_address
283+
recipient_json = UnityCatalogApi(api_client).update_recipient(name, data)
284+
click.echo(mc_pretty_format(recipient_json))
285+
else:
286+
json_cli_base(json_file, json,
287+
lambda json: UnityCatalogApi(api_client).update_recipient(name, json))
255288

256289

257290
@click.command(context_settings=CONTEXT_SETTINGS,
@@ -369,34 +402,53 @@ def get_provider_cli(api_client, name):
369402
@click.command(context_settings=CONTEXT_SETTINGS,
370403
short_help='Update a provider.')
371404
@click.option('--name', required=True, help='Name of the provider to update.')
372-
@click.option('--new_name', default=None, help='New name of the provider.')
405+
@click.option('--new-name', default=None, help='New name of the provider.')
373406
@click.option('--comment', default=None, required=False,
374407
help='Free-form text description.')
408+
@click.option('--owner', default=None, required=False,
409+
help='Owner of the provider.')
375410
@click.option('--recipient-profile-json-file', default=None, type=click.Path(),
376411
help='File containing recipient profile in JSON format.')
377412
@click.option('--recipient-profile-json', default=None, type=JsonClickType(),
378413
help='JSON string containing recipient profile.')
414+
@click.option('--json-file', default=None, type=click.Path(),
415+
help=json_file_help(method='PATCH', path='/providers/{name}'))
416+
@click.option('--json', default=None, type=JsonClickType(),
417+
help=json_string_help(method='PATCH', path='/providers/{name}'))
379418
@debug_option
380419
@profile_option
381420
@eat_exceptions
382421
@provide_api_client
383-
def update_provider_cli(api_client, name, new_name, comment, recipient_profile_json_file,
384-
recipient_profile_json):
422+
def update_provider_cli(api_client, name, new_name, comment, owner, recipient_profile_json_file,
423+
recipient_profile_json, json_file, json):
385424
"""
386425
Update a provider.
387426
388427
The public specification for the JSON request is in development.
389428
"""
390-
if recipient_profile_json is None and recipient_profile_json_file is None:
391-
updated_provider = UnityCatalogApi(api_client).update_provider(name, new_name, comment)
392-
click.echo(mc_pretty_format(updated_provider))
429+
if ((new_name is not None) or (comment is not None) or (owner is not None) or
430+
(recipient_profile_json_file is not None) or (recipient_profile_json) is not None):
431+
if (json_file is not None) or (json is not None):
432+
raise ValueError('Cannot specify JSON if any other update flags are specified')
433+
data = {'name': new_name, 'comment': comment, 'owner': owner}
434+
435+
if (recipient_profile_json_file is None) and (recipient_profile_json is None):
436+
provider_json = UnityCatalogApi(api_client).update_provider(name, provider_spec=data)
437+
click.echo(mc_pretty_format(provider_json))
438+
else:
439+
json_cli_base(recipient_profile_json_file, recipient_profile_json,
440+
lambda profile_json: UnityCatalogApi(api_client).update_provider(
441+
name,
442+
provider_spec=merge_dicts_shallow(
443+
data,
444+
{'recipient_profile_str': mc_pretty_format(profile_json)})),
445+
error_msg='Either --recipient-profile-json-file or ' +
446+
'--recipient-profile-json should be provided')
393447
else:
394-
json_cli_base(recipient_profile_json_file, recipient_profile_json,
395-
lambda json: UnityCatalogApi(api_client).update_provider(name, new_name,
396-
comment, json),
397-
error_msg='Either --recipient-profile-json-file or ' +
398-
'--recipient-profile-json should be provided')
399-
448+
json_cli_base(json_file, json,
449+
lambda json: UnityCatalogApi(api_client).update_provider(
450+
name, provider_spec=json))
451+
400452

401453
@click.command(context_settings=CONTEXT_SETTINGS,
402454
short_help='List shares of a provider.')

databricks_cli/unity_catalog/uc_service.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -457,17 +457,9 @@ def get_provider(self, name, headers=None):
457457
return self.client.perform_query('GET', '/unity-catalog/providers/%s' % (name),
458458
data={}, headers=headers)
459459

460-
def update_provider(self, name, new_name, comment, recipient_profile, headers=None):
461-
_data = {}
462-
if new_name is not None:
463-
_data['name'] = new_name
464-
if recipient_profile is not None:
465-
_data['recipient_profile_str'] = mc_pretty_format(recipient_profile)
466-
if comment is not None:
467-
_data['comment'] = comment
468-
460+
def update_provider(self, name, provider_spec, headers=None):
469461
return self.client.perform_query('PATCH', '/unity-catalog/providers/%s' % (name),
470-
data=_data, headers=headers)
462+
data=provider_spec, headers=headers)
471463

472464
def delete_provider(self, name, headers=None):
473465
return self.client.perform_query('DELETE', '/unity-catalog/providers/%s' % (name),

databricks_cli/utils.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,14 @@ def for_profile(profile):
161161
'Please configure by entering '
162162
'`{argv} configure --profile {profile}`').format(
163163
profile=profile, argv=sys.argv[0]))
164+
165+
166+
def merge_dicts_shallow(*dicts):
167+
"""
168+
Merges dicts through shallow copy.
169+
"""
170+
result = {}
171+
for d in dicts:
172+
result.update(d)
173+
return result
174+

0 commit comments

Comments
 (0)