15
15
import os
16
16
import shutil
17
17
import socket
18
- from typing import Dict , Text , Union , List
18
+ from typing import Dict , List , Union
19
19
20
20
import aiofiles
21
21
import aiohttp
43
43
44
44
45
45
class PRCustomComponentApiClient :
46
+ """Api Client."""
47
+
46
48
def __init__ (
47
49
self ,
48
50
session : aiohttp .ClientSession ,
49
51
pull_url : yarl .URL ,
50
- config_path : Text = "/config" ,
52
+ config_path : str = "/config" ,
51
53
) -> None :
52
54
"""Initialize API client.
53
55
54
56
Args:
55
57
session (aiohttp.ClientSession): Websession to use
56
- pull_url (yarl.URL): URL of pull request, e.g., https://github.com/home-assistant/core/pull/46558
57
- config_path (Text): base path for config, e.g., /config
58
+ pull_url (yarl.URL): URL of pull request, e.g.,
59
+ https://github.com/home-assistant/core/pull/46558
60
+ config_path (str): base path for config, e.g., /config
58
61
59
62
"""
60
63
self ._pull_url : yarl .URL = pull_url
61
64
self ._session : aiohttp .ClientSession = session
62
- self ._config_path : Text = config_path
63
- self ._manifest : Dict [Text , Union [Text , List [Text ]]] = {}
64
- self ._component_name : Text = ""
65
- self ._updated_at : Text = ""
66
- self ._base_path : Text = ""
67
- self ._update_available : Text = ""
68
- self ._token : Text = ""
69
- self ._headers : Dict [Text , Text ] = {}
65
+ self ._config_path : str = config_path
66
+ self ._manifest : Dict [str , Union [str , List [str ]]] = {}
67
+ self ._component_name : str = ""
68
+ self ._updated_at : str = ""
69
+ self ._base_path : str = ""
70
+ self ._update_available : str = ""
71
+ self ._token : str = ""
72
+ self ._headers : Dict [str , str ] = {}
70
73
self ._auto_update : bool = False
71
74
self ._pull_number : int = 0
72
75
73
76
@property
74
- def name (self ) -> Text :
77
+ def name (self ) -> str :
75
78
"""Return the component name."""
76
79
return self ._component_name
77
80
@@ -81,17 +84,17 @@ def pull_number(self) -> int:
81
84
return self ._pull_number
82
85
83
86
@property
84
- def updated_at (self ) -> Text :
87
+ def updated_at (self ) -> str :
85
88
"""Return the last updated time."""
86
89
return self ._updated_at
87
90
88
91
@updated_at .setter
89
- def updated_at (self , value : Text ) -> None :
92
+ def updated_at (self , value : str ) -> None :
90
93
"""Set the last updated time."""
91
94
self ._updated_at = value
92
95
93
96
@property
94
- def update_available (self ) -> Text :
97
+ def update_available (self ) -> str :
95
98
"""Return the whether an update is available."""
96
99
return self ._update_available
97
100
@@ -100,11 +103,11 @@ def auto_update(self) -> bool:
100
103
"""Return the whether an to autoupdate when available."""
101
104
return self ._auto_update
102
105
103
- def set_token (self , token : Text = "" ) -> None :
106
+ def set_token (self , token : str = "" ) -> None :
104
107
"""Set auth token for GitHub to avoid rate limits.
105
108
106
109
Args:
107
- token (Text , optional): Authentication token from GitHub. Defaults to "".
110
+ token (str , optional): Authentication token from GitHub. Defaults to "".
108
111
"""
109
112
if token :
110
113
self ._token = token
@@ -116,20 +119,21 @@ async def async_update_data(self, download: bool = False) -> dict:
116
119
if not pull_json or pull_json .get ("message" ) == "Not Found" :
117
120
_LOGGER .debug ("No pull data found" )
118
121
return {}
119
- component_name : Text = ""
122
+ component_name : str = ""
120
123
for label in pull_json ["labels" ]:
121
124
if label ["name" ].startswith ("integration: " ):
122
125
component_name = label ["name" ].replace ("integration: " , "" )
123
126
break
124
127
if not component_name :
125
128
_LOGGER .error ("Unable to find integration in pull request" )
129
+ return {}
126
130
else :
127
131
_LOGGER .debug ("Found %s integration" , component_name )
128
- branch : Text = pull_json ["head" ]["ref" ]
132
+ branch : str = pull_json ["head" ]["ref" ]
129
133
pull_number : int = pull_json ["number" ]
130
- user : Text = pull_json ["head" ]["user" ]["login" ]
131
- path : Text = f"{ COMPONENT_PATH } { component_name } "
132
- contents_url : Text = pull_json ["head" ]["repo" ]["contents_url" ]
134
+ user : str = pull_json ["head" ]["user" ]["login" ]
135
+ path : str = f"{ COMPONENT_PATH } { component_name } "
136
+ contents_url : str = pull_json ["head" ]["repo" ]["contents_url" ]
133
137
url : yarl .URL = yarl .URL (contents_url .replace ("{+path}" , path )).with_query (
134
138
{"ref" : branch }
135
139
)
@@ -144,7 +148,7 @@ async def async_update_data(self, download: bool = False) -> dict:
144
148
"version" : self ._updated_at .replace ("-" , "." ),
145
149
}
146
150
self ._component_name = component_name
147
- component_path : Text = os .path .join (
151
+ component_path : str = os .path .join (
148
152
self ._config_path , CUSTOM_COMPONENT_PATH , self ._component_name
149
153
)
150
154
if not os .path .isdir (component_path ):
@@ -174,12 +178,12 @@ async def async_get_patch_data(self) -> dict:
174
178
).with_host (PATCH_DOMAIN )
175
179
return await self .api_wrapper ("get" , url )
176
180
177
- async def async_download (self , url : Text , path : Text ) -> bool :
181
+ async def async_download (self , url : str , path : str ) -> bool :
178
182
"""Download and save files to path.
179
183
180
184
Args:
181
185
url (yarl.URL): Remote path to download
182
- path (Text ): Local path to save to
186
+ path (str ): Local path to save to
183
187
184
188
Returns:
185
189
bool: Whether saved successful
@@ -193,26 +197,6 @@ async def async_download(self, url: Text, path: Text) -> bool:
193
197
isinstance (result , dict ) and result .get ("message" ) == "Not Found"
194
198
):
195
199
return False
196
- # [
197
- # {
198
- # "name": "__init__.py",
199
- # "path": "homeassistant/components/tesla/__init__.py",
200
- # "sha": "9e6db33d24ab50c1af1e1e2818580cc96069e076",
201
- # "size": 9220,
202
- # "url": "https://api.github.com/repos/alandtse/home-assistant/contents/homeassistant/components/tesla/__init__.py?ref=tesla_oauth_callback",
203
- # "html_url": "https://github.com/alandtse/home-assistant/blob/tesla_oauth_callback/homeassistant/components/tesla/__init__.py",
204
- # "git_url": "https://api.github.com/repos/alandtse/home-assistant/git/blobs/9e6db33d24ab50c1af1e1e2818580cc96069e076",
205
- # "download_url": "https://raw.githubusercontent.com/alandtse/home-assistant/tesla_oauth_callback/homeassistant/components/tesla/__init__.py",
206
- # "type": "file",
207
- # "content": "IiIiU3V<SNIP>\n",
208
- # "encoding": "base64",
209
- # "_links": {
210
- # "self": "https://api.github.com/repos/alandtse/home-assistant/contents/homeassistant/components/tesla/__init__.py?ref=tesla_oauth_callback",
211
- # "git": "https://api.github.com/repos/alandtse/home-assistant/git/blobs/9e6db33d24ab50c1af1e1e2818580cc96069e076",
212
- # "html": "https://github.com/alandtse/home-assistant/blob/tesla_oauth_callback/homeassistant/components/tesla/__init__.py"
213
- # }
214
- # }
215
- # ]
216
200
if not result :
217
201
_LOGGER .debug ("%s is empty" , url )
218
202
return True
@@ -226,7 +210,7 @@ async def async_download(self, url: Text, path: Text) -> bool:
226
210
_LOGGER .debug ("Processing directory" )
227
211
tasks = []
228
212
for file_json in result :
229
- file_path : Text = os .path .join (path , file_json ["name" ])
213
+ file_path : str = os .path .join (path , file_json ["name" ])
230
214
if file_json ["type" ] == "dir" and not os .path .isdir (file_path ):
231
215
_LOGGER .debug ("Creating new sub directory %s" , file_path )
232
216
os .mkdir (file_path )
@@ -235,15 +219,9 @@ async def async_download(self, url: Text, path: Text) -> bool:
235
219
return True
236
220
if isinstance (result , dict ):
237
221
path .split (os .sep )
238
- file_name : Text = result ["name" ]
222
+ file_name : str = result ["name" ]
239
223
file_path = result ["path" ].replace (self ._base_path , "" )
240
- full_path : Text = os .path .join (path , file_path .lstrip (os .sep ))
241
- # if len(file_path.split(os.sep)) > 1:
242
- # directory = file_path.split(os.sep)[0]
243
- # directory_path = os.path.join(path, directory.lstrip(os.sep))
244
- # if not os.path.isdir(directory_path):
245
- # _LOGGER.debug("Creating new directory %s", directory_path)
246
- # os.mkdir(directory_path)
224
+ full_path : str = os .path .join (path , file_path .lstrip (os .sep ))
247
225
contents = base64 .b64decode (result ["content" ].encode ("utf-8" ))
248
226
_LOGGER .debug ("Saving %s size: %s KB" , full_path , result ["size" ] / 1000 )
249
227
if file_name == "manifest.json" :
@@ -267,11 +245,13 @@ async def api_wrapper(
267
245
self ,
268
246
method : str ,
269
247
url : Union [str , yarl .URL ],
270
- data : dict = {} ,
271
- headers : dict = {} ,
248
+ data : dict = None ,
249
+ headers : dict = None ,
272
250
) -> dict :
273
251
"""Get information from the API."""
252
+ headers = headers or {}
274
253
headers = self ._headers if not headers else headers
254
+ data = data or {}
275
255
try :
276
256
async with async_timeout .timeout (TIMEOUT , loop = asyncio .get_event_loop ()):
277
257
if method == "get" :
@@ -288,17 +268,17 @@ async def api_wrapper(
288
268
_LOGGER .error ("Rate limited: %s" , response_json ["message" ])
289
269
raise RateLimitException ("Rate limited" )
290
270
return response_json
291
- elif method == "put" :
271
+ if method == "put" :
292
272
return await (
293
273
await self ._session .put (url , headers = headers , json = data )
294
274
).json ()
295
275
296
- elif method == "patch" :
276
+ if method == "patch" :
297
277
return await (
298
278
await self ._session .patch (url , headers = headers , json = data )
299
279
).json ()
300
280
301
- elif method == "post" :
281
+ if method == "post" :
302
282
return await (
303
283
await self ._session .post (url , headers = headers , json = data )
304
284
).json ()
@@ -331,14 +311,19 @@ async def async_delete(self) -> bool:
331
311
"""Delete files from config path.
332
312
333
313
Args:
334
- path (Text ): Delete component files.
314
+ path (str ): Delete component files.
335
315
336
316
Returns:
337
317
bool: Whether delete is successful.
338
318
"""
339
- component_path : Text = os .path .join (
319
+ component_path : str = os .path .join (
340
320
self ._config_path , CUSTOM_COMPONENT_PATH , self ._component_name
341
321
)
322
+ if not self ._component_name or component_path == os .path .join (
323
+ self ._config_path , CUSTOM_COMPONENT_PATH
324
+ ):
325
+ _LOGGER .warning ("Component name was empty while delete was called." )
326
+ return False
342
327
if os .path .isdir (component_path ):
343
328
_LOGGER .debug ("Deleting %s" , component_path )
344
329
try :
0 commit comments