10000 feat(api): add project templates (#3057) · ibrahim60-git/python-gitlab@0d41da3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0d41da3

Browse files
feat(api): add project templates (python-gitlab#3057)
* feat(api): Added project template classes to templates.py * feat(api): Added project template managers to Project in project.py * docs(merge_requests): Add example of creating mr with description template * test(templates): Added unit tests for templates * docs(templates): added section for project templates
1 parent 735efff commit 0d41da3

File tree

5 files changed

+303
-1
lines changed

5 files changed

+303
-1
lines changed

docs/gl_objects/merge_requests.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ Get a single MR::
9595
mr = project.mergerequests.get(mr_iid)
9696

9797
Get MR reviewer details::
98+
9899
mr = project.mergerequests.get(mr_iid)
99100
reviewers = mr.reviewer_details.list()
100101

@@ -105,6 +106,13 @@ Create a MR::
105106
'title': 'merge cool feature',
106107
'labels': ['label1', 'label2']})
107108

109+
# Use a project MR description template
110+
mr_description_template = project.merge_request_templates.get("Default")
111+
mr = project.mergerequests.create({'source_branch': 'cool_feature',
112+
'target_branch': 'main',
113+
'title': 'merge cool feature',
114+
'description': mr_description_template.content})
115+
108116
Update a MR::
109117

110118
mr.description = 'New description'

docs/gl_objects/templates.rst

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ Reference
9999
+ :class:`gitlab.v4.objects.DockerfileManager`
100100
+ :attr:`gitlab.Gitlab.gitlabciymls`
101101

102-
* GitLab API: Not documented.
102+
* GitLab API: https://docs.gitlab.com/ce/api/templates/dockerfiles.html
103103

104104
Examples
105105
--------
@@ -112,3 +112,73 @@ Get a Dockerfile template::
112112

113113
dockerfile = gl.dockerfiles.get('Python')
114114
print(dockerfile.content)
115+
116+
Project templates
117+
=========================
118+
119+
These templates are project-specific versions of the templates above, as
120+
well as issue and merge request templates.
121+
122+
Reference
123+
---------
124+
125+
* v4 API:
126+
127+
+ :class:`gitlab.v4.objects.ProjectLicenseTemplate`
128+
+ :class:`gitlab.v4.objects.ProjectLicenseTemplateManager`
129+
+ :attr:`gitlab.v4.objects.Project.license_templates`
130+
+ :class:`gitlab.v4.objects.ProjectGitignoreTemplate`
131+
+ :class:`gitlab.v4.objects.ProjectGitignoreTemplateManager`
132+
+ :attr:`gitlab.v4.objects.Project.gitignore_templates`
133+
+ :class:`gitlab.v4.objects.ProjectGitlabciymlTemplate`
134+
+ :class:`gitlab.v4.objects.ProjectGitlabciymlTemplateManager`
135+
+ :attr:`gitlab.v4.objects.Project.gitlabciyml_templates`
136+
+ :class:`gitlab.v4.objects.ProjectDockerfileTemplate`
137+
+ :class:`gitlab.v4.objects.ProjectDockerfileTemplateManager`
138+
+ :attr:`gitlab.v4.objects.Project.dockerfile_templates`
139+
+ :class:`gitlab.v4.objects.ProjectIssueTemplate`
140+
+ :class:`gitlab.v4.objects.ProjectIssueTemplateManager`
141+
+ :attr:`gitlab.v4.objects.Project.issue_templates`
142+
+ :class:`gitlab.v4.objects.ProjectMergeRequestTemplate`
143+
+ :class:`gitlab.v4.objects.ProjectMergeRequestTemplateManager`
144+
+ :attr:`gitlab.v4.objects.Project.merge_request_templates`
145+
146+
* GitLab API: https://docs.gitlab.com/ce/api/project_templates.html
147+
148+
Examples
149+
--------
150+
151+
List known project templates::
152+
153+
license_templates = project.license_templates.list()
154+
gitignore_templates = project.gitignore_templates.list()
155+
gitlabciyml_templates = project.gitlabciyml_templates.list()
156+
dockerfile_templates = project.dockerfile_templates.list()
157+
issue_templates = project.issue_templates.list()
158+
merge_request_templates = project.merge_request_templates.list()
159+
160+
Get project templates::
161+
162+
license_template = project.license_templates.get('apache-2.0')
163+
gitignore_template = project.gitignore_templates.get('Python')
164+
gitlabciyml_template = project.gitlabciyml_templates.get('Pelican')
165+
dockerfile_template = project.dockerfile_templates.get('Python')
166+
issue_template = project.issue_templates.get('Default')
167+
merge_request_template = project.merge_request_templates.get('Default')
168+
169+
print(license_template.content)
170+
print(gitignore_template.content)
171+
print(gitlabciyml_template.content)
172+
print(dockerfile_template.content)
173+
print(issue_template.content)
174+
print(merge_request_template.content)
175+
176+
Create an issue or merge request using a description template::
177+
178+
issue = project.issues.create({'title': 'I have a bug',
179+
'description': issue_template.content})
180+
mr = project.mergerequests.create({'source_branch': 'cool_feature',
181+
'target_branch': 'main',
182+
'title': 'merge cool feature',
183+
'description': merge_request_template.content})
184+

