Skip to content

Commit c229cc9

Browse files
authored
[7.x] Allow the default space to be accessed via /s/default (#77109) (#80815)
* Allow the default space to be accessed via /s/default * apply suggestions from code review
1 parent 59e6011 commit c229cc9

File tree

11 files changed

+208
-119
lines changed

11 files changed

+208
-119
lines changed

x-pack/plugins/spaces/common/lib/spaces_url_parser.test.ts

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,41 +10,76 @@ describe('getSpaceIdFromPath', () => {
1010
describe('without a serverBasePath defined', () => {
1111
test('it identifies the space url context', () => {
1212
const basePath = `/s/my-awesome-space-lives-here`;
13-
expect(getSpaceIdFromPath(basePath)).toEqual('my-awesome-space-lives-here');
13+
expect(getSpaceIdFromPath(basePath)).toEqual({
14+
spaceId: 'my-awesome-space-lives-here',
15+
pathHasExplicitSpaceIdentifier: true,
16+
});
1417
});
1518

1619
test('ignores space identifiers in the middle of the path', () => {
1720
const basePath = `/this/is/a/crazy/path/s/my-awesome-space-lives-here`;
18-
expect(getSpaceIdFromPath(basePath)).toEqual(DEFAULT_SPACE_ID);
21+
expect(getSpaceIdFromPath(basePath)).toEqual({
22+
spaceId: DEFAULT_SPACE_ID,
23+
pathHasExplicitSpaceIdentifier: false,
24+
});
1925
});
2026

2127
test('it handles base url without a space url context', () => {
2228
const basePath = `/this/is/a/crazy/path/s`;
23-
expect(getSpaceIdFromPath(basePath)).toEqual(DEFAULT_SPACE_ID);
29+
expect(getSpaceIdFromPath(basePath)).toEqual({
30+
spaceId: DEFAULT_SPACE_ID,
31+
pathHasExplicitSpaceIdentifier: false,
32+
});
33+
});
34+
35+
test('it identifies the space url context with the default space', () => {
36+
const basePath = `/s/${DEFAULT_SPACE_ID}`;
37+
expect(getSpaceIdFromPath(basePath)).toEqual({
38+
spaceId: DEFAULT_SPACE_ID,
39+
pathHasExplicitSpaceIdentifier: true,
40+
});
2441
});
2542
});
2643

2744
describe('with a serverBasePath defined', () => {
2845
test('it identifies the space url context', () => {
2946
const basePath = `/s/my-awesome-space-lives-here`;
30-
expect(getSpaceIdFromPath(basePath, '/')).toEqual('my-awesome-space-lives-here');
47+
expect(getSpaceIdFromPath(basePath, '/')).toEqual({
48+
spaceId: 'my-awesome-space-lives-here',
49+
pathHasExplicitSpaceIdentifier: true,
50+
});
3151
});
3252

3353
test('it identifies the space url context following the server base path', () => {
3454
const basePath = `/server-base-path-here/s/my-awesome-space-lives-here`;
35-
expect(getSpaceIdFromPath(basePath, '/server-base-path-here')).toEqual(
36-
'my-awesome-space-lives-here'
37-
);
55+
expect(getSpaceIdFromPath(basePath, '/server-base-path-here')).toEqual({
56+
spaceId: 'my-awesome-space-lives-here',
57+
pathHasExplicitSpaceIdentifier: true,
58+
});
3859
});
3960

4061
test('ignores space identifiers in the middle of the path', () => {
4162
const basePath = `/this/is/a/crazy/path/s/my-awesome-space-lives-here`;
42-
expect(getSpaceIdFromPath(basePath, '/this/is/a')).toEqual(DEFAULT_SPACE_ID);
63+
expect(getSpaceIdFromPath(basePath, '/this/is/a')).toEqual({
64+
spaceId: DEFAULT_SPACE_ID,
65+
pathHasExplicitSpaceIdentifier: false,
66+
});
67+
});
68+
69+
test('it identifies the space url context with the default space following the server base path', () => {
70+
const basePath = `/server-base-path-here/s/${DEFAULT_SPACE_ID}`;
71+
expect(getSpaceIdFromPath(basePath, '/server-base-path-here')).toEqual({
72+
spaceId: DEFAULT_SPACE_ID,
73+
pathHasExplicitSpaceIdentifier: true,
74+
});
4375
});
4476

4577
test('it handles base url without a space url context', () => {
4678
const basePath = `/this/is/a/crazy/path/s`;
47-
expect(getSpaceIdFromPath(basePath, basePath)).toEqual(DEFAULT_SPACE_ID);
79+
expect(getSpaceIdFromPath(basePath, basePath)).toEqual({
80+
spaceId: DEFAULT_SPACE_ID,
81+
pathHasExplicitSpaceIdentifier: false,
82+
});
4883
});
4984
});
5085
});

x-pack/plugins/spaces/common/lib/spaces_url_parser.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,22 @@
55
*/
66
import { DEFAULT_SPACE_ID } from '../constants';
77

8+
const spaceContextRegex = /^\/s\/([a-z0-9_\-]+)/;
9+
810
export function getSpaceIdFromPath(
911
requestBasePath: string = '/',
1012
serverBasePath: string = '/'
11-
): string {
12-
let pathToCheck: string = requestBasePath;
13+
): { spaceId: string; pathHasExplicitSpaceIdentifier: boolean } {
14+
const pathToCheck: string = stripServerBasePath(requestBasePath, serverBasePath);
1315

14-
if (serverBasePath && serverBasePath !== '/' && requestBasePath.startsWith(serverBasePath)) {
15-
pathToCheck = requestBasePath.substr(serverBasePath.length);
16-
}
1716
// Look for `/s/space-url-context` in the base path
18-
const matchResult = pathToCheck.match(/^\/s\/([a-z0-9_\-]+)/);
17+
const matchResult = pathToCheck.match(spaceContextRegex);
1918

2019
if (!matchResult || matchResult.length === 0) {
21-
return DEFAULT_SPACE_ID;
20+
return {
21+
spaceId: DEFAULT_SPACE_ID,
22+
pathHasExplicitSpaceIdentifier: false,
23+
};
2224
}
2325

2426
// Ignoring first result, we only want the capture group result at index 1
@@ -28,7 +30,10 @@ export function getSpaceIdFromPath(
2830
throw new Error(`Unable to determine Space ID from request path: ${requestBasePath}`);
2931
}
3032

31-
return spaceId;
33+
return {
34+
spaceId,
35+
pathHasExplicitSpaceIdentifier: true,
36+
};
3237
}
3338

3439
export function addSpaceIdToPath(
@@ -45,3 +50,10 @@ export function addSpaceIdToPath(
4550
}
4651
return `${basePath}${requestedPath}`;
4752
}
53+
54+
function stripServerBasePath(requestBasePath: string, serverBasePath: string) {
55+
if (serverBasePath && serverBasePath !== '/' && requestBasePath.startsWith(serverBasePath)) {
56+
return requestBasePath.substr(serverBasePath.length);
57+
}
58+
return requestBasePath;
59+
}

x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
CoreSetup,
1111
} from 'src/core/server';
1212
import { format } from 'url';
13-
import { DEFAULT_SPACE_ID } from '../../../common/constants';
1413
import { modifyUrl } from '../utils/url';
1514
import { getSpaceIdFromPath } from '../../../common';
1615

@@ -28,9 +27,9 @@ export function initSpacesOnRequestInterceptor({ http }: OnRequestInterceptorDep
2827

2928
// If navigating within the context of a space, then we store the Space's URL Context on the request,
3029
// and rewrite the request to not include the space identifier in the URL.
31-
const spaceId = getSpaceIdFromPath(path, serverBasePath);
30+
const { spaceId, pathHasExplicitSpaceIdentifier } = getSpaceIdFromPath(path, serverBasePath);
3231

33-
if (spaceId !== DEFAULT_SPACE_ID) {
32+
if (pathHasExplicitSpaceIdentifier) {
3433
const reqBasePath = `/s/${spaceId}`;
3534

3635
http.basePath.set(request, reqBasePath);

x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const createService = async (serverBasePath: string = '') => {
5858
serverBasePath,
5959
} as HttpServiceSetup['basePath'];
6060
httpSetup.basePath.get = jest.fn().mockImplementation((request: KibanaRequest) => {
61-
const spaceId = getSpaceIdFromPath(request.url.path);
61+
const { spaceId } = getSpaceIdFromPath(request.url.path);
6262

6363
if (spaceId !== DEFAULT_SPACE_ID) {
6464
return `/s/${spaceId}`;

x-pack/plugins/spaces/server/spaces_service/spaces_service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class SpacesService {
6363
? (request as Record<string, any>).getBasePath()
6464
: http.basePath.get(request);
6565

66-
const spaceId = getSpaceIdFromPath(basePath, http.basePath.serverBasePath);
66+
const { spaceId } = getSpaceIdFromPath(basePath, http.basePath.serverBasePath);
6767

6868
return spaceId;
6969
};

x-pack/test/api_integration/apis/spaces/get_active_space.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,20 @@ export default function ({ getService }: FtrProviderContext) {
3535
});
3636
});
3737

38+
it('returns the default space when explicitly referenced', async () => {
39+
await supertest
40+
.get('/s/default/internal/spaces/_active_space')
41+
.set('kbn-xsrf', 'xxx')
42+
.expect(200, {
43+
id: 'default',
44+
name: 'Default',
45+
description: 'This is your default space!',
46+
color: '#00bfb3',
47+
disabledFeatures: [],
48+
_reserved: true,
49+
});
50+
});
51+
3852
it('returns the foo space', async () => {
3953
await supertest
4054
.get('/s/foo-space/internal/spaces/_active_space')

x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,24 @@ export function getUrlPrefix(spaceId?: string) {
1313
export function getIdPrefix(spaceId?: string) {
1414
return spaceId === DEFAULT_SPACE_ID ? '' : `${spaceId}-`;
1515
}
16+
17+
export function getTestScenariosForSpace(spaceId: string) {
18+
const explicitScenario = {
19+
spaceId,
20+
urlPrefix: `/s/${spaceId}`,
21+
scenario: `when referencing the ${spaceId} space explicitly in the URL`,
22+
};
23+
24+
if (spaceId === DEFAULT_SPACE_ID) {
25+
return [
26+
{
27+
spaceId,
28+
urlPrefix: ``,
29+
scenario: 'when referencing the default space implicitly',
30+
},
31+
explicitScenario,
32+
];
33+
}
34+
35+
return [explicitScenario];
36+
}

x-pack/test/spaces_api_integration/common/suites/create.ts

Lines changed: 44 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import expect from '@kbn/expect';
88
import { SuperTest } from 'supertest';
9-
import { getUrlPrefix } from '../lib/space_test_utils';
9+
import { getTestScenariosForSpace } from '../lib/space_test_utils';
1010
import { DescribeFn, TestDefinitionAuthentication } from '../lib/types';
1111

1212
interface CreateTest {
@@ -67,56 +67,58 @@ export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest<any
6767
{ user = {}, spaceId, tests }: CreateTestDefinition
6868
) => {
6969
describeFn(description, () => {
70-
before(() => esArchiver.load('saved_objects/spaces'));
71-
after(() => esArchiver.unload('saved_objects/spaces'));
70+
beforeEach(() => esArchiver.load('saved_objects/spaces'));
71+
afterEach(() => esArchiver.unload('saved_objects/spaces'));
7272

73-
it(`should return ${tests.newSpace.statusCode}`, async () => {
74-
return supertest
75-
.post(`${getUrlPrefix(spaceId)}/api/spaces/space`)
76-
.auth(user.username, user.password)
77-
.send({
78-
name: 'marketing',
79-
id: 'marketing',
80-
description: 'a description',
81-
color: '#5c5959',
82-
disabledFeatures: [],
83-
})
84-
.expect(tests.newSpace.statusCode)
85-
.then(tests.newSpace.response);
86-
});
87-
88-
describe('when it already exists', () => {
89-
it(`should return ${tests.alreadyExists.statusCode}`, async () => {
73+
getTestScenariosForSpace(spaceId).forEach(({ urlPrefix, scenario }) => {
74+
it(`should return ${tests.newSpace.statusCode} ${scenario}`, async () => {
9075
return supertest
91-
.post(`${getUrlPrefix(spaceId)}/api/spaces/space`)
76+
.post(`${urlPrefix}/api/spaces/space`)
9277
.auth(user.username, user.password)
9378
.send({
94-
name: 'space_1',
95-
id: 'space_1',
96-
color: '#ffffff',
79+
name: 'marketing',
80+
id: 'marketing',
9781
description: 'a description',
82+
color: '#5c5959',
9883
disabledFeatures: [],
9984
})
100-
.expect(tests.alreadyExists.statusCode)
101-
.then(tests.alreadyExists.response);
85+
.expect(tests.newSpace.statusCode)
86+
.then(tests.newSpace.response);
10287
});
103-
});
10488

105-
describe('when _reserved is specified', () => {
106-
it(`should return ${tests.reservedSpecified.statusCode} and ignore _reserved`, async () => {
107-
return supertest
108-
.post(`${getUrlPrefix(spaceId)}/api/spaces/space`)
109-
.auth(user.username, user.password)
110-
.send({
111-
name: 'reserved space',
112-
id: 'reserved',
113-
description: 'a description',
114-
color: '#5c5959',
115-
_reserved: true,
116-
disabledFeatures: [],
117-
})
118-
.expect(tests.reservedSpecified.statusCode)
119-
.then(tests.reservedSpecified.response);
89+
describe('when it already exists', () => {
90+
it(`should return ${tests.alreadyExists.statusCode} ${scenario}`, async () => {
91+
return supertest
92+
.post(`${urlPrefix}/api/spaces/space`)
93+
.auth(user.username, user.password)
94+
.send({
95+
name: 'space_1',
96+
id: 'space_1',
97+
color: '#ffffff',
98+
description: 'a description',
99+
disabledFeatures: [],
100+
})
101+
.expect(tests.alreadyExists.statusCode)
102+
.then(tests.alreadyExists.response);
103+
});
104+
});
105+
106+
describe('when _reserved is specified', () => {
107+
it(`should return ${tests.reservedSpecified.statusCode} and ignore _reserved ${scenario}`, async () => {
108+
return supertest
109+
.post(`${urlPrefix}/api/spaces/space`)
110+
.auth(user.username, user.password)
111+
.send({
112+
name: 'reserved space',
113+
id: 'reserved',
114+
description: 'a description',
115+
color: '#5c5959',
116+
_reserved: true,
117+
disabledFeatures: [],
118+
})
119+
.expect(tests.reservedSpecified.statusCode)
120+
.then(tests.reservedSpecified.response);
121+
});
120122
});
121123
});
122124
});

0 commit comments

Comments
 (0)