Skip to content

Commit 42c1608

Browse files
authored
Repos API support (#380)
* initial changes * removed file * removed file * newline * api support * newlines * lint * lint * newline * tests * comments * test
1 parent 21258ee commit 42c1608

9 files changed

Lines changed: 528 additions & 0 deletions

File tree

databricks_cli/cli.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from databricks_cli.tokens.cli import tokens_group
4141
from databricks_cli.instance_pools.cli import instance_pools_group
4242
from databricks_cli.pipelines.cli import pipelines_group
43+
from databricks_cli.repos.cli import repos_group
4344

4445

4546
@click.group(context_settings=CONTEXT_SETTINGS)
@@ -65,6 +66,7 @@ def cli():
6566
cli.add_command(tokens_group, name='tokens')
6667
cli.add_command(instance_pools_group, name="instance-pools")
6768
cli.add_command(pipelines_group, name='pipelines')
69+
cli.add_command(repos_group, name='repos')
6870

6971
if __name__ == "__main__":
7072
cli()

databricks_cli/repos/__init__.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Databricks CLI
2+
# Copyright 2017 Databricks, Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"), except
5+
# that the use of services to which certain application programming
6+
# interfaces (each, an "API") connect requires that the user first obtain
7+
# a license for the use of the APIs from Databricks, Inc. ("Databricks"),
8+
# by creating an account at www.databricks.com and agreeing to either (a)
9+
# the Community Edition Terms of Service, (b) the Databricks Terms of
10+
# Service, or (c) another written agreement between Licensee and Databricks
11+
# for the use of the APIs.
12+
#
13+
# You may not use this file except in compliance with the License.
14+
# You may obtain a copy of the License at
15+
#
16+
# http://www.apache.org/licenses/LICENSE-2.0
17+
#
18+
# Unless required by applicable law or agreed to in writing, software
19+
# distributed under the License is distributed on an "AS IS" BASIS,
20+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21+
# See the License for the specific language governing permissions and
22+
# limitations under the License.

databricks_cli/repos/api.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Databricks CLI
2+
# Copyright 2017 Databricks, Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"), except
5+
# that the use of services to which certain application programming
6+
# interfaces (each, an "API") connect requires that the user first obtain
7+
# a license for the use of the APIs from Databricks, Inc. ("Databricks"),
8+
# by creating an account at www.databricks.com and agreeing to either (a)
9+
# the Community Edition Terms of Service, (b) the Databricks Terms of
10+
# Service, or (c) another written agreement between Licensee and Databricks
11+
# for the use of the APIs.
12+
#
13+
# You may not use this file except in compliance with the License.
14+
# You may obtain a copy of the License at
15+
#
16+
# http://www.apache.org/licenses/LICENSE-2.0
17+
#
18+
# Unless required by applicable law or agreed to in writing, software
19+
# distributed under the License is distributed on an "AS IS" BASIS,
20+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21+
# See the License for the specific language governing permissions and
22+
# limitations under the License.
23+
import requests
24+
from databricks_cli.sdk import ReposService, WorkspaceService
25+
26+
27+
class ReposApi(object):
28+
def __init__(self, api_client):
29+
self.client = ReposService(api_client)
30+
self.ws_client = WorkspaceService(api_client)
31+
32+
def get_repo_id(self, path):
33+
if not path.startswith("/Repos/"):
34+
raise ValueError("Path must start with /Repos/ !")
35+
36+
if not len([x for x in path.split("/") if x]) == 3:
37+
raise ValueError("Repos paths must be in /Repos/{folder}/{repo} format!")
38+
39+
try:
40+
status = self.ws_client.get_status(path)
41+
if status['object_type'] == 'REPO':
42+
return status['object_id']
43+
except requests.exceptions.HTTPError:
44+
pass
45+
46+
raise RuntimeError("Can't find repo ID for {path}".format(path=path))
47+
48+
def list(self, path_prefix, next_page_token):
49+
"""
50+
List repos that the caller has Manage permissions on. Results are
51+
paginated with each page containing twenty repos.
52+
"""
53+
return self.client.list_repos(path_prefix, next_page_token)
54+
55+
def create(self, url, provider, path):
56+
"""
57+
Creates a repo object and links it to the remote Git repo specified.
58+
"""
59+
return self.client.create_repo(url, provider, path)
60+
61+
def get(self, repo_id):
62+
"""
63+
Gets the repo with the given ID.
64+
"""
65+
return self.client.get_repo(repo_id)
66+
67+
def update(self, repo_id, branch, tag):
68+
"""
69+
Checks out the repo to the given branch or tag. Only one of ``branch``
70+
or ``tag`` should be provided.
71+
"""
72+
assert bool(branch is not None) ^ bool(tag is not None)
73+
return self.client.update_repo(repo_id, branch, tag)
74+
75+
def delete(self, repo_id):
76+
"""
77+
Deletes the repo with the given ID.
78+
"""
79+
return self.client.delete_repo(repo_id)

databricks_cli/repos/cli.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Databricks CLI
2+
# Copyright 2017 Databricks, Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"), except
5+
# that the use of services to which certain application programming
6+
# interfaces (each, an "API") connect requires that the user first obtain
7+
# a license for the use of the APIs from Databricks, Inc. ("Databricks"),
8+
# by creating an account at www.databricks.com and agreeing to either (a)
9+
# the Community Edition Terms of Service, (b) the Databricks Terms of
10+
# Service, or (c) another written agreement between Licensee and Databricks
11+
# for the use of the APIs.
12+
#
13+
# You may not use this file except in compliance with the License.
14+
# You may obtain a copy of the License at
15+
#
16+
# http://www.apache.org/licenses/LICENSE-2.0
17+
#
18+
# Unless required by applicable law or agreed to in writing, software
19+
# distributed under the License is distributed on an "AS IS" BASIS,
20+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21+
# See the License for the specific language governing permissions and
22+
# limitations under the License.
23+
24+
import click
25+
26+
from databricks_cli.click_types import OneOfOption
27+
from databricks_cli.configure.config import provide_api_client, profile_option, debug_option
28+
from databricks_cli.repos.api import ReposApi
29+
from databricks_cli.utils import CONTEXT_SETTINGS, eat_exceptions, pretty_format
30+
from databricks_cli.version import print_version_callback, version
31+
32+
UPDATE_OPTIONS = ['branch', 'tag']
33+
ID_OPTIONS = ['repo-id', 'path']
34+
35+
36+
@click.command(context_settings=CONTEXT_SETTINGS,
37+
short_help='List repos that user has Manage permissions on')
38+
@click.option('--path-prefix', help="Path prefix to filter results by")
39+
@click.option('--next-page-token', help="Token used to fetch the next page of results")
40+
@debug_option
41+
@profile_option
42+
@eat_exceptions # noqa
43+
@provide_api_client
44+
def list_repos_cli(api_client, path_prefix, next_page_token):
45+
"""
46+
List repos that the user has Manage permissions on.
47+
"""
48+
content = ReposApi(api_client).list(path_prefix, next_page_token)
49+
click.echo(pretty_format(content))
50+
51+
52+
@click.command(context_settings=CONTEXT_SETTINGS,
53+
short_help='Create a repo and link it to the given remote Git repo')
54+
@click.option('--url', required=True, help="URL of the remote Git repo")
55+
@click.option('--provider', required=True, help="Git provider (case insensitive)")
56+
@click.option('--path', help="Desired workspace path of the repo object")
57+
@debug_option
58+
@profile_option
59+
@eat_exceptions # noqa
60+
@provide_api_client
61+
def create_repo_cli(api_client, url, provider, path):
62+
"""
63+
Creates a repo object and links it to the remote Git repo specified.
64+
"""
65+
content = ReposApi(api_client).create(url, provider, path)
66+
click.echo(pretty_format(content))
67+
68+
69+
@click.command(context_settings=CONTEXT_SETTINGS,
70+
short_help='Get repo based on ID or path')
71+
@click.option('--repo-id', cls=OneOfOption, default=None, one_of=ID_OPTIONS, help="Repo ID")
72+
@click.option('--path', cls=OneOfOption, default=None, one_of=ID_OPTIONS,
73+
help="Workspace path of the repo object")
74+
@debug_option
75+
@profile_option
76+
@eat_exceptions # noqa
77+
@provide_api_client
78+
def get_repo_cli(api_client, repo_id, path):
79+
"""
80+
Gets the repo.
81+
"""
82+
id_from_param_or_path = (repo_id if repo_id is not None
83+
else ReposApi(api_client).get_repo_id(path))
84+
content = ReposApi(api_client).get(id_from_param_or_path)
85+
click.echo(pretty_format(content))
86+
87+
88+
@click.command(context_settings=CONTEXT_SETTINGS,
89+
short_help='Checkout the repo to the given branch or tag')
90+
@click.option('--repo-id', cls=OneOfOption, default=None, one_of=ID_OPTIONS, help="Repo ID")
91+
@click.option('--path', cls=OneOfOption, default=None, one_of=ID_OPTIONS,
92+
help="Workspace path of the repo object")
93+
@click.option('--branch', cls=OneOfOption, default=None, one_of=UPDATE_OPTIONS, help="Branch name")
94+
@click.option('--tag', cls=OneOfOption, default=None, one_of=UPDATE_OPTIONS, help="Tag name")
95+
@debug_option
96+
@profile_option
97+
@eat_exceptions # noqa
98+
@provide_api_client
99+
def update_repo_cli(api_client, repo_id, branch, tag, path):
100+
"""
101+
Checks out the repo to the given branch or tag. This call returns an error if the branch
102+
or tag doesn't exist.
103+
"""
104+
id_from_param_or_path = (repo_id if repo_id is not None
105+
else ReposApi(api_client).get_repo_id(path))
106+
content = ReposApi(api_client).update(id_from_param_or_path, branch, tag)
107+
click.echo(pretty_format(content))
108+
109+
110+
@click.command(context_settings=CONTEXT_SETTINGS,
111+
short_help='Delete the repo based on ID or path')
112+
@click.option('--repo-id', cls=OneOfOption, default=None, one_of=ID_OPTIONS, help="Repo ID")
113+
@click.option('--path', cls=OneOfOption, default=None, one_of=ID_OPTIONS,
114+
help="Workspace path of the repo object")
115+
@debug_option
116+
@profile_option
117+
@eat_exceptions # noqa
118+
@provide_api_client
119+
def delete_repo_cli(api_client, repo_id, path):
120+
"""
121+
Deletes the repo.
122+
"""
123+
id_from_param_or_path = (repo_id if repo_id is not None
124+
else ReposApi(api_client).get_repo_id(path))
125+
content = ReposApi(api_client).delete(id_from_param_or_path)
126+
click.echo(pretty_format(content))
127+
128+
129+
@click.group(context_settings=CONTEXT_SETTINGS,
130+
short_help="Utility to interact with Repos.")
131+
@click.option("--version", "-v", is_flag=True, callback=print_version_callback,
132+
expose_value=False, is_eager=True, help=version)
133+
@debug_option
134+
@profile_option
135+
@eat_exceptions
136+
def repos_group(): # pragma: no cover
137+
"""Utility to interact with Repos."""
138+
pass
139+
140+
141+
repos_group.add_command(list_repos_cli, name='list')
142+
repos_group.add_command(get_repo_cli, name='get')
143+
repos_group.add_command(create_repo_cli, name='create')
144+
repos_group.add_command(update_repo_cli, name='update')
145+
repos_group.add_command(delete_repo_cli, name='delete')

databricks_cli/sdk/service.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,3 +1050,42 @@ def stop(self, pipeline_id=None, headers=None):
10501050

10511051
return self.client.perform_query('POST', '/pipelines/{pipeline_id}/stop'.format(pipeline_id=pipeline_id),
10521052
data=_data, headers=headers)
1053+
class ReposService(object):
1054+
def __init__(self, client):
1055+
self.client = client
1056+
1057+
def list_repos(self, path_prefix=None, next_page_token=None, headers=None):
1058+
_data = {}
1059+
if path_prefix is not None:
1060+
_data['path_prefix'] = path_prefix
1061+
if next_page_token is not None:
1062+
_data['next_page_token'] = next_page_token
1063+
return self.client.perform_query('GET', '/repos', data=_data, headers=headers)
1064+
1065+
def get_repo(self, id, headers=None):
1066+
_data = {}
1067+
1068+
return self.client.perform_query('GET', '/repos/{id}'.format(id=id), data=_data, headers=headers)
1069+
1070+
def update_repo(self, id, branch=None, tag=None, headers=None):
1071+
_data = {}
1072+
if branch is not None:
1073+
_data['branch'] = branch
1074+
if tag is not None:
1075+
_data['tag'] = tag
1076+
return self.client.perform_query('PATCH', '/repos/{id}'.format(id=id), data=_data, headers=headers)
1077+
1078+
def create_repo(self, url, provider, path=None, headers=None):
1079+
_data = {}
1080+
if url is not None:
1081+
_data['url'] = url
1082+
if provider is not None:
1083+
_data['provider'] = provider
1084+
if path is not None:
1085+
_data['path'] = path
1086+
return self.client.perform_query('POST', '/repos', data=_data, headers=headers)
1087+
1088+
def delete_repo(self, id, headers=None):
1089+
_data = {}
1090+
1091+
return self.client.perform_query('DELETE', '/repos/{id}'.format(id=id), data=_data, headers=headers)

databricks_cli/workspace/api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
DIRECTORY = 'DIRECTORY'
3535
NOTEBOOK = 'NOTEBOOK'
3636
LIBRARY = 'LIBRARY'
37+
REPO = 'REPO'
3738

3839

3940
class WorkspaceFileInfo(object):

tests/repos/__init__.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
# Databricks CLI
3+
# Copyright 2017 Databricks, Inc.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"), except
6+
# that the use of services to which certain application programming
7+
# interfaces (each, an "API") connect requires that the user first obtain
8+
# a license for the use of the APIs from Databricks, Inc. ("Databricks"),
9+
# by creating an account at www.databricks.com and agreeing to either (a)
10+
# the Community Edition Terms of Service, (b) the Databricks Terms of
11+
# Service, or (c) another written agreement between Licensee and Databricks
12+
# for the use of the APIs.
13+
#
14+
# You may not use this file except in compliance with the License.
15+
# You may obtain a copy of the License at
16+
#
17+
# http://www.apache.org/licenses/LICENSE-2.0
18+
#
19+
# Unless required by applicable law or agreed to in writing, software
20+
# distributed under the License is distributed on an "AS IS" BASIS,
21+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22+
# See the License for the specific language governing permissions and
23+
# limitations under the License.

0 commit comments

Comments
 (0)