gitlab/v4/objects/projects.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@
100100
ProjectIssuesStatisticsManager,
101101
)
102102
from .tags import ProjectProtectedTagManager, ProjectTagManager # noqa: F401
103+
from .templates import ( # noqa: F401
104+
ProjectDockerfileTemplateManager,
105+
ProjectGitignoreTemplateManager,
106+
ProjectGitlabciymlTemplateManager,
107+
ProjectIssueTemplateManager,
108+
ProjectLicenseTemplateManager,
109+
ProjectMergeRequestTemplateManager,
110+
)
103111
from .triggers import ProjectTriggerManager # noqa: F401
104112
from .users import ProjectUserManager # noqa: F401
105113
from .variables import ProjectVariableManager # noqa: F401
@@ -189,27 +197,33 @@ class Project(
189197
customattributes: ProjectCustomAttributeManager
190198
deployments: ProjectDeploymentManager
191199
deploytokens: ProjectDeployTokenManager
200+
dockerfile_templates: ProjectDockerfileTemplateManager
192201
environments: ProjectEnvironmentManager
193202
events: ProjectEventManager
194203
exports: ProjectExportManager
195204
files: ProjectFileManager
196205
forks: "ProjectForkManager"
197206
generic_packages: GenericPackageManager
207+
gitignore_templates: ProjectGitignoreTemplateManager
208+
gitlabciyml_templates: ProjectGitlabciymlTemplateManager
198209
groups: ProjectGroupManager
199210
hooks: ProjectHookManager
200211
imports: ProjectImportManager
201212
integrations: ProjectIntegrationManager
202213
invitations: ProjectInvitationManager
203214
issues: ProjectIssueManager
215+
issue_templates: ProjectIssueTemplateManager
204216
issues_statistics: ProjectIssuesStatisticsManager
205217
iterations: ProjectIterationManager
206218
jobs: ProjectJobManager
207219
job_token_scope: ProjectJobTokenScopeManager
208220
keys: ProjectKeyManager
209221
labels: ProjectLabelManager
222+
license_templates: ProjectLicenseTemplateManager
210223
members: ProjectMemberManager
211224
members_all: ProjectMemberAllManager
212225
mergerequests: ProjectMergeRequestManager
226+
merge_request_templates: ProjectMergeRequestTemplateManager
213227
merge_trains: ProjectMergeTrainManager
214228
milestones: ProjectMilestoneManager
215229
notes: ProjectNoteManager

gitlab/v4/objects/templates.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@
1212
"GitlabciymlManager",
1313
"License",
1414
"LicenseManager",
15+
"ProjectDockerfileTemplate",
16+
"ProjectDockerfileTemplateManager",
17+
"ProjectGitignoreTemplate",
18+
"ProjectGitignoreTemplateManager",
19+
"ProjectGitlabciymlTemplate",
20+
"ProjectGitlabciymlTemplateManager",
21+
"ProjectIssueTemplate",
22+
"ProjectIssueTemplateManager",
23+
"ProjectLicenseTemplate",
24+
"ProjectLicenseTemplateManager",
25+
"ProjectMergeRequestTemplate",
26+
"ProjectMergeRequestTemplateManager",
1527
]
1628

1729

@@ -65,3 +77,95 @@ class LicenseManager(RetrieveMixin, RESTManager):
6577

