Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
050eae8c
Commit
050eae8c
authored
Jul 06, 2017
by
Rémy Coutable
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '33657-user-projects-api' into 'master'
Add user projects API Closes #33657 See merge request !12596
parents
afd5c34d
8d44d514
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
257 additions
and
50 deletions
+257
-50
app/finders/projects_finder.rb
app/finders/projects_finder.rb
+8
-1
changelogs/unreleased/33657-user-projects-api.yml
changelogs/unreleased/33657-user-projects-api.yml
+4
-0
doc/api/projects.md
doc/api/projects.md
+158
-0
lib/api/helpers.rb
lib/api/helpers.rb
+2
-1
lib/api/projects.rb
lib/api/projects.rb
+65
-48
spec/requests/api/projects_spec.rb
spec/requests/api/projects_spec.rb
+20
-0
No files found.
app/finders/projects_finder.rb
View file @
050eae8c
...
...
@@ -28,7 +28,14 @@ class ProjectsFinder < UnionFinder
end
def
execute
collection
=
init_collection
user
=
params
.
delete
(
:user
)
collection
=
if
user
PersonalProjectsFinder
.
new
(
user
).
execute
(
current_user
)
else
init_collection
end
collection
=
by_ids
(
collection
)
collection
=
by_personal
(
collection
)
collection
=
by_starred
(
collection
)
...
...
changelogs/unreleased/33657-user-projects-api.yml
0 → 100644
View file @
050eae8c
---
title
:
Add user projects API
merge_request
:
12596
author
:
Ivan Chernov
doc/api/projects.md
View file @
050eae8c
...
...
@@ -173,6 +173,164 @@ Parameters:
]
```
### List a user's projects
Get a list of visible projects for the given user. When accessed without authentication, only public projects are returned.
```
GET /users/:user_id/projects
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
|
`user_id`
| string | yes | The ID or username of the user |
|
`archived`
| boolean | no | Limit by archived status |
|
`visibility`
| string | no | Limit by visibility
`public`
,
`internal`
, or
`private`
|
|
`order_by`
| string | no | Return projects ordered by
`id`
,
`name`
,
`path`
,
`created_at`
,
`updated_at`
, or
`last_activity_at`
fields. Default is
`created_at`
|
|
`sort`
| string | no | Return projects sorted in
`asc`
or
`desc`
order. Default is
`desc`
|
|
`search`
| string | no | Return list of projects matching the search criteria |
|
`simple`
| boolean | no | Return only the ID, URL, name, and path of each project |
|
`owned`
| boolean | no | Limit by projects owned by the current user |
|
`membership`
| boolean | no | Limit by projects that the current user is a member of |
|
`starred`
| boolean | no | Limit by projects starred by the current user |
|
`statistics`
| boolean | no | Include project statistics |
|
`with_issues_enabled`
| boolean | no | Limit by enabled issues feature |
|
`with_merge_requests_enabled`
| boolean | no | Limit by enabled merge requests feature |
```
json
[
{
"id"
:
4
,
"description"
:
null
,
"default_branch"
:
"master"
,
"visibility"
:
"private"
,
"ssh_url_to_repo"
:
"git@example.com:diaspora/diaspora-client.git"
,
"http_url_to_repo"
:
"http://example.com/diaspora/diaspora-client.git"
,
"web_url"
:
"http://example.com/diaspora/diaspora-client"
,
"tag_list"
:
[
"example"
,
"disapora client"
],
"owner"
:
{
"id"
:
3
,
"name"
:
"Diaspora"
,
"created_at"
:
"2013-09-30T13:46:02Z"
},
"name"
:
"Diaspora Client"
,
"name_with_namespace"
:
"Diaspora / Diaspora Client"
,
"path"
:
"diaspora-client"
,
"path_with_namespace"
:
"diaspora/diaspora-client"
,
"issues_enabled"
:
true
,
"open_issues_count"
:
1
,
"merge_requests_enabled"
:
true
,
"jobs_enabled"
:
true
,
"wiki_enabled"
:
true
,
"snippets_enabled"
:
false
,
"container_registry_enabled"
:
false
,
"created_at"
:
"2013-09-30T13:46:02Z"
,
"last_activity_at"
:
"2013-09-30T13:46:02Z"
,
"creator_id"
:
3
,
"namespace"
:
{
"id"
:
3
,
"name"
:
"Diaspora"
,
"path"
:
"diaspora"
,
"kind"
:
"group"
,
"full_path"
:
"diaspora"
},
"import_status"
:
"none"
,
"archived"
:
false
,
"avatar_url"
:
"http://example.com/uploads/project/avatar/4/uploads/avatar.png"
,
"shared_runners_enabled"
:
true
,
"forks_count"
:
0
,
"star_count"
:
0
,
"runners_token"
:
"b8547b1dc37721d05889db52fa2f02"
,
"public_jobs"
:
true
,
"shared_with_groups"
:
[],
"only_allow_merge_if_pipeline_succeeds"
:
false
,
"only_allow_merge_if_all_discussions_are_resolved"
:
false
,
"request_access_enabled"
:
false
,
"statistics"
:
{
"commit_count"
:
37
,
"storage_size"
:
1038090
,
"repository_size"
:
1038090
,
"lfs_objects_size"
:
0
,
"job_artifacts_size"
:
0
}
},
{
"id"
:
6
,
"description"
:
null
,
"default_branch"
:
"master"
,
"visibility"
:
"private"
,
"ssh_url_to_repo"
:
"git@example.com:brightbox/puppet.git"
,
"http_url_to_repo"
:
"http://example.com/brightbox/puppet.git"
,
"web_url"
:
"http://example.com/brightbox/puppet"
,
"tag_list"
:
[
"example"
,
"puppet"
],
"owner"
:
{
"id"
:
4
,
"name"
:
"Brightbox"
,
"created_at"
:
"2013-09-30T13:46:02Z"
},
"name"
:
"Puppet"
,
"name_with_namespace"
:
"Brightbox / Puppet"
,
"path"
:
"puppet"
,
"path_with_namespace"
:
"brightbox/puppet"
,
"issues_enabled"
:
true
,
"open_issues_count"
:
1
,
"merge_requests_enabled"
:
true
,
"jobs_enabled"
:
true
,
"wiki_enabled"
:
true
,
"snippets_enabled"
:
false
,
"container_registry_enabled"
:
false
,
"created_at"
:
"2013-09-30T13:46:02Z"
,
"last_activity_at"
:
"2013-09-30T13:46:02Z"
,
"creator_id"
:
3
,
"namespace"
:
{
"id"
:
4
,
"name"
:
"Brightbox"
,
"path"
:
"brightbox"
,
"kind"
:
"group"
,
"full_path"
:
"brightbox"
},
"import_status"
:
"none"
,
"import_error"
:
null
,
"permissions"
:
{
"project_access"
:
{
"access_level"
:
10
,
"notification_level"
:
3
},
"group_access"
:
{
"access_level"
:
50
,
"notification_level"
:
3
}
},
"archived"
:
false
,
"avatar_url"
:
null
,
"shared_runners_enabled"
:
true
,
"forks_count"
:
0
,
"star_count"
:
0
,
"runners_token"
:
"b8547b1dc37721d05889db52fa2f02"
,
"public_jobs"
:
true
,
"shared_with_groups"
:
[],
"only_allow_merge_if_pipeline_succeeds"
:
false
,
"only_allow_merge_if_all_discussions_are_resolved"
:
false
,
"request_access_enabled"
:
false
,
"statistics"
:
{
"commit_count"
:
12
,
"storage_size"
:
2066080
,
"repository_size"
:
2066080
,
"lfs_objects_size"
:
0
,
"job_artifacts_size"
:
0
}
}
]
```
### Get single project
Get a specific project. This endpoint can be accessed without authentication if
...
...
lib/api/helpers.rb
View file @
050eae8c
...
...
@@ -268,6 +268,7 @@ module API
finder_params
[
:visibility_level
]
=
Gitlab
::
VisibilityLevel
.
level_value
(
params
[
:visibility
])
if
params
[
:visibility
]
finder_params
[
:archived
]
=
params
[
:archived
]
finder_params
[
:search
]
=
params
[
:search
]
if
params
[
:search
]
finder_params
[
:user
]
=
params
.
delete
(
:user
)
if
params
[
:user
]
finder_params
end
...
...
@@ -313,7 +314,7 @@ module API
def
present_artifacts!
(
artifacts_file
)
return
not_found!
unless
artifacts_file
.
exists?
if
artifacts_file
.
file_storage?
present_file!
(
artifacts_file
.
path
,
artifacts_file
.
filename
)
else
...
...
lib/api/projects.rb
View file @
050eae8c
...
...
@@ -36,61 +36,78 @@ module API
params
:statistics_params
do
optional
:statistics
,
type:
Boolean
,
default:
false
,
desc:
'Include project statistics'
end
end
resource
:projects
do
helpers
do
params
:collection_params
do
use
:sort_params
use
:filter_params
use
:pagination
optional
:simple
,
type:
Boolean
,
default:
false
,
desc:
'Return only the ID, URL, name, and path of each project'
end
params
:collection_params
do
use
:sort_params
use
:filter_params
use
:pagination
params
:sort_params
do
optional
:order_by
,
type:
String
,
values:
%w[id name path created_at updated_at last_activity_at]
,
default:
'created_at'
,
desc:
'Return projects ordered by field'
optional
:sort
,
type:
String
,
values:
%w[asc desc]
,
default:
'desc'
,
desc:
'Return projects sorted in ascending and descending order'
end
optional
:simple
,
type:
Boolean
,
default:
false
,
desc:
'Return only the ID, URL, name, and path of each project'
end
params
:filter_params
do
optional
:archived
,
type:
Boolean
,
default:
false
,
desc:
'Limit by archived status'
optional
:visibility
,
type:
String
,
values:
Gitlab
::
VisibilityLevel
.
string_values
,
desc:
'Limit by visibility'
optional
:search
,
type:
String
,
desc:
'Return list of projects matching the search criteria'
optional
:owned
,
type:
Boolean
,
default:
false
,
desc:
'Limit by owned by authenticated user'
optional
:starred
,
type:
Boolean
,
default:
false
,
desc:
'Limit by starred status'
optional
:membership
,
type:
Boolean
,
default:
false
,
desc:
'Limit by projects that the current user is a member of'
optional
:with_issues_enabled
,
type:
Boolean
,
default:
false
,
desc:
'Limit by enabled issues feature'
optional
:with_merge_requests_enabled
,
type:
Boolean
,
default:
false
,
desc:
'Limit by enabled merge requests feature'
end
params
:sort_params
do
optional
:order_by
,
type:
String
,
values:
%w[id name path created_at updated_at last_activity_at]
,
default:
'created_at'
,
desc:
'Return projects ordered by field'
optional
:sort
,
type:
String
,
values:
%w[asc desc]
,
default:
'desc'
,
desc:
'Return projects sorted in ascending and descending order'
end
params
:create_params
do
optional
:namespace_id
,
type:
Integer
,
desc:
'Namespace ID for the new project. Default to the user namespace.'
optional
:import_url
,
type:
String
,
desc:
'URL from which the project is imported'
end
params
:filter_params
do
optional
:archived
,
type:
Boolean
,
default:
false
,
desc:
'Limit by archived status'
optional
:visibility
,
type:
String
,
values:
Gitlab
::
VisibilityLevel
.
string_values
,
desc:
'Limit by visibility'
optional
:search
,
type:
String
,
desc:
'Return list of projects matching the search criteria'
optional
:owned
,
type:
Boolean
,
default:
false
,
desc:
'Limit by owned by authenticated user'
optional
:starred
,
type:
Boolean
,
default:
false
,
desc:
'Limit by starred status'
optional
:membership
,
type:
Boolean
,
default:
false
,
desc:
'Limit by projects that the current user is a member of'
optional
:with_issues_enabled
,
type:
Boolean
,
default:
false
,
desc:
'Limit by enabled issues feature'
optional
:with_merge_requests_enabled
,
type:
Boolean
,
default:
false
,
desc:
'Limit by enabled merge requests feature'
end
def
present_projects
(
options
=
{})
projects
=
ProjectsFinder
.
new
(
current_user:
current_user
,
params:
project_finder_params
).
execute
projects
=
reorder_projects
(
projects
)
projects
=
projects
.
with_statistics
if
params
[
:statistics
]
projects
=
projects
.
with_issues_enabled
if
params
[
:with_issues_enabled
]
projects
=
projects
.
with_merge_requests_enabled
if
params
[
:with_merge_requests_enabled
]
options
=
options
.
reverse_merge
(
with:
current_user
?
Entities
::
ProjectWithAccess
:
Entities
::
BasicProjectDetails
,
statistics:
params
[
:statistics
],
current_user:
current_user
)
options
[
:with
]
=
Entities
::
BasicProjectDetails
if
params
[
:simple
]
present
paginate
(
projects
),
options
end
params
:create_params
do
optional
:namespace_id
,
type:
Integer
,
desc:
'Namespace ID for the new project. Default to the user namespace.'
optional
:import_url
,
type:
String
,
desc:
'URL from which the project is imported'
end
def
present_projects
(
options
=
{})
projects
=
ProjectsFinder
.
new
(
current_user:
current_user
,
params:
project_finder_params
).
execute
projects
=
reorder_projects
(
projects
)
projects
=
projects
.
with_statistics
if
params
[
:statistics
]
projects
=
projects
.
with_issues_enabled
if
params
[
:with_issues_enabled
]
projects
=
projects
.
with_merge_requests_enabled
if
params
[
:with_merge_requests_enabled
]
options
=
options
.
reverse_merge
(
with:
current_user
?
Entities
::
ProjectWithAccess
:
Entities
::
BasicProjectDetails
,
statistics:
params
[
:statistics
],
current_user:
current_user
)
options
[
:with
]
=
Entities
::
BasicProjectDetails
if
params
[
:simple
]
present
paginate
(
projects
),
options
end
end
resource
:users
,
requirements:
{
user_id:
%r{[^/]+}
}
do
desc
'Get a user projects'
do
success
Entities
::
BasicProjectDetails
end
params
do
requires
:user_id
,
type:
String
,
desc:
'The ID or username of the user'
use
:collection_params
use
:statistics_params
end
get
":user_id/projects"
do
user
=
find_user
(
params
[
:user_id
])
not_found!
(
'User'
)
unless
user
params
[
:user
]
=
user
present_projects
end
end
resource
:projects
do
desc
'Get a list of visible projects for authenticated user'
do
success
Entities
::
BasicProjectDetails
end
...
...
spec/requests/api/projects_spec.rb
View file @
050eae8c
...
...
@@ -476,6 +476,26 @@ describe API::Projects do
end
end
describe
'GET /users/:user_id/projects/'
do
let!
(
:public_project
)
{
create
(
:empty_project
,
:public
,
name:
'public_project'
,
creator_id:
user4
.
id
,
namespace:
user4
.
namespace
)
}
it
'returns error when user not found'
do
get
api
(
'/users/9999/projects/'
)
expect
(
response
).
to
have_http_status
(
404
)
expect
(
json_response
[
'message'
]).
to
eq
(
'404 User Not Found'
)
end
it
'returns projects filtered by user'
do
get
api
(
"/users/
#{
user4
.
id
}
/projects/"
,
user
)
expect
(
response
).
to
have_http_status
(
200
)
expect
(
response
).
to
include_pagination_headers
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
map
{
|
project
|
project
[
'id'
]
}).
to
contain_exactly
(
public_project
.
id
)
end
end
describe
'POST /projects/user/:id'
do
before
do
expect
(
project
).
to
be_persisted
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment