Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbc48840

Browse files
authored
Merge pull request#1819 from python-gitlab/jlvillal/encoded_id
fix: use url-encoded ID in all paths
2 parents58e5b25 +b07eece commitbc48840

26 files changed

+267
-134
lines changed

‎gitlab/base.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,15 @@ def get_id(self) -> Any:
217217
returnNone
218218
returngetattr(self,self._id_attr)
219219

220+
@property
221+
defencoded_id(self)->Any:
222+
"""Ensure that the ID is url-encoded so that it can be safely used in a URL
223+
path"""
224+
obj_id=self.get_id()
225+
ifisinstance(obj_id,str):
226+
obj_id=gitlab.utils.EncodedId(obj_id)
227+
returnobj_id
228+
220229
@property
221230
defattributes(self)->Dict[str,Any]:
222231
d=self.__dict__["_updated_attrs"].copy()

‎gitlab/mixins.py

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ def get(
9999
GitlabAuthenticationError: If authentication is not correct
100100
GitlabGetError: If the server cannot perform the request
101101
"""
102-
ifnotisinstance(id,int):
103-
id=utils._url_encode(id)
102+
ifisinstance(id,str):
103+
id=utils.EncodedId(id)
104104
path=f"{self.path}/{id}"
105105
ifTYPE_CHECKING:
106106
assertself._obj_clsisnotNone
@@ -173,7 +173,7 @@ def refresh(self, **kwargs: Any) -> None:
173173
GitlabGetError: If the server cannot perform the request
174174
"""
175175
ifself._id_attr:
176-
path=f"{self.manager.path}/{self.id}"
176+
path=f"{self.manager.path}/{self.encoded_id}"
177177
else:
178178
ifTYPE_CHECKING:
179179
assertself.manager.pathisnotNone
@@ -391,7 +391,7 @@ def update(
391391
ifidisNone:
392392
path=self.path
393393
else:
394-
path=f"{self.path}/{id}"
394+
path=f"{self.path}/{utils.EncodedId(id)}"
395395

396396
self._check_missing_update_attrs(new_data)
397397
files= {}
@@ -444,7 +444,7 @@ def set(self, key: str, value: str, **kwargs: Any) -> base.RESTObject:
444444
Returns:
445445
The created/updated attribute
446446
"""
447-
path=f"{self.path}/{utils._url_encode(key)}"
447+
path=f"{self.path}/{utils.EncodedId(key)}"
448448
data= {"value":value}
449449
server_data=self.gitlab.http_put(path,post_data=data,**kwargs)
450450
ifTYPE_CHECKING:
@@ -477,9 +477,7 @@ def delete(self, id: Union[str, int], **kwargs: Any) -> None:
477477
ifidisNone:
478478
path=self.path
479479
else:
480-
ifnotisinstance(id,int):
481-
id=utils._url_encode(id)
482-
path=f"{self.path}/{id}"
480+
path=f"{self.path}/{utils.EncodedId(id)}"
483481
self.gitlab.http_delete(path,**kwargs)
484482

485483

@@ -545,7 +543,7 @@ def save(self, **kwargs: Any) -> None:
545543
return
546544

547545
# call the manager
548-
obj_id=self.get_id()
546+
obj_id=self.encoded_id
549547
ifTYPE_CHECKING:
550548
assertisinstance(self.manager,UpdateMixin)
551549
server_data=self.manager.update(obj_id,updated_data,**kwargs)
@@ -575,7 +573,7 @@ def delete(self, **kwargs: Any) -> None:
575573
"""
576574
ifTYPE_CHECKING:
577575
assertisinstance(self.manager,DeleteMixin)
578-
self.manager.delete(self.get_id(),**kwargs)
576+
self.manager.delete(self.encoded_id,**kwargs)
579577

580578

581579
classUserAgentDetailMixin(_RestObjectBase):
@@ -598,7 +596,7 @@ def user_agent_detail(self, **kwargs: Any) -> Dict[str, Any]:
598596
GitlabAuthenticationError: If authentication is not correct
599597
GitlabGetError: If the server cannot perform the request
600598
"""
601-
path=f"{self.manager.path}/{self.get_id()}/user_agent_detail"
599+
path=f"{self.manager.path}/{self.encoded_id}/user_agent_detail"
602600
result=self.manager.gitlab.http_get(path,**kwargs)
603601
ifTYPE_CHECKING:
604602
assertnotisinstance(result,requests.Response)
@@ -631,7 +629,7 @@ def approve(
631629
GitlabUpdateError: If the server fails to perform the request
632630
"""
633631

634-
path=f"{self.manager.path}/{self.id}/approve"
632+
path=f"{self.manager.path}/{self.encoded_id}/approve"
635633
data= {"access_level":access_level}
636634
server_data=self.manager.gitlab.http_put(path,post_data=data,**kwargs)
637635
ifTYPE_CHECKING:
@@ -705,7 +703,7 @@ def subscribe(self, **kwargs: Any) -> None:
705703
GitlabAuthenticationError: If authentication is not correct
706704
GitlabSubscribeError: If the subscription cannot be done
707705
"""
708-
path=f"{self.manager.path}/{self.get_id()}/subscribe"
706+
path=f"{self.manager.path}/{self.encoded_id}/subscribe"
709707
server_data=self.manager.gitlab.http_post(path,**kwargs)
710708
ifTYPE_CHECKING:
711709
assertnotisinstance(server_data,requests.Response)
@@ -725,7 +723,7 @@ def unsubscribe(self, **kwargs: Any) -> None:
725723
GitlabAuthenticationError: If authentication is not correct
726724
GitlabUnsubscribeError: If the unsubscription cannot be done
727725
"""
728-
path=f"{self.manager.path}/{self.get_id()}/unsubscribe"
726+
path=f"{self.manager.path}/{self.encoded_id}/unsubscribe"
729727
server_data=self.manager.gitlab.http_post(path,**kwargs)
730728
ifTYPE_CHECKING:
731729
assertnotisinstance(server_data,requests.Response)
@@ -752,7 +750,7 @@ def todo(self, **kwargs: Any) -> None:
752750
GitlabAuthenticationError: If authentication is not correct
753751
GitlabTodoError: If the todo cannot be set
754752
"""
755-
path=f"{self.manager.path}/{self.get_id()}/todo"
753+
path=f"{self.manager.path}/{self.encoded_id}/todo"
756754
self.manager.gitlab.http_post(path,**kwargs)
757755

758756

@@ -781,7 +779,7 @@ def time_stats(self, **kwargs: Any) -> Dict[str, Any]:
781779
if"time_stats"inself.attributes:
782780
returnself.attributes["time_stats"]
783781

784-
path=f"{self.manager.path}/{self.get_id()}/time_stats"
782+
path=f"{self.manager.path}/{self.encoded_id}/time_stats"
785783
result=self.manager.gitlab.http_get(path,**kwargs)
786784
ifTYPE_CHECKING:
787785
assertnotisinstance(result,requests.Response)
@@ -800,7 +798,7 @@ def time_estimate(self, duration: str, **kwargs: Any) -> Dict[str, Any]:
800798
GitlabAuthenticationError: If authentication is not correct
801799
GitlabTimeTrackingError: If the time tracking update cannot be done
802800
"""
803-
path=f"{self.manager.path}/{self.get_id()}/time_estimate"
801+
path=f"{self.manager.path}/{self.encoded_id}/time_estimate"
804802
data= {"duration":duration}
805803
result=self.manager.gitlab.http_post(path,post_data=data,**kwargs)
806804
ifTYPE_CHECKING:
@@ -819,7 +817,7 @@ def reset_time_estimate(self, **kwargs: Any) -> Dict[str, Any]:
819817
GitlabAuthenticationError: If authentication is not correct
820818
GitlabTimeTrackingError: If the time tracking update cannot be done
821819
"""
822-
path=f"{self.manager.path}/{self.get_id()}/reset_time_estimate"
820+
path=f"{self.manager.path}/{self.encoded_id}/reset_time_estimate"
823821
result=self.manager.gitlab.http_post(path,**kwargs)
824822
ifTYPE_CHECKING:
825823
assertnotisinstance(result,requests.Response)
@@ -838,7 +836,7 @@ def add_spent_time(self, duration: str, **kwargs: Any) -> Dict[str, Any]:
838836
GitlabAuthenticationError: If authentication is not correct
839837
GitlabTimeTrackingError: If the time tracking update cannot be done
840838
"""
841-
path=f"{self.manager.path}/{self.get_id()}/add_spent_time"
839+
path=f"{self.manager.path}/{self.encoded_id}/add_spent_time"
842840
data= {"duration":duration}
843841
result=self.manager.gitlab.http_post(path,post_data=data,**kwargs)
844842
ifTYPE_CHECKING:
@@ -857,7 +855,7 @@ def reset_spent_time(self, **kwargs: Any) -> Dict[str, Any]:
857855
GitlabAuthenticationError: If authentication is not correct
858856
GitlabTimeTrackingError: If the time tracking update cannot be done
859857
"""
860-
path=f"{self.manager.path}/{self.get_id()}/reset_spent_time"
858+
path=f"{self.manager.path}/{self.encoded_id}/reset_spent_time"
861859
result=self.manager.gitlab.http_post(path,**kwargs)
862860
ifTYPE_CHECKING:
863861
assertnotisinstance(result,requests.Response)
@@ -893,7 +891,7 @@ def participants(self, **kwargs: Any) -> Dict[str, Any]:
893891
The list of participants
894892
"""
895893

896-
path=f"{self.manager.path}/{self.get_id()}/participants"
894+
path=f"{self.manager.path}/{self.encoded_id}/participants"
897895
result=self.manager.gitlab.http_get(path,**kwargs)
898896
ifTYPE_CHECKING:
899897
assertnotisinstance(result,requests.Response)
@@ -967,7 +965,7 @@ def promote(self, **kwargs: Any) -> Dict[str, Any]:
967965
The updated object data (*not* a RESTObject)
968966
"""
969967

970-
path=f"{self.manager.path}/{self.id}/promote"
968+
path=f"{self.manager.path}/{self.encoded_id}/promote"
971969
http_method=self._get_update_method()
972970
result=http_method(path,**kwargs)
973971
ifTYPE_CHECKING:

‎gitlab/utils.py

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717

1818
importurllib.parse
19-
fromtypingimportAny,Callable,Dict,Optional
19+
fromtypingimportAny,Callable,Dict,Optional,Union
2020

2121
importrequests
2222

@@ -56,25 +56,32 @@ def copy_dict(dest: Dict[str, Any], src: Dict[str, Any]) -> None:
5656
dest[k]=v
5757

5858

59-
def_url_encode(id:str)->str:
60-
"""Encode/quote the characters in the string sothatthey can be used in a path.
59+
classEncodedId(str):
60+
"""A custom `str` classthatwill return the URL-encoded value of the string.
6161
62-
Reference to documentation on why this is necessary.
63-
64-
https://docs.gitlab.com/ee/api/index.html#namespaced-path-encoding
65-
66-
If using namespaced API requests, make sure that the NAMESPACE/PROJECT_PATH is
67-
URL-encoded. For example, / is represented by %2F
62+
* Using it recursively will only url-encode the value once.
63+
* Can accept either `str` or `int` as input value.
64+
* Can be used in an f-string and output the URL-encoded string.
6865
69-
https://docs.gitlab.com/ee/api/index.html#path-parameters
66+
Reference to documentation on why this is necessary.
7067
71-
Path parameters that are required to be URL-encoded must be followed. If not, it
72-
doesn’t match an API endpoint and responds with a 404. If there’s something in front
73-
of the API (for example, Apache), ensure that it doesn’t decode the URL-encoded path
74-
parameters.
68+
See::
7569
70+
https://docs.gitlab.com/ee/api/index.html#namespaced-path-encoding
71+
https://docs.gitlab.com/ee/api/index.html#path-parameters
7672
"""
77-
returnurllib.parse.quote(id,safe="")
73+
74+
# mypy complains if return type other than the class type. So we ignore issue.
75+
def__new__(# type: ignore
76+
cls,value:Union[str,int,"EncodedId"]
77+
)->Union[int,"EncodedId"]:
78+
ifisinstance(value, (int,EncodedId)):
79+
returnvalue
80+
81+
ifnotisinstance(value,str):
82+
raiseTypeError(f"Unsupported type received:{type(value)}")
83+
value=urllib.parse.quote(value,safe="")
84+
returnsuper().__new__(cls,value)
7885

7986

8087
defremove_none_from_dict(data:Dict[str,Any])->Dict[str,Any]:

‎gitlab/v4/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def _process_from_parent_attrs(self) -> None:
7575
ifkeynotinself.args:
7676
continue
7777

78-
self.parent_args[key]=gitlab.utils._url_encode(self.args[key])
78+
self.parent_args[key]=gitlab.utils.EncodedId(self.args[key])
7979
# If we don't delete it then it will be added to the URL as a query-string
8080
delself.args[key]
8181

‎gitlab/v4/objects/commits.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def diff(self, **kwargs: Any) -> Union[gitlab.GitlabList, List[Dict[str, Any]]]:
4242
Returns:
4343
The changes done in this commit
4444
"""
45-
path=f"{self.manager.path}/{self.get_id()}/diff"
45+
path=f"{self.manager.path}/{self.encoded_id}/diff"
4646
returnself.manager.gitlab.http_list(path,**kwargs)
4747

4848
@cli.register_custom_action("ProjectCommit", ("branch",))
@@ -58,7 +58,7 @@ def cherry_pick(self, branch: str, **kwargs: Any) -> None:
5858
GitlabAuthenticationError: If authentication is not correct
5959
GitlabCherryPickError: If the cherry-pick could not be performed
6060
"""
61-
path=f"{self.manager.path}/{self.get_id()}/cherry_pick"
61+
path=f"{self.manager.path}/{self.encoded_id}/cherry_pick"
6262
post_data= {"branch":branch}
6363
self.manager.gitlab.http_post(path,post_data=post_data,**kwargs)
6464

@@ -80,7 +80,7 @@ def refs(
8080
Returns:
8181
The references the commit is pushed to.
8282
"""
83-
path=f"{self.manager.path}/{self.get_id()}/refs"
83+
path=f"{self.manager.path}/{self.encoded_id}/refs"
8484
query_data= {"type":type}
8585
returnself.manager.gitlab.http_list(path,query_data=query_data,**kwargs)
8686

@@ -101,7 +101,7 @@ def merge_requests(
101101
Returns:
102102
The merge requests related to the commit.
103103
"""
104-
path=f"{self.manager.path}/{self.get_id()}/merge_requests"
104+
path=f"{self.manager.path}/{self.encoded_id}/merge_requests"
105105
returnself.manager.gitlab.http_list(path,**kwargs)
106106

107107
@cli.register_custom_action("ProjectCommit", ("branch",))
@@ -122,7 +122,7 @@ def revert(
122122
Returns:
123123
The new commit data (*not* a RESTObject)
124124
"""
125-
path=f"{self.manager.path}/{self.get_id()}/revert"
125+
path=f"{self.manager.path}/{self.encoded_id}/revert"
126126
post_data= {"branch":branch}
127127
returnself.manager.gitlab.http_post(path,post_data=post_data,**kwargs)
128128

@@ -141,7 +141,7 @@ def signature(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
141141
Returns:
142142
The commit's signature data
143143
"""
144-
path=f"{self.manager.path}/{self.get_id()}/signature"
144+
path=f"{self.manager.path}/{self.encoded_id}/signature"
145145
returnself.manager.gitlab.http_get(path,**kwargs)
146146

147147

‎gitlab/v4/objects/environments.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def stop(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
3636
Returns:
3737
A dict of the result.
3838
"""
39-
path=f"{self.manager.path}/{self.get_id()}/stop"
39+
path=f"{self.manager.path}/{self.encoded_id}/stop"
4040
returnself.manager.gitlab.http_post(path,**kwargs)
4141

4242

‎gitlab/v4/objects/epics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def save(self, **kwargs: Any) -> None:
7272
return
7373

7474
# call the manager
75-
obj_id=self.get_id()
75+
obj_id=self.encoded_id
7676
self.manager.update(obj_id,updated_data,**kwargs)
7777

7878

‎gitlab/v4/objects/features.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def set(
5252
Returns:
5353
The created/updated attribute
5454
"""
55-
name=utils._url_encode(name)
55+
name=utils.EncodedId(name)
5656
path=f"{self.path}/{name}"
5757
data= {
5858
"value":value,

‎gitlab/v4/objects/files.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def save( # type: ignore
5656
"""
5757
self.branch=branch
5858
self.commit_message=commit_message
59-
self.file_path=utils._url_encode(self.file_path)
59+
self.file_path=utils.EncodedId(self.file_path)
6060
super(ProjectFile,self).save(**kwargs)
6161

6262
@exc.on_http_error(exc.GitlabDeleteError)
@@ -76,7 +76,7 @@ def delete( # type: ignore
7676
GitlabAuthenticationError: If authentication is not correct
7777
GitlabDeleteError: If the server cannot perform the request
7878
"""
79-
file_path=utils._url_encode(self.get_id())
79+
file_path=self.encoded_id
8080
self.manager.delete(file_path,branch,commit_message,**kwargs)
8181

8282

@@ -144,7 +144,7 @@ def create(
144144
assertdataisnotNone
145145
self._check_missing_create_attrs(data)
146146
new_data=data.copy()
147-
file_path=utils._url_encode(new_data.pop("file_path"))
147+
file_path=utils.EncodedId(new_data.pop("file_path"))
148148
path=f"{self.path}/{file_path}"
149149
server_data=self.gitlab.http_post(path,post_data=new_data,**kwargs)
150150
ifTYPE_CHECKING:
@@ -173,7 +173,7 @@ def update( # type: ignore
173173
"""
174174
new_data=new_dataor {}
175175
data=new_data.copy()
176-
file_path=utils._url_encode(file_path)
176+
file_path=utils.EncodedId(file_path)
177177
data["file_path"]=file_path
178178
path=f"{self.path}/{file_path}"
179179
self._check_missing_update_attrs(data)
@@ -203,7 +203,7 @@ def delete( # type: ignore
203203
GitlabAuthenticationError: If authentication is not correct
204204
GitlabDeleteError: If the server cannot perform the request
205205
"""
206-
file_path=utils._url_encode(file_path)
206+
file_path=utils.EncodedId(file_path)
207207
path=f"{self.path}/{file_path}"
208208
data= {"branch":branch,"commit_message":commit_message}
209209
self.gitlab.http_delete(path,query_data=data,**kwargs)
@@ -239,7 +239,7 @@ def raw(
239239
Returns:
240240
The file content
241241
"""
242-
file_path=utils._url_encode(file_path)
242+
file_path=utils.EncodedId(file_path)
243243
path=f"{self.path}/{file_path}/raw"
244244
query_data= {"ref":ref}
245245
result=self.gitlab.http_get(
@@ -266,7 +266,7 @@ def blame(self, file_path: str, ref: str, **kwargs: Any) -> List[Dict[str, Any]]
266266
Returns:
267267
A list of commits/lines matching the file
268268
"""
269-
file_path=utils._url_encode(file_path)
269+
file_path=utils.EncodedId(file_path)
270270
path=f"{self.path}/{file_path}/blame"
271271
query_data= {"ref":ref}
272272
result=self.gitlab.http_list(path,query_data,**kwargs)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp