diff --git a/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue b/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue index fbc7ddf5c9119a9d42f4c415c063eecf90c69236..91aaf2237b74e165fa8ac1726385dd46df729469 100644 --- a/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue +++ b/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue @@ -12,7 +12,7 @@ export default { GlTableLite, }, props: { - resourceId: { + resourcePath: { type: String, required: true, }, @@ -27,7 +27,7 @@ export default { query: getCiCatalogResourceComponents, variables() { return { - id: this.resourceId, + fullPath: this.resourcePath, }; }, update(data) { diff --git a/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue b/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue index 026a30988fdfbf07832d6cd366075377a4db8163..b1170b13ef6c974d2ada6856596f330945166bb3 100644 --- a/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue +++ b/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue @@ -14,7 +14,7 @@ export default { }, mixins: [glFeatureFlagsMixin()], props: { - resourceId: { + resourcePath: { type: String, required: true, }, @@ -31,10 +31,10 @@ export default { <template> <gl-tabs> <gl-tab :title="$options.i18n.tabs.readme" lazy> - <ci-resource-readme :resource-id="resourceId" /> + <ci-resource-readme :resource-path="resourcePath" /> </gl-tab> <gl-tab v-if="glFeatures.ciCatalogComponentsTab" :title="$options.i18n.tabs.components" lazy> - <ci-resource-components :resource-id="resourceId" + <ci-resource-components :resource-path="resourcePath" /></gl-tab> </gl-tabs> </template> diff --git a/app/assets/javascripts/ci/catalog/components/details/ci_resource_readme.vue b/app/assets/javascripts/ci/catalog/components/details/ci_resource_readme.vue index d473833869d517c8bc21f918c3108d7be95a537c..343b555c4d8518c344834fc0297eb2c8447f3240 100644 --- a/app/assets/javascripts/ci/catalog/components/details/ci_resource_readme.vue +++ b/app/assets/javascripts/ci/catalog/components/details/ci_resource_readme.vue @@ -11,7 +11,7 @@ export default { }, directives: { SafeHtml }, props: { - resourceId: { + resourcePath: { type: String, required: true, }, @@ -26,7 +26,7 @@ export default { query: getCiCatalogResourceReadme, variables() { return { - id: this.resourceId, + fullPath: this.resourcePath, }; }, update(data) { diff --git a/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue b/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue index 6675118db59ec5668fc8c642cfd7b75ed5cf72d1..57d19af614f7a6c9d79188e5f9d4d2c094fb9786 100644 --- a/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue +++ b/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue @@ -34,11 +34,17 @@ export default { authorProfileUrl() { return this.latestVersion.author.webUrl; }, - detailsPageHref() { + resourceId() { + return cleanLeadingSeparator(this.resource.webPath); + }, + detailsPageResolved() { return this.$router.resolve({ name: CI_RESOURCE_DETAILS_PAGE_NAME, - params: { id: this.entityId }, - }).href; + params: { id: this.resourceId }, + }); + }, + detailsPageHref() { + return decodeURIComponent(this.detailsPageResolved.href); }, entityId() { return getIdFromGraphQLId(this.resource.id); @@ -79,10 +85,8 @@ export default { // open a new tab. e.preventDefault(); - this.$router.push({ - name: CI_RESOURCE_DETAILS_PAGE_NAME, - params: { id: this.entityId }, - }); + // Push to the decoded URL to avoid all the / being encoded + this.$router.push({ path: decodeURIComponent(this.resourceId) }); }, }, }; diff --git a/app/assets/javascripts/ci/catalog/components/pages/ci_resource_details_page.vue b/app/assets/javascripts/ci/catalog/components/pages/ci_resource_details_page.vue index da2c73be900bc29914f1e6ebfd3151dfe3f84f53..b7e117f9c260b42fbc498bcaf477e9ccd2f36b81 100644 --- a/app/assets/javascripts/ci/catalog/components/pages/ci_resource_details_page.vue +++ b/app/assets/javascripts/ci/catalog/components/pages/ci_resource_details_page.vue @@ -2,8 +2,7 @@ import { GlEmptyState } from '@gitlab/ui'; import { s__ } from '~/locale'; import { createAlert } from '~/alert'; -import { convertToGraphQLId } from '~/graphql_shared/utils'; -import { CI_CATALOG_RESOURCE_TYPE } from '../../graphql/settings'; +import { cleanLeadingSeparator } from '~/lib/utils/url_utility'; import getCatalogCiResourceDetails from '../../graphql/queries/get_ci_catalog_resource_details.query.graphql'; import getCatalogCiResourceSharedData from '../../graphql/queries/get_ci_catalog_resource_shared_data.query.graphql'; import CiResourceDetails from '../details/ci_resource_details.vue'; @@ -28,7 +27,7 @@ export default { query: getCatalogCiResourceSharedData, variables() { return { - id: this.graphQLId, + fullPath: this.cleanFullPath, }; }, update(data) { @@ -43,7 +42,7 @@ export default { query: getCatalogCiResourceDetails, variables() { return { - id: this.graphQLId, + fullPath: this.cleanFullPath, }; }, update(data) { @@ -56,8 +55,8 @@ export default { }, }, computed: { - graphQLId() { - return convertToGraphQLId(CI_CATALOG_RESOURCE_TYPE, this.$route.params.id); + cleanFullPath() { + return cleanLeadingSeparator(this.$route.params.id); }, isLoadingDetails() { return this.$apollo.queries.resourceAdditionalDetails.loading; @@ -103,7 +102,7 @@ export default { :pipeline-status="pipelineStatus" :resource="resourceSharedData" /> - <ci-resource-details :resource-id="graphQLId" /> + <ci-resource-details :resource-path="cleanFullPath" /> </div> </div> </template> diff --git a/app/assets/javascripts/ci/catalog/graphql/fragments/catalog_resource.fragment.graphql b/app/assets/javascripts/ci/catalog/graphql/fragments/catalog_resource.fragment.graphql index 0dc16e3492df0b0c6a6d645c7d88c71151bbea11..b3a750e9604a2be9245907d6a046340326c08a87 100644 --- a/app/assets/javascripts/ci/catalog/graphql/fragments/catalog_resource.fragment.graphql +++ b/app/assets/javascripts/ci/catalog/graphql/fragments/catalog_resource.fragment.graphql @@ -1,5 +1,6 @@ fragment CatalogResourceFields on CiCatalogResource { id + webPath icon name description @@ -15,5 +16,4 @@ fragment CatalogResourceFields on CiCatalogResource { webUrl } } - webPath } diff --git a/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_components.query.graphql b/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_components.query.graphql index 6aef5dcc4e768afe1c6128f56ac3cb10c2b370ed..92272bd97c501ef5e50cf3bcc9dcf16da20739d6 100644 --- a/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_components.query.graphql +++ b/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_components.query.graphql @@ -1,6 +1,7 @@ -query getCiCatalogResourceComponents($id: CiCatalogResourceID!) { - ciCatalogResource(id: $id) { +query getCiCatalogResourceComponents($fullPath: ID!) { + ciCatalogResource(fullPath: $fullPath) { id + webPath components @client { nodes { id diff --git a/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_details.query.graphql b/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_details.query.graphql index 382d38667953ce9dea74028806dc4ecb8fd38020..a77e8f12d032733d8fd570d2f4488143f0b2b893 100644 --- a/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_details.query.graphql +++ b/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_details.query.graphql @@ -1,6 +1,7 @@ -query getCiCatalogResourceDetails($id: CiCatalogResourceID!) { - ciCatalogResource(id: $id) { +query getCiCatalogResourceDetails($fullPath: ID!) { + ciCatalogResource(fullPath: $fullPath) { id + webPath openIssuesCount openMergeRequestsCount versions(first: 1) { diff --git a/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_readme.query.graphql b/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_readme.query.graphql index 6b3d0cdcfc70e1b9f19565afc1226ca0851c4d83..c1fde8dcb433dc477eb3ec47ccafa72dbc88c175 100644 --- a/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_readme.query.graphql +++ b/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_readme.query.graphql @@ -1,6 +1,7 @@ -query getCiCatalogResourceReadme($id: CiCatalogResourceID!) { - ciCatalogResource(id: $id) { +query getCiCatalogResourceReadme($fullPath: ID!) { + ciCatalogResource(fullPath: $fullPath) { id + webPath readmeHtml } } diff --git a/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_shared_data.query.graphql b/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_shared_data.query.graphql index 4ac4cb0e39474d21857f351823e4aa293be5d3ef..3d5d139a33484a5aa60b5f53ee5a4c3d3fbf3188 100644 --- a/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_shared_data.query.graphql +++ b/app/assets/javascripts/ci/catalog/graphql/queries/get_ci_catalog_resource_shared_data.query.graphql @@ -1,7 +1,7 @@ #import "../fragments/catalog_resource.fragment.graphql" -query getCiCatalogResourceSharedData($id: CiCatalogResourceID!) { - ciCatalogResource(id: $id) { +query getCiCatalogResourceSharedData($fullPath: ID!) { + ciCatalogResource(fullPath: $fullPath) { ...CatalogResourceFields } } diff --git a/app/assets/javascripts/ci/catalog/graphql/settings.js b/app/assets/javascripts/ci/catalog/graphql/settings.js index a87b26ca4fc986ebcf5a43ac9a6abb55b65d7e82..5e71fee818e30959b0524f58640411cb407c6592 100644 --- a/app/assets/javascripts/ci/catalog/graphql/settings.js +++ b/app/assets/javascripts/ci/catalog/graphql/settings.js @@ -11,7 +11,8 @@ export const cacheConfig = { ciCatalogResource(_, { args, toReference }) { return toReference({ __typename: 'CiCatalogResource', - id: args.id, + // Webpath is the fullpath with a leading slash + webPath: `/${args.fullPath}`, }); }, ciCatalogResources: { @@ -19,6 +20,9 @@ export const cacheConfig = { }, }, }, + CiCatalogResource: { + keyFields: ['webPath'], + }, }, }, }; diff --git a/app/assets/javascripts/ci/catalog/index.js b/app/assets/javascripts/ci/catalog/index.js index 6d3a4814ffa74a65be4548fdac2b6772d3ee0034..5815245506c1bcf5035dd084756f9df9e34fe583 100644 --- a/app/assets/javascripts/ci/catalog/index.js +++ b/app/assets/javascripts/ci/catalog/index.js @@ -27,6 +27,9 @@ export const initCatalog = (selector = '#js-ci-cd-catalog') => { name: 'GlobalCatalog', router: createRouter(ciCatalogPath, CiResourcesPage), apolloProvider, + provide: { + ciCatalogPath, + }, render(h) { return h(GlobalCatalog); }, diff --git a/app/assets/javascripts/ci/catalog/router/routes.js b/app/assets/javascripts/ci/catalog/router/routes.js index ccfb0673c8328d210c6a81f7b1d378498a020ae0..ce859e266d7610456d0d8682c20ce2790b134eaf 100644 --- a/app/assets/javascripts/ci/catalog/router/routes.js +++ b/app/assets/javascripts/ci/catalog/router/routes.js @@ -4,6 +4,6 @@ import { CI_RESOURCES_PAGE_NAME, CI_RESOURCE_DETAILS_PAGE_NAME } from './constan export const createRoutes = (listComponent) => { return [ { name: CI_RESOURCES_PAGE_NAME, path: '', component: listComponent }, - { name: CI_RESOURCE_DETAILS_PAGE_NAME, path: '/:id', component: CiResourceDetailsPage }, + { name: CI_RESOURCE_DETAILS_PAGE_NAME, path: '/:id+', component: CiResourceDetailsPage }, ]; }; diff --git a/app/controllers/explore/catalog_controller.rb b/app/controllers/explore/catalog_controller.rb index 8207f3e55c66a190a8b56bd32f81651e18727a66..50846c21b1bdc067ea12e99e8c11870b7118f0e1 100644 --- a/app/controllers/explore/catalog_controller.rb +++ b/app/controllers/explore/catalog_controller.rb @@ -23,7 +23,7 @@ def check_resource_access end def catalog_resource - ::Ci::Catalog::Listing.new(current_user).find_resource(id: params[:id]) + ::Ci::Catalog::Listing.new(current_user).find_resource(full_path: params[:full_path]) end end end diff --git a/app/models/ci/catalog/listing.rb b/app/models/ci/catalog/listing.rb index 9baf5e7b2ccd8c6dc45638b4adeed95aae40f8c9..ebdaba55eeefae9f72db9c853a9401b5790877db 100644 --- a/app/models/ci/catalog/listing.rb +++ b/app/models/ci/catalog/listing.rb @@ -30,8 +30,8 @@ def resources(namespace: nil, sort: nil, search: nil, scope: :all) end end - def find_resource(id:) - resource = Ci::Catalog::Resource.find_by_id(id) + def find_resource(id: nil, full_path: nil) + resource = id ? Ci::Catalog::Resource.find_by_id(id) : Project.find_by_full_path(full_path)&.catalog_resource return unless resource.present? return unless resource.published? diff --git a/app/models/ci/catalog/resource.rb b/app/models/ci/catalog/resource.rb index 225f00ab35ea0a267f653b50bb52b52b8ff5ce56..8739450fb8e472b58d79a5d8a6344d380a223eaa 100644 --- a/app/models/ci/catalog/resource.rb +++ b/app/models/ci/catalog/resource.rb @@ -31,12 +31,16 @@ class Resource < ::ApplicationRecord scope :order_by_latest_released_at_desc, -> { reorder(arel_table[:latest_released_at].desc.nulls_last) } scope :order_by_latest_released_at_asc, -> { reorder(arel_table[:latest_released_at].asc.nulls_last) } - delegate :avatar_path, :star_count, to: :project + delegate :avatar_path, :star_count, :full_path, to: :project enum state: { draft: 0, published: 1 } before_create :sync_with_project + def to_param + full_path + end + def unpublish! update!(state: :draft) end diff --git a/config/routes/explore.rb b/config/routes/explore.rb index 36c2432d0ccb0374b4b19ec53acc0996e8247050..4c7ed50567984826601e1b7dabd57170b1b49b82 100644 --- a/config/routes/explore.rb +++ b/config/routes/explore.rb @@ -11,7 +11,10 @@ end resources :groups, only: [:index] - resources :catalog, only: [:index, :show], constraints: { id: /\d+/ } + scope :catalog do + get '/' => 'catalog#index', as: :catalog_index + get '/*full_path' => 'catalog#show', as: :catalog + end resources :snippets, only: [:index] root to: 'projects#index' end diff --git a/spec/features/explore/catalog/catalog_spec.rb b/spec/features/explore/catalog/catalog_spec.rb index d9ad27904e90b8502042491ef48f5ec9c9c4452c..00bbb02ebbf38a925be59bbf483da6da5d5df1bf 100644 --- a/spec/features/explore/catalog/catalog_spec.rb +++ b/spec/features/explore/catalog/catalog_spec.rb @@ -114,11 +114,11 @@ let_it_be(:project) { create(:project, :repository, namespace: namespace) } before do - visit explore_catalog_path(id: new_ci_resource["id"]) + visit explore_catalog_path(new_ci_resource) end context 'when the resource is published' do - let_it_be(:new_ci_resource) { create(:ci_catalog_resource, :published, project: project) } + let(:new_ci_resource) { create(:ci_catalog_resource, :published, project: project) } it 'navigates to the details page' do expect(page).to have_content('Go to the project') @@ -126,7 +126,7 @@ end context 'when the resource is not published' do - let_it_be(:new_ci_resource) { create(:ci_catalog_resource, project: project, state: :draft) } + let(:new_ci_resource) { create(:ci_catalog_resource, project: project, state: :draft) } it 'returns a 404' do expect(page).to have_title('Not Found') diff --git a/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js b/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js index 382f8e4620320ceea1d286c3e5e817998dc3ce02..0293cfec6f0ac618c3ac667842650e56a5a7c610 100644 --- a/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js +++ b/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js @@ -19,9 +19,9 @@ describe('CiResourceComponents', () => { const components = mockComponents.data.ciCatalogResource.components.nodes; - const resourceId = 'gid://gitlab/Ci::Catalog::Resource/1'; + const resourcePath = 'twitter/project-1'; - const defaultProps = { resourceId }; + const defaultProps = { resourcePath }; const createComponent = async () => { const handlers = [[getCiCatalogcomponentComponents, mockComponentsResponse]]; diff --git a/spec/frontend/ci/catalog/components/details/ci_resource_details_spec.js b/spec/frontend/ci/catalog/components/details/ci_resource_details_spec.js index 1f7dcf9d4e5bf2d516dd661f92aaa319d4afd932..e4b6c1cd046f396527faa213769cf67d7d5adc8a 100644 --- a/spec/frontend/ci/catalog/components/details/ci_resource_details_spec.js +++ b/spec/frontend/ci/catalog/components/details/ci_resource_details_spec.js @@ -8,7 +8,7 @@ describe('CiResourceDetails', () => { let wrapper; const defaultProps = { - resourceId: 'gid://gitlab/Ci::Catalog::Resource/1', + resourcePath: 'twitter/project-1', }; const defaultProvide = { glFeatures: { ciCatalogComponentsTab: true }, diff --git a/spec/frontend/ci/catalog/components/details/ci_resource_readme_spec.js b/spec/frontend/ci/catalog/components/details/ci_resource_readme_spec.js index 0dadac236a8df63318d213f4f98fa691dde941fe..ad76b47db574e2a585b0e847bbbdd1a9925a542d 100644 --- a/spec/frontend/ci/catalog/components/details/ci_resource_readme_spec.js +++ b/spec/frontend/ci/catalog/components/details/ci_resource_readme_spec.js @@ -23,12 +23,13 @@ describe('CiResourceReadme', () => { data: { ciCatalogResource: { id: resourceId, + webPath: 'twitter/project-1', readmeHtml, }, }, }; - const defaultProps = { resourceId }; + const defaultProps = { resourcePath: readmeMockData.data.ciCatalogResource.webPath }; const createComponent = ({ props = {} } = {}) => { const handlers = [[getCiCatalogResourceReadme, mockReadmeResponse]]; diff --git a/spec/frontend/ci/catalog/components/list/ci_resources_list_item_spec.js b/spec/frontend/ci/catalog/components/list/ci_resources_list_item_spec.js index 5d20c64c1cb67a5a650e452a9e0f000646cb8b7a..d74b133f3868e46be5eefb0bb2ecb360bbefb56d 100644 --- a/spec/frontend/ci/catalog/components/list/ci_resources_list_item_spec.js +++ b/spec/frontend/ci/catalog/components/list/ci_resources_list_item_spec.js @@ -2,10 +2,10 @@ import Vue from 'vue'; import VueRouter from 'vue-router'; import { GlAvatar, GlBadge, GlSprintf } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { cleanLeadingSeparator } from '~/lib/utils/url_utility'; import { createRouter } from '~/ci/catalog/router/index'; import CiResourcesListItem from '~/ci/catalog/components/list/ci_resources_list_item.vue'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import { CI_RESOURCE_DETAILS_PAGE_NAME } from '~/ci/catalog/router/constants'; import { catalogSinglePageResponse } from '../../mock'; Vue.use(VueRouter); @@ -70,9 +70,7 @@ describe('CiResourcesListItem', () => { it('renders the resource name and link', () => { expect(findResourceName().exists()).toBe(true); - expect(findResourceName().attributes().href).toBe( - `/${getIdFromGraphQLId(defaultProps.resource.id)}`, - ); + expect(findResourceName().attributes().href).toBe(defaultProps.resource.webPath); }); it('renders the resource version badge', () => { @@ -128,10 +126,7 @@ describe('CiResourcesListItem', () => { await findResourceName().vm.$emit('click', defaultEvent); expect(routerPush).toHaveBeenCalledWith({ - name: CI_RESOURCE_DETAILS_PAGE_NAME, - params: { - id: getIdFromGraphQLId(resource.id), - }, + path: cleanLeadingSeparator(resource.webPath), }); }); }); @@ -160,12 +155,7 @@ describe('CiResourcesListItem', () => { }); it('navigates to the details page', () => { - expect(routerPush).toHaveBeenCalledWith({ - name: CI_RESOURCE_DETAILS_PAGE_NAME, - params: { - id: getIdFromGraphQLId(resource.id), - }, - }); + expect(routerPush).toHaveBeenCalledWith({ path: cleanLeadingSeparator(resource.webPath) }); }); }); diff --git a/spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js b/spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js index 40f243ed89164342fef68576fc55bee6f22cc3f5..015c6504fa5c67402948384f10ec93ccf1b5a1e8 100644 --- a/spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js +++ b/spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js @@ -5,7 +5,8 @@ import { GlEmptyState } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import { CI_CATALOG_RESOURCE_TYPE, cacheConfig } from '~/ci/catalog/graphql/settings'; +import { cacheConfig } from '~/ci/catalog/graphql/settings'; +import { cleanLeadingSeparator } from '~/lib/utils/url_utility'; import getCiCatalogResourceSharedData from '~/ci/catalog/graphql/queries/get_ci_catalog_resource_shared_data.query.graphql'; import getCiCatalogResourceDetails from '~/ci/catalog/graphql/queries/get_ci_catalog_resource_details.query.graphql'; @@ -17,7 +18,6 @@ import CiResourceHeaderSkeletonLoader from '~/ci/catalog/components/details/ci_r import { createRouter } from '~/ci/catalog/router/index'; import { CI_RESOURCE_DETAILS_PAGE_NAME } from '~/ci/catalog/router/constants'; -import { convertToGraphQLId } from '~/graphql_shared/utils'; import { catalogSharedDataMock, catalogAdditionalDetailsMock } from '../../mock'; Vue.use(VueApollo); @@ -75,7 +75,7 @@ describe('CiResourceDetailsPage', () => { router = createRouter(); await router.push({ name: CI_RESOURCE_DETAILS_PAGE_NAME, - params: { id: defaultSharedData.id }, + params: { id: defaultSharedData.webPath }, }); }); @@ -178,7 +178,7 @@ describe('CiResourceDetailsPage', () => { it('passes expected props', () => { expect(findDetailsComponent().props()).toEqual({ - resourceId: convertToGraphQLId(CI_CATALOG_RESOURCE_TYPE, defaultAdditionalData.id), + resourcePath: cleanLeadingSeparator(defaultSharedData.webPath), }); }); }); diff --git a/spec/frontend/ci/catalog/mock.js b/spec/frontend/ci/catalog/mock.js index 49aaadf3c672cd94157b500b4d43e16d3ec85005..2d5d198ebe1cb02b408229250c9682025837a116 100644 --- a/spec/frontend/ci/catalog/mock.js +++ b/spec/frontend/ci/catalog/mock.js @@ -305,7 +305,7 @@ export const catalogSharedDataMock = { releasedAt: Date.now(), author: { id: 1, webUrl: 'profile/1', name: 'username' }, }, - webPath: 'path/to/project', + webPath: '/path/to/project', }, }, }; @@ -315,6 +315,7 @@ export const catalogAdditionalDetailsMock = { ciCatalogResource: { __typename: 'CiCatalogResource', id: `gid://gitlab/CiCatalogResource/1`, + webPath: '/twitter/project', openIssuesCount: 4, openMergeRequestsCount: 10, readmeHtml: '<h1>Hello world</h1>', @@ -386,6 +387,7 @@ export const mockComponents = { ciCatalogResource: { __typename: 'CiCatalogResource', id: `gid://gitlab/CiCatalogResource/1`, + webPath: 'twitter/project-1', components: { ...componentsMockData, }, @@ -398,6 +400,7 @@ export const mockComponentsEmpty = { ciCatalogResource: { __typename: 'CiCatalogResource', id: `gid://gitlab/CiCatalogResource/1`, + webPath: 'twitter/project-1', components: [], }, }, diff --git a/spec/models/ci/catalog/listing_spec.rb b/spec/models/ci/catalog/listing_spec.rb index 9d20d944e5ac6175336724fdf59bd53427af335c..2ffffb9112cbbab8f9f16e863ce9bd3552fd899d 100644 --- a/spec/models/ci/catalog/listing_spec.rb +++ b/spec/models/ci/catalog/listing_spec.rb @@ -186,55 +186,78 @@ describe '#find_resource' do let_it_be(:accessible_resource) { create(:ci_catalog_resource, :published, project: public_project) } + let_it_be(:inaccessible_resource) { create(:ci_catalog_resource, :published, project: project_noaccess) } + let_it_be(:draft_resource) { create(:ci_catalog_resource, project: public_namespace_project, state: :draft) } - subject { list.find_resource(id: id) } + context 'when using the ID argument' do + subject { list.find_resource(id: id) } - context 'when the resource is published and visible to the user' do - let(:id) { accessible_resource.id } + context 'when the resource is published and visible to the user' do + let(:id) { accessible_resource.id } - it 'fetches the resource' do - is_expected.to eq(accessible_resource) + it 'fetches the resource' do + is_expected.to eq(accessible_resource) + end end - end - context 'when the resource is not found' do - let(:id) { 'not-an-id' } + context 'when the resource is not found' do + let(:id) { 'not-an-id' } - it { is_expected.to be_nil } - end + it 'returns nil' do + is_expected.to be_nil + end + end + + context 'when the resource is not published' do + let(:id) { draft_resource.id } - context 'when the resource is not published' do - let_it_be(:draft_resource) { create(:ci_catalog_resource, project: public_namespace_project, state: :draft) } + it 'returns nil' do + is_expected.to be_nil + end + end - let(:id) { draft_resource.id } + context "when the current user cannot read code on the resource's project" do + let(:id) { inaccessible_resource.id } - it { is_expected.to be_nil } + it 'returns nil' do + is_expected.to be_nil + end + end end - context "when the current user cannot read code on the resource's project" do - let_it_be(:inaccessible_resource) { create(:ci_catalog_resource, :published, project: project_noaccess) } + context 'when using the full_path argument' do + subject { list.find_resource(full_path: full_path) } - let(:id) { inaccessible_resource.id } + context 'when the resource is published and visible to the user' do + let(:full_path) { accessible_resource.project.full_path } - it { is_expected.to be_nil } - end + it 'fetches the resource' do + is_expected.to eq(accessible_resource) + end + end - context 'when the current user is anonymous' do - let(:user) { nil } + context 'when the resource is not found' do + let(:full_path) { 'not-a-path' } - context 'when the resource is public' do - let(:id) { accessible_resource.id } + it 'returns nil' do + is_expected.to be_nil + end + end - it 'fetches the public resource' do - is_expected.to eq(accessible_resource) + context 'when the resource is not published' do + let(:full_path) { draft_resource.project.full_path } + + it 'returns nil' do + is_expected.to be_nil end end - context 'when the resource is internal' do - let(:internal_resource) { create(:ci_catalog_resource, :published, project: internal_project) } - let(:id) { internal_resource.id } + context "when the current user cannot read code on the resource's project" do + let(:full_path) { inaccessible_resource.project.full_path } - it { is_expected.to be_nil } + it 'returns nil' do + is_expected.to be_nil + end end end end diff --git a/spec/requests/explore/catalog_controller_spec.rb b/spec/requests/explore/catalog_controller_spec.rb index 25131eb17b5e353ac9389f3d499c4221683f935b..1361e20777926462ef4a3b7efd9d29095fd55948 100644 --- a/spec/requests/explore/catalog_controller_spec.rb +++ b/spec/requests/explore/catalog_controller_spec.rb @@ -19,7 +19,7 @@ if action == :index explore_catalog_index_path else - explore_catalog_path(id: catalog_resource.id) + explore_catalog_path(catalog_resource) end end @@ -55,7 +55,7 @@ it 'responds with 404' do catalog_resource = create(:ci_catalog_resource, state: :draft) - get explore_catalog_path(id: catalog_resource.id) + get explore_catalog_path(catalog_resource) expect(response).to have_gitlab_http_status(:not_found) end