Skip to content
This repository was archived by the owner on Feb 27, 2023. It is now read-only.

Commit f7bc10a

Browse files
authored
Full license request (#26)
License support
2 parents 1b695ab + ea404f3 commit f7bc10a

10 files changed

Lines changed: 454 additions & 143 deletions

bintray/bintray.py

Lines changed: 112 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
#!/usr/bin/env python
21
# -*- coding: utf-8 -*-
32
""" Python Wrapper for Bintray API
43
54
https://bintray.com/docs/api
65
"""
76
import os
8-
import logging
9-
import requests
10-
from requests.auth import HTTPBasicAuth
7+
8+
from bintray.requester import Requester
9+
from bintray.logger import Logger
10+
from bintray.utils import bool_to_number
1111

1212

1313
__version__ = "0.1.1"
@@ -31,52 +31,8 @@ def __init__(self, username=None, api_key=None):
3131
"""
3232
self._username = username or os.getenv("BINTRAY_USERNAME")
3333
self._password = api_key or os.getenv("BINTRAY_API_KEY")
34-
35-
self._logger = logging.getLogger(__file__)
36-
self._logger.setLevel(logging.INFO)
37-
formatter = logging.Formatter('%(asctime)s:%(levelname)s: %(message)s')
38-
ch = logging.StreamHandler()
39-
level = int(os.getenv("BINTRAY_LOGGING_LEVEL", logging.INFO))
40-
ch.setLevel(level)
41-
ch.setFormatter(formatter)
42-
self._logger.addHandler(ch)
43-
44-
def _get_authentication(self):
45-
""" Retrieve Basic HTTP Authentication based on username and API key
46-
47-
:return: Basic Authentication handler
48-
"""
49-
if not self._username or not self._password:
50-
return None
51-
return HTTPBasicAuth(self._username, self._password)
52-
53-
def _add_status_code(self, response):
54-
""" Update JSON result with error and status code
55-
56-
:param response: Requests response
57-
:return: Response JSON
58-
"""
59-
json_data = response.json()
60-
if isinstance(json_data, list):
61-
json_data.append({"statusCode": response.status_code, "error": not response.ok})
62-
else:
63-
json_data.update({"statusCode": response.status_code, "error": not response.ok})
64-
return json_data
65-
66-
67-
def _bool_to_number(self, value):
68-
""" Convert boolean result into numeric string
69-
70-
:param value: Any boolean value
71-
:return: "1" when True. Otherwise, "0"
72-
"""
73-
return "1" if value else "0"
74-
75-
def _raise_error(self, message, response):
76-
try:
77-
response.raise_for_status()
78-
except Exception as error:
79-
raise Exception("{} ({}): {}".format(message, response.status_code, str(error)))
34+
self._requester = Requester(self._username, self._password)
35+
self._logger = Logger().logger
8036

8137
# Files
8238

@@ -91,16 +47,13 @@ def get_package_files(self, subject, repo, package, include_unpublished=False):
9147
:param include_unpublished: Show not published files
9248
:return: List with all files
9349
"""
94-
parameters = {"include_unpublished": self._bool_to_number(include_unpublished)}
50+
parameters = {"include_unpublished": bool_to_number(include_unpublished)}
9551
url = "{}/packages/{}/{}/{}/files?include_unpublished={}".format(Bintray.BINTRAY_URL,
9652
subject,
9753
repo,
9854
package,
9955
include_unpublished)
100-
response = requests.get(url, auth=self._get_authentication(), params=parameters)
101-
if not response.ok:
102-
self._raise_error("Could not list package files", response)
103-
return self._add_status_code(response)
56+
return self._requester.get(url, parameters)
10457

10558
# Content Uploading & Publishing
10659

@@ -122,17 +75,15 @@ def upload_content(self, subject, repo, package, version, remote_file_path, loca
12275
"""
12376
url = "{}/content/{}/{}/{}/{}/{}".format(Bintray.BINTRAY_URL, subject, repo, package,
12477
version, remote_file_path)
125-
parameters = {"publish": self._bool_to_number(publish),
126-
"override": self._bool_to_number(override),
127-
"explode": self._bool_to_number(explode)}
78+
parameters = {"publish": bool_to_number(publish),
79+
"override": bool_to_number(override),
80+
"explode": bool_to_number(explode)}
12881

12982
with open(local_file_path, 'rb') as file_content:
130-
response = requests.put(url, auth=self._get_authentication(), params=parameters,
131-
data=file_content)
132-
if response.status_code != 201:
133-
self._raise_error("Could not upload", response)
83+
response = self._requester.put(url, params=parameters, data=file_content)
84+
13485
self._logger.info("Upload successfully: {}".format(url))
135-
return self._add_status_code(response)
86+
return response
13687

13788
# Content Downloading
13889

@@ -146,10 +97,103 @@ def download_content(self, subject, repo, remote_file_path, local_file_path):
14697
"""
14798
download_base_url = "https://dl.bintray.com"
14899
url = "{}/{}/{}/{}".format(download_base_url, subject, repo, remote_file_path)
149-
response = requests.get(url, auth=self._get_authentication())
150-
if not response.ok:
151-
self._raise_error("Could not download file content", response)
100+
response, content = self._requester.download(url)
101+
152102
with open(local_file_path, 'wb') as local_fd:
153-
local_fd.write(response.content)
103+
local_fd.write(content)
104+
154105
self._logger.info("Download successfully: {}".format(url))
155-
return self._add_status_code(response)
106+
return response
107+
108+
# Licenses
109+
110+
def get_org_proprietary_licenses(self, org):
111+
""" Get a list of custom, proprietary licenses associated with an organization
112+
113+
:param org: Organization name
114+
:return: Licenses list
115+
"""
116+
url = "{}/orgs/{}/licenses".format(Bintray.BINTRAY_URL, org)
117+
return self._requester.get(url)
118+
119+
def get_user_proprietary_licenses(self, user):
120+
""" Get a list of custom, proprietary licenses associated with an user
121+
122+
:param user: User name
123+
:return: Licenses list
124+
"""
125+
url = "{}/users/{}/licenses".format(Bintray.BINTRAY_URL, user)
126+
return self._requester.get(url)
127+
128+
def create_org_proprietary_license(self, org, license):
129+
""" Create a license associated with an organization.
130+
Caller must be an admin of the organization.
131+
132+
:param org: Organization name
133+
:param license: JSON data with license information
134+
:return: request answer
135+
"""
136+
url = "{}/orgs/{}/licenses".format(Bintray.BINTRAY_URL, org)
137+
return self._requester.post(url, json=license)
138+
139+
def create_user_proprietary_license(self, user, license):
140+
""" Create a license associated with an user.
141+
142+
:param user: User name
143+
:param license: JSON data with license information
144+
:return: request answer
145+
"""
146+
url = "{}/users/{}/licenses".format(Bintray.BINTRAY_URL, user)
147+
return self._requester.post(url, json=license)
148+
149+
def update_org_proprietary_license(self, org, custom_license_name, license):
150+
""" Update a license associated with an organization.
151+
Caller must be an admin of the organization.
152+
153+
:param org: Organization name
154+
:param custom_license_name: License to be updated
155+
:param license: JSON data with license information
156+
:return: request answer
157+
"""
158+
url = "{}/orgs/{}/licenses/{}".format(Bintray.BINTRAY_URL, org, custom_license_name)
159+
return self._requester.patch(url, json=license)
160+
161+
def update_user_proprietary_license(self, user, custom_license_name, license):
162+
""" Update a license associated with an user.
163+
164+
:param user: User name
165+
:param custom_license_name: License to be updated
166+
:param license: JSON data with license information
167+
:return: request answer
168+
"""
169+
url = "{}/users/{}/licenses/{}".format(Bintray.BINTRAY_URL, user, custom_license_name)
170+
return self._requester.patch(url, json=license)
171+
172+
def delete_org_proprietary_license(self, org, custom_license_name):
173+
""" Delete a license associated with an organization.
174+
For organization, caller must be an admin of the organization.
175+
176+
:param org: Organization name
177+
:param custom_license_name: License name to be deleted
178+
:return: request answer
179+
"""
180+
url = "{}/orgs/{}/licenses/{}".format(Bintray.BINTRAY_URL, org, custom_license_name)
181+
return self._requester.delete(url)
182+
183+
def delete_user_proprietary_license(self, user, custom_license_name):
184+
""" Delete a license associated with an user.
185+
186+
:param user: User name
187+
:param custom_license_name: License to be deleted
188+
:return: request answer
189+
"""
190+
url = "{}/users/{}/licenses/{}".format(Bintray.BINTRAY_URL, user, custom_license_name)
191+
return self._requester.patch(url)
192+
193+
def get_oss_licenses(self):
194+
""" Returns a list of all the OSS licenses.
195+
196+
:return: List with OSS licenses
197+
"""
198+
url = "{}/licenses/oss_licenses".format(Bintray.BINTRAY_URL)
199+
return self._requester.get(url)

bintray/logger.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import logging
4+
import os
5+
6+
7+
class Logger(object):
8+
9+
def __init__(self):
10+
self._logger = logging.getLogger("bintray")
11+
self._logger.setLevel(logging.INFO)
12+
formatter = logging.Formatter('%(asctime)s:%(levelname)s: %(message)s')
13+
ch = logging.StreamHandler()
14+
level = int(os.getenv("BINTRAY_LOGGING_LEVEL", logging.INFO))
15+
ch.setLevel(level)
16+
ch.setFormatter(formatter)
17+
self._logger.addHandler(ch)
18+
19+
@property
20+
def logger(self):
21+
return self._logger

bintray/requester.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import requests
4+
5+
from requests.auth import HTTPBasicAuth
6+
7+
8+
class Requester(object):
9+
10+
def __init__(self, username=None, api_key=None):
11+
""" Initialize arguments for login
12+
13+
:param username: Bintray username
14+
:param api_key: Bintray API Key
15+
"""
16+
self._username = username
17+
self._password = api_key
18+
19+
def _get_authentication(self):
20+
""" Retrieve Basic HTTP Authentication based on username and API key
21+
22+
:return: Basic Authentication handler
23+
"""
24+
if not self._username or not self._password:
25+
return None
26+
return HTTPBasicAuth(self._username, self._password)
27+
28+
def _add_status_code(self, response):
29+
""" Update JSON result with error and status code
30+
31+
:param response: Requests response
32+
:return: Response JSON
33+
"""
34+
json_data = response.json()
35+
if isinstance(json_data, list):
36+
json_data.append({"statusCode": response.status_code, "error": not response.ok})
37+
else:
38+
json_data.update({"statusCode": response.status_code, "error": not response.ok})
39+
return json_data
40+
41+
def _raise_error(self, message, response):
42+
try:
43+
response.raise_for_status()
44+
except Exception as error:
45+
raise Exception("{} ({}): {}".format(message, response.status_code, str(error)))
46+
47+
def get(self, url, params=None):
48+
""" Forward GET method
49+
50+
:param url: Web address
51+
:param params: URL params
52+
:return: JSON answer
53+
"""
54+
response, _ = self.download(url, params)
55+
return response
56+
57+
def download(self, url, params=None):
58+
""" Just like GET method, but with content
59+
60+
:param url: URL Address
61+
:param params: URL parameters
62+
:return: JSON response and content
63+
"""
64+
response = requests.get(url, auth=self._get_authentication(), params=params)
65+
if not response.ok:
66+
self._raise_error("Could not GET", response)
67+
return self._add_status_code(response), response.content
68+
69+
def put(self, url, params=None, data=None):
70+
""" Forward PUT method
71+
72+
:param url: URL address
73+
:param params: URL params
74+
:param data: Data content
75+
:return: JSON
76+
"""
77+
response = requests.put(url, auth=self._get_authentication(), params=params, data=data)
78+
if not response.ok:
79+
self._raise_error("Could not PUT", response)
80+
return self._add_status_code(response)
81+
82+
def post(self, url, json=None, params=None):
83+
""" Forward POST method
84+
85+
:param url: URL address
86+
:param params: URL parameters
87+
:param json: Data to be posted
88+
:return: Request response
89+
"""
90+
response = requests.post(url, auth=self._get_authentication(), json=json, params=params)
91+
if not response.ok:
92+
self._raise_error("Could not POST", response)
93+
return self._add_status_code(response)
94+
95+
def patch(self, url, json=None, params=None):
96+
""" Forward PATCH method
97+
98+
:param url: URL address
99+
:param params: URL parameters
100+
:param json: Data to be patched
101+
:return: Request response
102+
"""
103+
response = requests.post(url, auth=self._get_authentication(), json=json, params=params)
104+
if not response.ok:
105+
self._raise_error("Could not PATCH", response)
106+
return self._add_status_code(response)
107+
108+
def delete(self, url, params=None):
109+
""" Forward DELETE method
110+
111+
:param url: URL address
112+
:param params: URL parameters
113+
:return: Request response
114+
"""
115+
response = requests.delete(url, auth=self._get_authentication(), params=params)
116+
if not response.ok:
117+
self._raise_error("Could not DELETE", response)
118+
return self._add_status_code(response)

bintray/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# -*- coding: utf-8 -*-
2+
3+
4+
def bool_to_number(value):
5+
""" Convert boolean result into numeric string
6+
7+
:param value: Any boolean value
8+
:return: "1" when True. Otherwise, "0"
9+
"""
10+
return "1" if value else "0"

docs/bintray_REST_API.pdf

457 KB
Binary file not shown.

0 commit comments

Comments
 (0)