6678
def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> License:
6779
return cast(License, super().get(id=id, lazy=lazy, **kwargs))
80+
81+
82+
class ProjectDockerfileTemplate(RESTObject):
83+
_id_attr = "name"
84+
85+
86+
class ProjectDockerfileTemplateManager(RetrieveMixin, RESTManager):
87+
_path = "/projects/{project_id}/templates/dockerfiles"
88+
_obj_cls = ProjectDockerfileTemplate
89+
_from_parent_attrs = {"project_id": "id"}
90+
91+
def get(
92+
self, id: Union[str, int], lazy: bool = False, **kwargs: Any
93+
) -> ProjectDockerfileTemplate:
94+
return cast(ProjectDockerfileTemplate, super().get(id=id, lazy=lazy, **kwargs))
95+
96+
97+
class ProjectGitignoreTemplate(RESTObject):
98+
_id_attr = "name"
99+
100+
101+
class ProjectGitignoreTemplateManager(RetrieveMixin, RESTManager):
102+
_path = "/projects/{project_id}/templates/gitignores"
103+
_obj_cls = ProjectGitignoreTemplate
104+
_from_parent_attrs = {"project_id": "id"}
105+
106+
def get(
107+
self, id: Union[str, int], lazy: bool = False, **kwargs: Any
108+
) -> ProjectGitignoreTemplate:
109+
return cast(ProjectGitignoreTemplate, super().get(id=id, lazy=lazy, **kwargs))
110+
111+
112+
class ProjectGitlabciymlTemplate(RESTObject):
113+
_id_attr = "name"
114+
115+
116+
class ProjectGitlabciymlTemplateManager(RetrieveMixin, RESTManager):
117+
_path = "/projects/{project_id}/templates/gitlab_ci_ymls"
118+
_obj_cls = ProjectGitlabciymlTemplate
119+
_from_parent_attrs = {"project_id": "id"}
120+
121+
def get(
122+
self, id: Union[str, int], lazy: bool = False, **kwargs: Any
123+
) -> ProjectGitlabciymlTemplate:
124+
return cast(ProjectGitlabciymlTemplate, super().get(id=id, lazy=lazy, **kwargs))
125+
126+
127+
class ProjectLicenseTemplate(RESTObject):
128+
_id_attr = "key"
129+
130+
131+
class ProjectLicenseTemplateManager(RetrieveMixin, RESTManager):
132+
_path = "/projects/{project_id}/templates/licenses"
133+
_obj_cls = ProjectLicenseTemplate
134+
_from_parent_attrs = {"project_id": "id"}
135+
136+
def get(
137+
self, id: Union[str, int], lazy: bool = False, **kwargs: Any
138+
) -> ProjectLicenseTemplate:
139+
return cast(ProjectLicenseTemplate, super().get(id=id, lazy=lazy, **kwargs))
140+
141+
142+
class ProjectIssueTemplate(RESTObject):
143+
_id_attr = "name"
144+
145+
146+
class ProjectIssueTemplateManager(RetrieveMixin, RESTManager):
147+
_path = "/projects/{project_id}/templates/issues"
148+
_obj_cls = ProjectIssueTemplate
149+
_from_parent_attrs = {"project_id": "id"}
150+
151+
def get(
152+
self, id: Union[str, int], lazy: bool = False, **kwargs: Any
153+
) -> ProjectIssueTemplate:
154+
return cast(ProjectIssueTemplate, super().get(id=id, lazy=lazy, **kwargs))
155+
156+
157+
class ProjectMergeRequestTemplate(RESTObject):
158+
_id_attr = "name"
159+
160+
161+
class ProjectMergeRequestTemplateManager(RetrieveMixin, RESTManager):
162+
_path = "/projects/{project_id}/templates/merge_requests"
163+
_obj_cls = ProjectMergeRequestTemplate
164+
_from_parent_attrs = {"project_id": "id"}
165+
166+
def get(
167+
self, id: Union[str, int], lazy: bool = False, **kwargs: Any
168+
) -> ProjectMergeRequestTemplate:
169+
return cast(
170+
ProjectMergeRequestTemplate, super().get(id=id, lazy=lazy, **kwargs)
171+
)

