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
96db744c
Commit
96db744c
authored
Nov 08, 2021
by
Illya Klymov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement separate status for bulk imports
- return separate status for each top level group Changelog: added
parent
e4d749f0
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
89 additions
and
42 deletions
+89
-42
app/assets/javascripts/import_entities/import_groups/components/import_table.vue
...import_entities/import_groups/components/import_table.vue
+1
-2
app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue
..._entities/import_groups/components/import_target_cell.vue
+7
-4
app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js
...s/import_entities/import_groups/graphql/client_factory.js
+6
-6
app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_progress.fragment.graphql
...gments/bulk_import_source_group_progress.fragment.graphql
+1
-0
app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql
...t_groups/graphql/mutations/import_groups.mutation.graphql
+1
-0
app/assets/javascripts/import_entities/import_groups/graphql/services/local_storage_cache.js
...ies/import_groups/graphql/services/local_storage_cache.js
+8
-1
app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql
...ts/import_entities/import_groups/graphql/typedefs.graphql
+1
-0
app/controllers/import/bulk_imports_controller.rb
app/controllers/import/bulk_imports_controller.rb
+2
-6
spec/controllers/import/bulk_imports_controller_spec.rb
spec/controllers/import/bulk_imports_controller_spec.rb
+15
-17
spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js
...ities/import_groups/components/import_target_cell_spec.js
+10
-1
spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js
...ort_entities/import_groups/graphql/client_factory_spec.js
+35
-4
spec/frontend/import_entities/import_groups/graphql/fixtures.js
...rontend/import_entities/import_groups/graphql/fixtures.js
+2
-1
No files found.
app/assets/javascripts/import_entities/import_groups/components/import_table.vue
View file @
96db744c
...
@@ -314,9 +314,8 @@ export default {
...
@@ -314,9 +314,8 @@ export default {
variables
:
{
importRequests
},
variables
:
{
importRequests
},
});
});
}
catch
(
error
)
{
}
catch
(
error
)
{
const
message
=
error
?.
networkError
?.
response
?.
data
?.
error
??
i18n
.
ERROR_IMPORT
;
createFlash
({
createFlash
({
message
,
message
:
i18n
.
ERROR_IMPORT
,
captureError
:
true
,
captureError
:
true
,
error
,
error
,
});
});
...
...
app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue
View file @
96db744c
...
@@ -32,8 +32,10 @@ export default {
...
@@ -32,8 +32,10 @@ export default {
fullPath
()
{
fullPath
()
{
return
this
.
group
.
importTarget
.
targetNamespace
.
fullPath
||
s__
(
'
BulkImport|No parent
'
);
return
this
.
group
.
importTarget
.
targetNamespace
.
fullPath
||
s__
(
'
BulkImport|No parent
'
);
},
},
invalidNameValidationMessage
()
{
validationMessage
()
{
return
getInvalidNameValidationMessage
(
this
.
group
.
importTarget
);
return
(
this
.
group
.
progress
?.
message
||
getInvalidNameValidationMessage
(
this
.
group
.
importTarget
)
);
},
},
},
},
};
};
...
@@ -93,10 +95,11 @@ export default {
...
@@ -93,10 +95,11 @@ export default {
@
input=
"$emit('update-new-name', $event)"
@
input=
"$emit('update-new-name', $event)"
/>
/>
<p
<p
v-if=
"group.flags.isAvailableForImport &&
group.flags.isInvalid
"
v-if=
"group.flags.isAvailableForImport &&
(group.flags.isInvalid || validationMessage)
"
class=
"gl-text-red-500 gl-m-0 gl-mt-2"
class=
"gl-text-red-500 gl-m-0 gl-mt-2"
role=
"alert"
>
>
{{
invalidNameV
alidationMessage }}
{{
v
alidationMessage }}
</p>
</p>
</div>
</div>
</div>
</div>
...
...
app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js
View file @
96db744c
...
@@ -142,9 +142,7 @@ export function createResolvers({ endpoints }) {
...
@@ -142,9 +142,7 @@ export function createResolvers({ endpoints }) {
};
};
});
});
const
{
const
{
data
:
responses
}
=
await
axios
.
post
(
endpoints
.
createBulkImport
,
{
data
:
{
id
:
jobId
},
}
=
await
axios
.
post
(
endpoints
.
createBulkImport
,
{
bulk_import
:
importOperations
.
map
((
op
)
=>
({
bulk_import
:
importOperations
.
map
((
op
)
=>
({
source_type
:
'
group_entity
'
,
source_type
:
'
group_entity
'
,
source_full_path
:
op
.
group
.
fullPath
,
source_full_path
:
op
.
group
.
fullPath
,
...
@@ -153,15 +151,17 @@ export function createResolvers({ endpoints }) {
...
@@ -153,15 +151,17 @@ export function createResolvers({ endpoints }) {
})),
})),
});
});
return
importOperations
.
map
((
op
)
=>
{
return
importOperations
.
map
((
op
,
idx
)
=>
{
const
response
=
responses
[
idx
];
const
lastImportTarget
=
{
const
lastImportTarget
=
{
targetNamespace
:
op
.
targetNamespace
,
targetNamespace
:
op
.
targetNamespace
,
newName
:
op
.
newName
,
newName
:
op
.
newName
,
};
};
const
progress
=
{
const
progress
=
{
id
:
jobId
,
id
:
response
.
id
||
`local-
${
Date
.
now
()}
-
${
idx
}
`
,
status
:
STATUSES
.
CREATED
,
status
:
response
.
success
?
STATUSES
.
CREATED
:
STATUSES
.
FAILED
,
message
:
response
.
message
||
null
,
};
};
localStorageCache
.
set
(
op
.
group
.
webUrl
,
{
progress
,
lastImportTarget
});
localStorageCache
.
set
(
op
.
group
.
webUrl
,
{
progress
,
lastImportTarget
});
...
...
app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_progress.fragment.graphql
View file @
96db744c
fragment
BulkImportSourceGroupProgress
on
ClientBulkImportProgress
{
fragment
BulkImportSourceGroupProgress
on
ClientBulkImportProgress
{
id
id
status
status
message
}
}
app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql
View file @
96db744c
...
@@ -9,6 +9,7 @@ mutation importGroups($importRequests: [ImportGroupInput!]!) {
...
@@ -9,6 +9,7 @@ mutation importGroups($importRequests: [ImportGroupInput!]!) {
progress
{
progress
{
id
id
status
status
message
}
}
}
}
}
}
app/assets/javascripts/import_entities/import_groups/graphql/services/local_storage_cache.js
View file @
96db744c
...
@@ -22,7 +22,14 @@ export class LocalStorageCache {
...
@@ -22,7 +22,14 @@ export class LocalStorageCache {
loadCacheFromStorage
()
{
loadCacheFromStorage
()
{
try
{
try
{
return
JSON
.
parse
(
this
.
storage
.
getItem
(
KEY
))
??
{};
const
storage
=
JSON
.
parse
(
this
.
storage
.
getItem
(
KEY
))
??
{};
Object
.
values
(
storage
).
forEach
((
entry
)
=>
{
if
(
entry
.
progress
&&
!
(
'
message
'
in
entry
.
progress
))
{
// eslint-disable-next-line no-param-reassign
entry
.
progress
.
message
=
''
;
}
});
return
storage
;
}
catch
{
}
catch
{
return
{};
return
{};
}
}
...
...
app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql
View file @
96db744c
...
@@ -16,6 +16,7 @@ type ClientBulkImportSourceGroupConnection {
...
@@ -16,6 +16,7 @@ type ClientBulkImportSourceGroupConnection {
type
ClientBulkImportProgress
{
type
ClientBulkImportProgress
{
id
:
ID
!
id
:
ID
!
status
:
String
!
status
:
String
!
message
:
String
}
}
type
ClientBulkImportValidationError
{
type
ClientBulkImportValidationError
{
...
...
app/controllers/import/bulk_imports_controller.rb
View file @
96db744c
...
@@ -40,13 +40,9 @@ class Import::BulkImportsController < ApplicationController
...
@@ -40,13 +40,9 @@ class Import::BulkImportsController < ApplicationController
end
end
def
create
def
create
response
=
::
BulkImports
::
CreateService
.
new
(
current_user
,
create_params
,
credentials
).
execute
response
s
=
create_params
.
map
{
|
entry
|
::
BulkImports
::
CreateService
.
new
(
current_user
,
[
entry
],
credentials
).
execute
}
if
response
.
success?
render
json:
responses
.
map
{
|
response
|
{
success:
response
.
success?
,
id:
response
.
payload
[
:id
],
message:
response
.
message
}
}
render
json:
response
.
payload
.
to_json
(
only:
[
:id
])
else
render
json:
{
error:
response
.
message
},
status:
response
.
http_status
end
end
end
def
realtime_changes
def
realtime_changes
...
...
spec/controllers/import/bulk_imports_controller_spec.rb
View file @
96db744c
...
@@ -215,9 +215,13 @@ RSpec.describe Import::BulkImportsController do
...
@@ -215,9 +215,13 @@ RSpec.describe Import::BulkImportsController do
let
(
:pat
)
{
"fake-pat"
}
let
(
:pat
)
{
"fake-pat"
}
let
(
:bulk_import_params
)
do
let
(
:bulk_import_params
)
do
[{
"source_type"
=>
"group_entity"
,
[{
"source_type"
=>
"group_entity"
,
"source_full_path"
=>
"full_path"
,
"source_full_path"
=>
"full_path"
,
"destination_name"
=>
"destination_name"
,
"destination_name"
=>
"destination_name"
,
"destination_namespace"
=>
"root"
}]
"destination_namespace"
=>
"root"
},
{
"source_type"
=>
"group_entity2"
,
"source_full_path"
=>
"full_path2"
,
"destination_name"
=>
"destination_name2"
,
"destination_namespace"
=>
"root"
}]
end
end
before
do
before
do
...
@@ -225,29 +229,23 @@ RSpec.describe Import::BulkImportsController do
...
@@ -225,29 +229,23 @@ RSpec.describe Import::BulkImportsController do
session
[
:bulk_import_gitlab_url
]
=
instance_url
session
[
:bulk_import_gitlab_url
]
=
instance_url
end
end
it
'executes BulkImpors::CreatetService'
do
it
'executes BulkImpors::CreateService'
do
error_response
=
ServiceResponse
.
error
(
message:
'Record invalid'
,
http_status: :unprocessable_entity
)
expect_next_instance_of
(
expect_next_instance_of
(
::
BulkImports
::
CreateService
,
user
,
bulk_import_params
,
{
url:
instance_url
,
access_token:
pat
})
do
|
service
|
::
BulkImports
::
CreateService
,
user
,
[
bulk_import_params
[
0
]]
,
{
url:
instance_url
,
access_token:
pat
})
do
|
service
|
allow
(
service
).
to
receive
(
:execute
).
and_return
(
ServiceResponse
.
success
(
payload:
bulk_import
))
allow
(
service
).
to
receive
(
:execute
).
and_return
(
ServiceResponse
.
success
(
payload:
bulk_import
))
end
end
post
:create
,
params:
{
bulk_import:
bulk_import_params
}
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
.
body
).
to
eq
({
id:
bulk_import
.
id
}.
to_json
)
end
it
'returns error when validation fails'
do
error_response
=
ServiceResponse
.
error
(
message:
'Record invalid'
,
http_status: :unprocessable_entity
)
expect_next_instance_of
(
expect_next_instance_of
(
::
BulkImports
::
CreateService
,
user
,
bulk_import_params
,
{
url:
instance_url
,
access_token:
pat
})
do
|
service
|
::
BulkImports
::
CreateService
,
user
,
[
bulk_import_params
[
1
]]
,
{
url:
instance_url
,
access_token:
pat
})
do
|
service
|
allow
(
service
).
to
receive
(
:execute
).
and_return
(
error_response
)
allow
(
service
).
to
receive
(
:execute
).
and_return
(
error_response
)
end
end
post
:create
,
params:
{
bulk_import:
bulk_import_params
}
post
:create
,
params:
{
bulk_import:
bulk_import_params
}
expect
(
response
).
to
have_gitlab_http_status
(
:unprocessable_entity
)
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
.
body
).
to
eq
({
error:
'Record invalid'
}.
to_json
)
expect
(
json_response
).
to
eq
([{
"success"
=>
true
,
"id"
=>
bulk_import
.
id
,
"message"
=>
nil
},
{
"success"
=>
false
,
"id"
=>
nil
,
"message"
=>
"Record invalid"
}])
end
end
end
end
end
end
...
...
spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js
View file @
96db744c
...
@@ -123,13 +123,22 @@ describe('import target cell', () => {
...
@@ -123,13 +123,22 @@ describe('import target cell', () => {
});
});
describe
(
'
when entity is available for import
'
,
()
=>
{
describe
(
'
when entity is available for import
'
,
()
=>
{
const
FAKE_PROGRESS_MESSAGE
=
'
progress message
'
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
group
=
generateFakeTableEntry
({
id
:
1
,
flags
:
{
isAvailableForImport
:
true
}
});
group
=
generateFakeTableEntry
({
id
:
1
,
flags
:
{
isAvailableForImport
:
true
},
progress
:
{
message
:
FAKE_PROGRESS_MESSAGE
},
});
createComponent
({
group
});
createComponent
({
group
});
});
});
it
(
'
renders namespace dropdown as enabled
'
,
()
=>
{
it
(
'
renders namespace dropdown as enabled
'
,
()
=>
{
expect
(
findNamespaceDropdown
().
attributes
(
'
disabled
'
)).
toBe
(
undefined
);
expect
(
findNamespaceDropdown
().
attributes
(
'
disabled
'
)).
toBe
(
undefined
);
});
});
it
(
'
renders progress message as error if it exists
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
[role=alert]
'
).
text
()).
toBe
(
FAKE_PROGRESS_MESSAGE
);
});
});
});
});
});
spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js
View file @
96db744c
...
@@ -163,12 +163,14 @@ describe('Bulk import resolvers', () => {
...
@@ -163,12 +163,14 @@ describe('Bulk import resolvers', () => {
});
});
describe
(
'
mutations
'
,
()
=>
{
describe
(
'
mutations
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{});
axiosMockAdapter
.
onPost
(
FAKE_ENDPOINTS
.
createBulkImport
).
reply
(
httpStatus
.
OK
,
{
id
:
1
});
});
describe
(
'
importGroup
'
,
()
=>
{
describe
(
'
importGroup
'
,
()
=>
{
it
(
'
sets import status to CREATED when request completes
'
,
async
()
=>
{
it
(
'
sets import status to CREATED for successful groups when request completes
'
,
async
()
=>
{
axiosMockAdapter
.
onPost
(
FAKE_ENDPOINTS
.
createBulkImport
)
.
reply
(
httpStatus
.
OK
,
[{
success
:
true
,
id
:
1
}]);
await
client
.
mutate
({
await
client
.
mutate
({
mutation
:
importGroupsMutation
,
mutation
:
importGroupsMutation
,
variables
:
{
variables
:
{
...
@@ -185,9 +187,37 @@ describe('Bulk import resolvers', () => {
...
@@ -185,9 +187,37 @@ describe('Bulk import resolvers', () => {
await
axios
.
waitForAll
();
await
axios
.
waitForAll
();
expect
(
results
[
0
].
progress
.
status
).
toBe
(
STATUSES
.
CREATED
);
expect
(
results
[
0
].
progress
.
status
).
toBe
(
STATUSES
.
CREATED
);
});
});
it
(
'
sets import status to FAILED and sets progress message for failed groups when request completes
'
,
async
()
=>
{
const
FAKE_ERROR_MESSAGE
=
'
foo
'
;
axiosMockAdapter
.
onPost
(
FAKE_ENDPOINTS
.
createBulkImport
)
.
reply
(
httpStatus
.
OK
,
[{
success
:
false
,
id
:
1
,
message
:
FAKE_ERROR_MESSAGE
}]);
await
client
.
mutate
({
mutation
:
importGroupsMutation
,
variables
:
{
importRequests
:
[
{
sourceGroupId
:
statusEndpointFixture
.
importable_data
[
0
].
id
,
newName
:
'
test
'
,
targetNamespace
:
'
root
'
,
},
],
},
});
await
axios
.
waitForAll
();
expect
(
results
[
0
].
progress
.
status
).
toBe
(
STATUSES
.
FAILED
);
expect
(
results
[
0
].
progress
.
message
).
toBe
(
FAKE_ERROR_MESSAGE
);
});
});
});
it
(
'
updateImportStatus updates status
'
,
async
()
=>
{
it
(
'
updateImportStatus updates status
'
,
async
()
=>
{
axiosMockAdapter
.
onPost
(
FAKE_ENDPOINTS
.
createBulkImport
)
.
reply
(
httpStatus
.
OK
,
[{
success
:
true
,
id
:
1
}]);
const
NEW_STATUS
=
'
dummy
'
;
const
NEW_STATUS
=
'
dummy
'
;
await
client
.
mutate
({
await
client
.
mutate
({
mutation
:
importGroupsMutation
,
mutation
:
importGroupsMutation
,
...
@@ -216,6 +246,7 @@ describe('Bulk import resolvers', () => {
...
@@ -216,6 +246,7 @@ describe('Bulk import resolvers', () => {
expect
(
statusInResponse
).
toStrictEqual
({
expect
(
statusInResponse
).
toStrictEqual
({
__typename
:
clientTypenames
.
BulkImportProgress
,
__typename
:
clientTypenames
.
BulkImportProgress
,
id
,
id
,
message
:
null
,
status
:
NEW_STATUS
,
status
:
NEW_STATUS
,
});
});
});
});
...
...
spec/frontend/import_entities/import_groups/graphql/fixtures.js
View file @
96db744c
import
{
STATUSES
}
from
'
~/import_entities/constants
'
;
import
{
STATUSES
}
from
'
~/import_entities/constants
'
;
import
{
clientTypenames
}
from
'
~/import_entities/import_groups/graphql/client_factory
'
;
import
{
clientTypenames
}
from
'
~/import_entities/import_groups/graphql/client_factory
'
;
export
const
generateFakeEntry
=
({
id
,
status
,
...
rest
})
=>
({
export
const
generateFakeEntry
=
({
id
,
status
,
message
,
...
rest
})
=>
({
__typename
:
clientTypenames
.
BulkImportSourceGroup
,
__typename
:
clientTypenames
.
BulkImportSourceGroup
,
webUrl
:
`https://fake.host/
${
id
}
`
,
webUrl
:
`https://fake.host/
${
id
}
`
,
fullPath
:
`fake_group_
${
id
}
`
,
fullPath
:
`fake_group_
${
id
}
`
,
...
@@ -18,6 +18,7 @@ export const generateFakeEntry = ({ id, status, ...rest }) => ({
...
@@ -18,6 +18,7 @@ export const generateFakeEntry = ({ id, status, ...rest }) => ({
:
{
:
{
id
,
id
,
status
,
status
,
message
:
message
||
''
,
},
},
...
rest
,
...
rest
,
});
});
...
...
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