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
87b05b06
Commit
87b05b06
authored
Jun 23, 2021
by
Frédéric Caplette
Committed by
Sarah Groff Hennigh-Palermo
Jun 23, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update Pipeline editor getBlobContent to use graphQL
parent
7705f554
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
94 additions
and
132 deletions
+94
-132
app/assets/javascripts/pipeline_editor/graphql/queries/blob_content.graphql
...ipts/pipeline_editor/graphql/queries/blob_content.graphql
+9
-3
app/assets/javascripts/pipeline_editor/graphql/resolvers.js
app/assets/javascripts/pipeline_editor/graphql/resolvers.js
+0
-11
app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
...ssets/javascripts/pipeline_editor/pipeline_editor_app.vue
+28
-27
spec/frontend/pipeline_editor/graphql/resolvers_spec.js
spec/frontend/pipeline_editor/graphql/resolvers_spec.js
+1
-38
spec/frontend/pipeline_editor/mock_data.js
spec/frontend/pipeline_editor/mock_data.js
+17
-0
spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+39
-53
No files found.
app/assets/javascripts/pipeline_editor/graphql/queries/blob_content.graphql
View file @
87b05b06
query
getBlobContent
(
$projectPath
:
ID
!,
$path
:
String
,
$ref
:
String
!)
{
query
getBlobContent
(
$projectPath
:
ID
!,
$path
:
String
!,
$ref
:
String
)
{
blobContent
(
projectPath
:
$projectPath
,
path
:
$path
,
ref
:
$ref
)
@client
{
project
(
fullPath
:
$projectPath
)
{
rawData
repository
{
blobs
(
paths
:
[
$path
],
ref
:
$ref
)
{
nodes
{
rawBlob
}
}
}
}
}
}
}
app/assets/javascripts/pipeline_editor/graphql/resolvers.js
View file @
87b05b06
import
produce
from
'
immer
'
;
import
produce
from
'
immer
'
;
import
Api
from
'
~/api
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
getCurrentBranchQuery
from
'
./queries/client/current_branch.graphql
'
;
import
getCurrentBranchQuery
from
'
./queries/client/current_branch.graphql
'
;
import
getLastCommitBranchQuery
from
'
./queries/client/last_commit_branch.query.graphql
'
;
import
getLastCommitBranchQuery
from
'
./queries/client/last_commit_branch.query.graphql
'
;
export
const
resolvers
=
{
export
const
resolvers
=
{
Query
:
{
blobContent
(
_
,
{
projectPath
,
path
,
ref
})
{
return
{
__typename
:
'
BlobContent
'
,
rawData
:
Api
.
getRawFile
(
projectPath
,
path
,
{
ref
}).
then
(({
data
})
=>
{
return
data
;
}),
};
},
},
Mutation
:
{
Mutation
:
{
lintCI
:
(
_
,
{
endpoint
,
content
,
dry_run
})
=>
{
lintCI
:
(
_
,
{
endpoint
,
content
,
dry_run
})
=>
{
return
axios
.
post
(
endpoint
,
{
content
,
dry_run
}).
then
(({
data
})
=>
({
return
axios
.
post
(
endpoint
,
{
content
,
dry_run
}).
then
(({
data
})
=>
({
...
...
app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
View file @
87b05b06
<
script
>
<
script
>
import
{
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
{
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
{
fetchPolicies
}
from
'
~/lib/graphql
'
;
import
{
fetchPolicies
}
from
'
~/lib/graphql
'
;
import
httpStatusCodes
from
'
~/lib/utils/http_status
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
unwrapStagesWithNeeds
}
from
'
~/pipelines/components/unwrapping_utils
'
;
import
{
unwrapStagesWithNeeds
}
from
'
~/pipelines/components/unwrapping_utils
'
;
...
@@ -76,22 +75,40 @@ export default {
...
@@ -76,22 +75,40 @@ export default {
};
};
},
},
update
(
data
)
{
update
(
data
)
{
return
data
?.
blobContent
?.
rawData
;
return
data
?.
project
?.
repository
?.
blobs
?.
nodes
[
0
]?.
rawBlob
;
},
},
result
({
data
})
{
result
({
data
})
{
const
fileContent
=
data
?.
blobContent
?.
rawData
??
''
;
const
nodes
=
data
?.
project
?.
repository
?.
blobs
?.
nodes
;
if
(
!
nodes
)
{
this
.
reportFailure
(
LOAD_FAILURE_UNKNOWN
);
}
else
{
const
rawBlob
=
nodes
[
0
]?.
rawBlob
;
const
fileContent
=
rawBlob
??
''
;
this
.
lastCommittedContent
=
fileContent
;
this
.
lastCommittedContent
=
fileContent
;
this
.
currentCiFileContent
=
fileContent
;
this
.
currentCiFileContent
=
fileContent
;
// make sure to reset the start screen flag during a refetch
// If rawBlob is defined and returns a string, it means that there is
// e.g. when switching branches
// a CI config file with empty content. If `rawBlob` is not defined
if
(
fileContent
.
length
)
{
// at all, it means there was no file found.
this
.
showStartScreen
=
false
;
const
hasCIFile
=
rawBlob
===
''
||
fileContent
.
length
>
0
;
if
(
!
fileContent
.
length
)
{
this
.
setAppStatus
(
EDITOR_APP_STATUS_EMPTY
);
}
if
(
!
hasCIFile
)
{
this
.
showStartScreen
=
true
;
}
else
if
(
fileContent
.
length
)
{
// If the file content is > 0, then we make sure to reset the
// start screen flag during a refetch
// e.g. when switching branches
this
.
showStartScreen
=
false
;
}
}
}
},
},
error
(
error
)
{
error
()
{
this
.
handleBlobContentError
(
error
);
this
.
reportFailure
(
LOAD_FAILURE_UNKNOWN
);
},
},
watchLoading
(
isLoading
)
{
watchLoading
(
isLoading
)
{
if
(
isLoading
)
{
if
(
isLoading
)
{
...
@@ -187,22 +204,6 @@ export default {
...
@@ -187,22 +204,6 @@ export default {
},
},
},
},
methods
:
{
methods
:
{
handleBlobContentError
(
error
=
{})
{
const
{
networkError
}
=
error
;
const
{
response
}
=
networkError
;
// 404 for missing CI file
// 400 for blank projects with no repository
if
(
response
?.
status
===
httpStatusCodes
.
NOT_FOUND
||
response
?.
status
===
httpStatusCodes
.
BAD_REQUEST
)
{
this
.
setAppStatus
(
EDITOR_APP_STATUS_EMPTY
);
this
.
showStartScreen
=
true
;
}
else
{
this
.
reportFailure
(
LOAD_FAILURE_UNKNOWN
);
}
},
hideFailure
()
{
hideFailure
()
{
this
.
showFailure
=
false
;
this
.
showFailure
=
false
;
},
},
...
...
spec/frontend/pipeline_editor/graphql/resolvers_spec.js
View file @
87b05b06
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
Api
from
'
~/api
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
httpStatus
from
'
~/lib/utils/http_status
'
;
import
httpStatus
from
'
~/lib/utils/http_status
'
;
import
{
resolvers
}
from
'
~/pipeline_editor/graphql/resolvers
'
;
import
{
resolvers
}
from
'
~/pipeline_editor/graphql/resolvers
'
;
import
{
import
{
mockLintResponse
}
from
'
../mock_data
'
;
mockCiConfigPath
,
mockCiYml
,
mockDefaultBranch
,
mockLintResponse
,
mockProjectFullPath
,
}
from
'
../mock_data
'
;
jest
.
mock
(
'
~/api
'
,
()
=>
{
jest
.
mock
(
'
~/api
'
,
()
=>
{
return
{
return
{
...
@@ -18,36 +11,6 @@ jest.mock('~/api', () => {
...
@@ -18,36 +11,6 @@ jest.mock('~/api', () => {
});
});
describe
(
'
~/pipeline_editor/graphql/resolvers
'
,
()
=>
{
describe
(
'
~/pipeline_editor/graphql/resolvers
'
,
()
=>
{
describe
(
'
Query
'
,
()
=>
{
describe
(
'
blobContent
'
,
()
=>
{
beforeEach
(()
=>
{
Api
.
getRawFile
.
mockResolvedValue
({
data
:
mockCiYml
,
});
});
afterEach
(()
=>
{
Api
.
getRawFile
.
mockReset
();
});
it
(
'
resolves lint data with type names
'
,
async
()
=>
{
const
result
=
resolvers
.
Query
.
blobContent
(
null
,
{
projectPath
:
mockProjectFullPath
,
path
:
mockCiConfigPath
,
ref
:
mockDefaultBranch
,
});
expect
(
Api
.
getRawFile
).
toHaveBeenCalledWith
(
mockProjectFullPath
,
mockCiConfigPath
,
{
ref
:
mockDefaultBranch
,
});
// eslint-disable-next-line no-underscore-dangle
expect
(
result
.
__typename
).
toBe
(
'
BlobContent
'
);
await
expect
(
result
.
rawData
).
resolves
.
toBe
(
mockCiYml
);
});
});
});
describe
(
'
Mutation
'
,
()
=>
{
describe
(
'
Mutation
'
,
()
=>
{
describe
(
'
lintCI
'
,
()
=>
{
describe
(
'
lintCI
'
,
()
=>
{
let
mock
;
let
mock
;
...
...
spec/frontend/pipeline_editor/mock_data.js
View file @
87b05b06
...
@@ -35,6 +35,23 @@ job_build:
...
@@ -35,6 +35,23 @@ job_build:
- echo "build"
- echo "build"
needs: ["job_test_2"]
needs: ["job_test_2"]
`
;
`
;
export
const
mockBlobContentQueryResponse
=
{
data
:
{
project
:
{
repository
:
{
blobs
:
{
nodes
:
[{
rawBlob
:
mockCiYml
}]
}
}
},
},
};
export
const
mockBlobContentQueryResponseNoCiFile
=
{
data
:
{
project
:
{
repository
:
{
blobs
:
{
nodes
:
[]
}
}
},
},
};
export
const
mockBlobContentQueryResponseEmptyCiFile
=
{
data
:
{
project
:
{
repository
:
{
blobs
:
{
nodes
:
[{
rawBlob
:
''
}]
}
}
},
},
};
const
mockJobFields
=
{
const
mockJobFields
=
{
beforeScript
:
[],
beforeScript
:
[],
...
...
spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
View file @
87b05b06
...
@@ -3,7 +3,6 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
...
@@ -3,7 +3,6 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import
VueApollo
from
'
vue-apollo
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
httpStatusCodes
from
'
~/lib/utils/http_status
'
;
import
CommitForm
from
'
~/pipeline_editor/components/commit/commit_form.vue
'
;
import
CommitForm
from
'
~/pipeline_editor/components/commit/commit_form.vue
'
;
import
TextEditor
from
'
~/pipeline_editor/components/editor/text_editor.vue
'
;
import
TextEditor
from
'
~/pipeline_editor/components/editor/text_editor.vue
'
;
...
@@ -11,15 +10,19 @@ import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tab
...
@@ -11,15 +10,19 @@ import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tab
import
PipelineEditorEmptyState
from
'
~/pipeline_editor/components/ui/pipeline_editor_empty_state.vue
'
;
import
PipelineEditorEmptyState
from
'
~/pipeline_editor/components/ui/pipeline_editor_empty_state.vue
'
;
import
PipelineEditorMessages
from
'
~/pipeline_editor/components/ui/pipeline_editor_messages.vue
'
;
import
PipelineEditorMessages
from
'
~/pipeline_editor/components/ui/pipeline_editor_messages.vue
'
;
import
{
COMMIT_SUCCESS
,
COMMIT_FAILURE
}
from
'
~/pipeline_editor/constants
'
;
import
{
COMMIT_SUCCESS
,
COMMIT_FAILURE
}
from
'
~/pipeline_editor/constants
'
;
import
getBlobContent
from
'
~/pipeline_editor/graphql/queries/blob_content.graphql
'
;
import
getCiConfigData
from
'
~/pipeline_editor/graphql/queries/ci_config.graphql
'
;
import
getCiConfigData
from
'
~/pipeline_editor/graphql/queries/ci_config.graphql
'
;
import
PipelineEditorApp
from
'
~/pipeline_editor/pipeline_editor_app.vue
'
;
import
PipelineEditorApp
from
'
~/pipeline_editor/pipeline_editor_app.vue
'
;
import
PipelineEditorHome
from
'
~/pipeline_editor/pipeline_editor_home.vue
'
;
import
PipelineEditorHome
from
'
~/pipeline_editor/pipeline_editor_home.vue
'
;
import
{
import
{
mockCiConfigPath
,
mockCiConfigPath
,
mockCiConfigQueryResponse
,
mockCiConfigQueryResponse
,
mockCiYml
,
mockBlobContentQueryResponse
,
mockBlobContentQueryResponseEmptyCiFile
,
mockBlobContentQueryResponseNoCiFile
,
mockDefaultBranch
,
mockDefaultBranch
,
mockProjectFullPath
,
mockProjectFullPath
,
mockCiYml
,
}
from
'
./mock_data
'
;
}
from
'
./mock_data
'
;
const
localVue
=
createLocalVue
();
const
localVue
=
createLocalVue
();
...
@@ -75,19 +78,12 @@ describe('Pipeline editor app component', () => {
...
@@ -75,19 +78,12 @@ describe('Pipeline editor app component', () => {
};
};
const
createComponentWithApollo
=
async
({
props
=
{},
provide
=
{}
}
=
{})
=>
{
const
createComponentWithApollo
=
async
({
props
=
{},
provide
=
{}
}
=
{})
=>
{
const
handlers
=
[[
getCiConfigData
,
mockCiConfigData
]];
const
handlers
=
[
const
resolvers
=
{
[
getBlobContent
,
mockBlobContentData
],
Query
:
{
[
getCiConfigData
,
mockCiConfigData
],
blobContent
()
{
];
return
{
__typename
:
'
BlobContent
'
,
rawData
:
mockBlobContentData
(),
};
},
},
};
mockApollo
=
createMockApollo
(
handlers
,
resolvers
);
mockApollo
=
createMockApollo
(
handlers
);
const
options
=
{
const
options
=
{
localVue
,
localVue
,
...
@@ -133,7 +129,7 @@ describe('Pipeline editor app component', () => {
...
@@ -133,7 +129,7 @@ describe('Pipeline editor app component', () => {
describe
(
'
when queries are called
'
,
()
=>
{
describe
(
'
when queries are called
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mockBlobContentData
.
mockResolvedValue
(
mock
CiYml
);
mockBlobContentData
.
mockResolvedValue
(
mock
BlobContentQueryResponse
);
mockCiConfigData
.
mockResolvedValue
(
mockCiConfigQueryResponse
);
mockCiConfigData
.
mockResolvedValue
(
mockCiConfigQueryResponse
);
});
});
...
@@ -159,34 +155,13 @@ describe('Pipeline editor app component', () => {
...
@@ -159,34 +155,13 @@ describe('Pipeline editor app component', () => {
});
});
describe
(
'
when no CI config file exists
'
,
()
=>
{
describe
(
'
when no CI config file exists
'
,
()
=>
{
describe
(
'
in a project without a repository
'
,
()
=>
{
it
(
'
shows an empty state and does not show editor home component
'
,
async
()
=>
{
it
(
'
shows an empty state and does not show editor home component
'
,
async
()
=>
{
mockBlobContentData
.
mockResolvedValue
(
mockBlobContentQueryResponseNoCiFile
);
mockBlobContentData
.
mockRejectedValueOnce
({
await
createComponentWithApollo
();
response
:
{
status
:
httpStatusCodes
.
BAD_REQUEST
,
},
});
await
createComponentWithApollo
();
expect
(
findEmptyState
().
exists
()).
toBe
(
true
);
expect
(
findAlert
().
exists
()).
toBe
(
false
);
expect
(
findEditorHome
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
in a project with a repository
'
,
()
=>
{
it
(
'
shows an empty state and does not show editor home component
'
,
async
()
=>
{
mockBlobContentData
.
mockRejectedValueOnce
({
response
:
{
status
:
httpStatusCodes
.
NOT_FOUND
,
},
});
await
createComponentWithApollo
();
expect
(
findEmptyState
().
exists
()).
toBe
(
true
);
expect
(
findEmptyState
().
exists
()).
toBe
(
true
);
expect
(
findAlert
().
exists
()).
toBe
(
false
);
expect
(
findAlert
().
exists
()).
toBe
(
false
);
expect
(
findEditorHome
().
exists
()).
toBe
(
false
);
expect
(
findEditorHome
().
exists
()).
toBe
(
false
);
});
});
});
describe
(
'
because of a fetching error
'
,
()
=>
{
describe
(
'
because of a fetching error
'
,
()
=>
{
...
@@ -204,13 +179,28 @@ describe('Pipeline editor app component', () => {
...
@@ -204,13 +179,28 @@ describe('Pipeline editor app component', () => {
});
});
});
});
describe
(
'
with an empty CI config file
'
,
()
=>
{
describe
(
'
with empty state feature flag on
'
,
()
=>
{
it
(
'
does not show the empty screen state
'
,
async
()
=>
{
mockBlobContentData
.
mockResolvedValue
(
mockBlobContentQueryResponseEmptyCiFile
);
await
createComponentWithApollo
({
provide
:
{
glFeatures
:
{
pipelineEditorEmptyStateAction
:
true
,
},
},
});
expect
(
findEmptyState
().
exists
()).
toBe
(
false
);
expect
(
findTextEditor
().
exists
()).
toBe
(
true
);
});
});
});
describe
(
'
when landing on the empty state with feature flag on
'
,
()
=>
{
describe
(
'
when landing on the empty state with feature flag on
'
,
()
=>
{
it
(
'
user can click on CTA button and see an empty editor
'
,
async
()
=>
{
it
(
'
user can click on CTA button and see an empty editor
'
,
async
()
=>
{
mockBlobContentData
.
mockRejectedValueOnce
({
mockBlobContentData
.
mockResolvedValue
(
mockBlobContentQueryResponseNoCiFile
);
response
:
{
status
:
httpStatusCodes
.
NOT_FOUND
,
},
});
await
createComponentWithApollo
({
await
createComponentWithApollo
({
provide
:
{
provide
:
{
...
@@ -315,17 +305,13 @@ describe('Pipeline editor app component', () => {
...
@@ -315,17 +305,13 @@ describe('Pipeline editor app component', () => {
});
});
it
(
'
hides start screen when refetch fetches CI file
'
,
async
()
=>
{
it
(
'
hides start screen when refetch fetches CI file
'
,
async
()
=>
{
mockBlobContentData
.
mockRejectedValue
({
mockBlobContentData
.
mockResolvedValue
(
mockBlobContentQueryResponseNoCiFile
);
response
:
{
status
:
httpStatusCodes
.
NOT_FOUND
,
},
});
await
createComponentWithApollo
();
await
createComponentWithApollo
();
expect
(
findEmptyState
().
exists
()).
toBe
(
true
);
expect
(
findEmptyState
().
exists
()).
toBe
(
true
);
expect
(
findEditorHome
().
exists
()).
toBe
(
false
);
expect
(
findEditorHome
().
exists
()).
toBe
(
false
);
mockBlobContentData
.
mockResolvedValue
(
mock
CiYml
);
mockBlobContentData
.
mockResolvedValue
(
mock
BlobContentQueryResponse
);
await
wrapper
.
vm
.
$apollo
.
queries
.
initialCiFileContent
.
refetch
();
await
wrapper
.
vm
.
$apollo
.
queries
.
initialCiFileContent
.
refetch
();
expect
(
findEmptyState
().
exists
()).
toBe
(
false
);
expect
(
findEmptyState
().
exists
()).
toBe
(
false
);
...
...
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