Skip to content

Commit bee39c2

Browse files
authored
feat(stream_routes): show disabled error (#3130)
* feat(stream_routes): show disabled error * test: add show disabled error case * fix
1 parent 70712bd commit bee39c2

File tree

10 files changed

+179
-1
lines changed

10 files changed

+179
-1
lines changed

e2e/pom/stream_routes.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import { uiGoto } from '@e2e/utils/ui';
18+
import type { Page } from '@playwright/test';
19+
20+
const goto = {
21+
toIndex: (page: Page) => uiGoto(page, '/stream_routes'),
22+
};
23+
24+
export const streamRoutesPom = {
25+
...goto,
26+
};
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
/**
18+
* Licensed to the Apache Software Foundation (ASF) under one or more
19+
* contributor license agreements. See the NOTICE file distributed with
20+
* this work for additional information regarding copyright ownership.
21+
* The ASF licenses this file to You under the Apache License, Version 2.0
22+
* (the "License"); you may not use this file except in compliance with
23+
* the License. You may obtain a copy of the License at
24+
*
25+
* http://www.apache.org/licenses/LICENSE-2.0
26+
*
27+
* Unless required by applicable law or agreed to in writing, software
28+
* distributed under the License is distributed on an "AS IS" BASIS,
29+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30+
* See the License for the specific language governing permissions and
31+
* limitations under the License.
32+
*/
33+
import { exec } from 'node:child_process';
34+
import { readFile, writeFile } from 'node:fs/promises';
35+
import path from 'node:path';
36+
import { promisify } from 'node:util';
37+
38+
import { streamRoutesPom } from '@e2e/pom/stream_routes';
39+
import { test } from '@e2e/utils/test';
40+
import { expect } from '@playwright/test';
41+
import { produce, type WritableDraft } from 'immer';
42+
import { parse, stringify } from 'yaml';
43+
44+
const execAsync = promisify(exec);
45+
46+
type APISIXConf = {
47+
apisix: {
48+
proxy_mode: string;
49+
};
50+
};
51+
52+
const getE2EServerDir = () => {
53+
const currentDir = new URL('.', import.meta.url).pathname;
54+
return path.join(currentDir, '../server');
55+
};
56+
57+
const updateAPISIXConf = async (
58+
func: (v: WritableDraft<APISIXConf>) => void
59+
) => {
60+
const confPath = path.join(getE2EServerDir(), 'apisix_conf.yml');
61+
const fileContent = await readFile(confPath, 'utf-8');
62+
const config = parse(fileContent) as APISIXConf;
63+
64+
const updatedContent = stringify(produce(config, func));
65+
await writeFile(confPath, updatedContent, 'utf-8');
66+
};
67+
68+
const restartDockerServices = async () => {
69+
await execAsync('docker compose restart apisix', { cwd: getE2EServerDir() });
70+
const url = 'http://127.0.0.1:6174/ui/';
71+
const maxRetries = 20;
72+
const interval = 1000;
73+
for (let i = 0; i < maxRetries; i++) {
74+
const res = await fetch(url);
75+
if (res.ok) return;
76+
await new Promise((resolve) => setTimeout(resolve, interval));
77+
}
78+
throw new Error('APISIX is not ready');
79+
};
80+
81+
test.beforeAll(async () => {
82+
await updateAPISIXConf((d) => {
83+
d.apisix.proxy_mode = 'http';
84+
});
85+
await restartDockerServices();
86+
});
87+
88+
test.afterAll(async () => {
89+
await updateAPISIXConf((d) => {
90+
d.apisix.proxy_mode = 'http&stream';
91+
});
92+
await restartDockerServices();
93+
});
94+
95+
test('show disabled error', async ({ page }) => {
96+
await streamRoutesPom.toIndex(page);
97+
98+
await expect(page.locator('main > span')).toContainText(
99+
'stream mode is disabled, can not add stream routes'
100+
);
101+
102+
// Verify the error message is still shown after refresh
103+
await page.reload();
104+
await expect(page.locator('main > span')).toContainText(
105+
'stream mode is disabled, can not add stream routes'
106+
);
107+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import {
18+
ErrorComponent,
19+
type ErrorComponentProps,
20+
} from '@tanstack/react-router';
21+
import { AxiosError } from 'axios';
22+
23+
import type { APISIXRespErr } from '@/config/req';
24+
25+
export const StreamRoutesErrorComponent = (props: ErrorComponentProps) => {
26+
if (props.error instanceof AxiosError) {
27+
const err = props.error as AxiosError<APISIXRespErr>;
28+
if (err.response?.status === 400) {
29+
return <span>{err.response?.data.error_msg}</span>;
30+
}
31+
}
32+
return <ErrorComponent {...props} />;
33+
};

src/config/req.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ req.interceptors.request.use((conf) => {
4444
return conf;
4545
});
4646

47-
type APISIXRespErr = {
47+
export type APISIXRespErr = {
4848
error_msg?: string;
4949
message?: string;
5050
};

src/routes/services/detail.$id/stream_routes/add.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { useTranslation } from 'react-i18next';
2323

2424
import { FormTOCBox } from '@/components/form-slice/FormSection';
2525
import PageHeader from '@/components/page/PageHeader';
26+
import { StreamRoutesErrorComponent } from '@/components/page-slice/stream_routes/ErrorComponent';
2627
import { StreamRouteAddForm } from '@/routes/stream_routes/add';
2728
import { CommonFormContext } from '@/utils/form-context';
2829

@@ -54,4 +55,5 @@ function RouteComponent() {
5455

5556
export const Route = createFileRoute('/services/detail/$id/stream_routes/add')({
5657
component: RouteComponent,
58+
errorComponent: StreamRoutesErrorComponent,
5759
});

src/routes/services/detail.$id/stream_routes/detail.$routeId.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
useParams,
2121
} from '@tanstack/react-router';
2222

23+
import { StreamRoutesErrorComponent } from '@/components/page-slice/stream_routes/ErrorComponent';
2324
import { StreamRouteDetail } from '@/routes/stream_routes/detail.$id';
2425
import { CommonFormContext } from '@/utils/form-context';
2526

@@ -47,4 +48,5 @@ export const Route = createFileRoute(
4748
'/services/detail/$id/stream_routes/detail/$routeId'
4849
)({
4950
component: RouteComponent,
51+
errorComponent: StreamRoutesErrorComponent,
5052
});

src/routes/services/detail.$id/stream_routes/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { useTranslation } from 'react-i18next';
2020
import { getStreamRouteListQueryOptions } from '@/apis/hooks';
2121
import PageHeader from '@/components/page/PageHeader';
2222
import { ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
23+
import { StreamRoutesErrorComponent } from '@/components/page-slice/stream_routes/ErrorComponent';
2324
import { queryClient } from '@/config/global';
2425
import { StreamRouteList } from '@/routes/stream_routes';
2526
import { pageSearchSchema } from '@/types/schema/pageSearch';
@@ -51,6 +52,7 @@ function StreamRouteComponent() {
5152

5253
export const Route = createFileRoute('/services/detail/$id/stream_routes/')({
5354
component: StreamRouteComponent,
55+
errorComponent: StreamRoutesErrorComponent,
5456
validateSearch: pageSearchSchema,
5557
loaderDeps: ({ search }) => search,
5658
loader: ({ deps }) =>

src/routes/stream_routes/add.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from '@/components/form-slice/FormPartStreamRoute/schema';
3131
import { FormTOCBox } from '@/components/form-slice/FormSection';
3232
import PageHeader from '@/components/page/PageHeader';
33+
import { StreamRoutesErrorComponent } from '@/components/page-slice/stream_routes/ErrorComponent';
3334
import { req } from '@/config/req';
3435
import type { APISIXType } from '@/types/schema/apisix';
3536
import { produceRmUpstreamWhenHas } from '@/utils/form-producer';
@@ -101,4 +102,5 @@ function RouteComponent() {
101102

102103
export const Route = createFileRoute('/stream_routes/add')({
103104
component: RouteComponent,
105+
errorComponent: StreamRoutesErrorComponent,
104106
});

src/routes/stream_routes/detail.$id.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { FormTOCBox } from '@/components/form-slice/FormSection';
3636
import { FormSectionGeneral } from '@/components/form-slice/FormSectionGeneral';
3737
import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
3838
import PageHeader from '@/components/page/PageHeader';
39+
import { StreamRoutesErrorComponent } from '@/components/page-slice/stream_routes/ErrorComponent';
3940
import { API_STREAM_ROUTES } from '@/config/constant';
4041
import { req } from '@/config/req';
4142
import { APISIX, type APISIXType } from '@/types/schema/apisix';
@@ -166,4 +167,5 @@ function RouteComponent() {
166167

167168
export const Route = createFileRoute('/stream_routes/detail/$id')({
168169
component: RouteComponent,
170+
errorComponent: StreamRoutesErrorComponent,
169171
});

src/routes/stream_routes/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import type { WithServiceIdFilter } from '@/apis/routes';
2525
import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
2626
import PageHeader from '@/components/page/PageHeader';
2727
import { ToAddPageBtn, ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
28+
import { StreamRoutesErrorComponent } from '@/components/page-slice/stream_routes/ErrorComponent';
2829
import { AntdConfigProvider } from '@/config/antdConfigProvider';
2930
import { API_STREAM_ROUTES } from '@/config/constant';
3031
import { queryClient } from '@/config/global';
@@ -155,6 +156,7 @@ function StreamRouteComponent() {
155156

156157
export const Route = createFileRoute('/stream_routes/')({
157158
component: StreamRouteComponent,
159+
errorComponent: StreamRoutesErrorComponent,
158160
validateSearch: pageSearchSchema,
159161
loaderDeps: ({ search }) => search,
160162
loader: ({ deps }) =>

0 commit comments

Comments
 (0)