tests/unit/objects/test_templates.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""
2+
Gitlab API:
3+
https://docs.gitlab.com/ce/api/templates/dockerfiles.html
4+
https://docs.gitlab.com/ce/api/templates/gitignores.html
5+
https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html
6+
https://docs.gitlab.com/ce/api/templates/licenses.html
7+
https://docs.gitlab.com/ce/api/project_templates.html
8+
"""
9+
10+
import pytest
11+
import responses
12+
13+
from gitlab.v4.objects import (
14+
Dockerfile,
15+
Gitignore,
16+
Gitlabciyml,
17+
License,
18+
ProjectDockerfileTemplate,
19+
ProjectGitignoreTemplate,
20+
ProjectGitlabciymlTemplate,
21+
ProjectIssueTemplate,
22+
ProjectLicenseTemplate,
23+
ProjectMergeRequestTemplate,
24+
)
25+
26+
27+
@pytest.mark.parametrize(
28+
"tmpl, tmpl_mgr, tmpl_path",
29+
[
30+
(Dockerfile, "dockerfiles", "dockerfiles"),
31+
(Gitignore, "gitignores", "gitignores"),
32+
(Gitlabciyml, "gitlabciymls", "gitlab_ci_ymls"),
33+
(License, "licenses", "licenses"),
34+
],
35+
ids=[
36+
"dockerfile",
37+
"gitignore",
38+
"gitlabciyml",
39+
"license",
40+
],
41+
)
42+
def test_get_template(gl, tmpl, tmpl_mgr, tmpl_path):
43+
tmpl_id = "sample"
44+
tmpl_content = {"name": tmpl_id, "content": "Sample template content"}
45+
46+
# License templates have 'key' as the id attribute, so ensure
47+
# this is included in the response content
48+
if tmpl == License:
49+
tmpl_id = "smpl"
50+
tmpl_content.update({"key": tmpl_id})
51+
52+
path = f"templates/{tmpl_path}/{tmpl_id}"
53+
with responses.RequestsMock() as rsps:
54+
rsps.add(
55+
method=responses.GET,
56+
url=f"http://localhost/api/v4/{path}",
57+
json=tmpl_content,
58+
)
59+
60+
template = getattr(gl, tmpl_mgr).get(tmpl_id)
61+
62+
assert isinstance(template, tmpl)
63+
assert getattr(template, template._id_attr) == tmpl_id
64+
65+
66+
@pytest.mark.parametrize(
67+
"tmpl, tmpl_mgr, tmpl_path",
68+
[
69+
(ProjectDockerfileTemplate, "dockerfile_templates", "dockerfiles"),
70+
(ProjectGitignoreTemplate, "gitignore_templates", "gitignores"),
71+
(ProjectGitlabciymlTemplate, "gitlabciyml_templates", "gitlab_ci_ymls"),
72+
(ProjectLicenseTemplate, "license_templates", "licenses"),
73+
(ProjectIssueTemplate, "issue_templates", "issues"),
74+
(ProjectMergeRequestTemplate, "merge_request_templates", "merge_requests"),
75+
],
76+
ids=[
77+
"dockerfile",
78+
"gitignore",
79+
"gitlabciyml",
80+
"license",
81+
"issue",
82+
"mergerequest",
83+
],
84+
)
85+
def test_get_project_template(project, tmpl, tmpl_mgr, tmpl_path):
86+
tmpl_id = "sample"
87+
tmpl_content = {"name": tmpl_id, "content": "Sample template content"}
88+
89+
# ProjectLicenseTemplate templates have 'key' as the id attribute, so ensure
90+
# this is included in the response content
91+
if tmpl == ProjectLicenseTemplate:
92+
tmpl_id = "smpl"
93+
tmpl_content.update({"key": tmpl_id})
94+
95+
path = f"projects/{project.id}/templates/{tmpl_path}/{tmpl_id}"
96+
with responses.RequestsMock() as rsps:
97+
rsps.add(
98+
method=responses.GET,
99+
url=f"http://localhost/api/v4/{path}",
100+
json=tmpl_content,
101+
)
102+
103+
template = getattr(project, tmpl_mgr).get(tmpl_id)
104+
105+
assert isinstance(template, tmpl)
106+
assert getattr(template, template._id_attr) == tmpl_id

0 commit comments

Comments
 (0)
0