Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/cli/api-doc-gen.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'fs';
import path from 'path';

const API_DIR = './src/api-clients';
const OUTPUT_PATH = './src/api-clients/_common/constants/api-doc.ts';
const OUTPUT_PATH = './src/api-clients/_common/constants/api-doc-constant.ts';


const generateAPIDocumentation = (basePath) => {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"stylelint:fix": "stylelint --fix \"src/**/*.{css,vue,pcss,scss}\"",
"format": "npm run eslint:fix && npm run stylelint:fix",
"postcss": "node build/reset-css.js",
"api-doc": "node ./cli/api-doc-gen.js && npx eslint ./src/api-clients/_common/constants/api-doc.ts --fix"
"api-doc": "node ./cli/api-doc-gen.js && npx eslint ./src/api-clients/_common/constants/api-doc-constant.ts --fix"
},
"main": "dist/cloudforet-component.common.js",
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/api-clients/_common/types/query-key-type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { API_DOC } from '@/api-clients/_common/constants/api-doc';
import type { API_DOC } from '@/api-clients/_common/constants/api-doc-constant';


/**
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import VTooltip from 'v-tooltip';
import SpaceDesignSystem from '@cloudforet/mirinae';

import directive from '@/directives';
import { serviceQueryClient } from '@/query/clients';
import { SpaceRouter } from '@/router';
import { i18n } from '@/translations';

Expand All @@ -24,13 +25,12 @@ import '@/styles/style.pcss';
import '@cloudforet/mirinae/css/light-style.css';
import '@cloudforet/mirinae/dist/style.css';


/** ********** SET VUE PLUGINS ************** */
Vue.use(Fragment.Plugin);
Vue.use(VTooltip, { defaultClass: 'p-tooltip', defaultBoundariesElement: document.body });
Vue.use(PortalVue);
Vue.use(PiniaVuePlugin);
Vue.use(VueQueryPlugin);
Vue.use(VueQueryPlugin, { defaultQueryClient: serviceQueryClient });

directive(Vue);

Expand Down
15 changes: 15 additions & 0 deletions apps/web/src/query/clients.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { QueryClient } from '@tanstack/vue-query';

/**
* Main query client for service-level data fetching and caching.
* This is the default query client used throughout the application.
* It's automatically injected when useQueryClient() is called in service context.
*/
export const serviceQueryClient = new QueryClient();

/**
* Dedicated query client for the reference data system.
* This client is used internally by the reference data system for managing its own cache.
* It's not meant to be used directly in service context.
*/
export const referenceQueryClient = new QueryClient();
176 changes: 176 additions & 0 deletions apps/web/src/query/query-key/__tests__/use-service-query-key.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { computed } from 'vue';

import {
describe, it, expect, vi,
} from 'vitest';

import { _useServiceQueryKey } from '../use-service-query-key';

// Mock useQueryKeyAppContext
vi.mock('@/query/query-key/_composable/use-app-context-query-key', () => ({
useQueryKeyAppContext: () => ({
value: ['workspace', 'workspace-123'] as const,
}),
}));

describe('_useServiceQueryKey', () => {
it('should generate correct query key structure for basic usage', () => {
const { key } = _useServiceQueryKey(
'dashboard',
'public-data-table',
'list',
{
params: { page: 1, limit: 10 },
},
);

expect(key.value).toMatchInlineSnapshot(`
[
"workspace",
"workspace-123",
"dashboard",
"public-data-table",
"list",
{
"limit": 10,
"page": 1,
},
]
`);
});

it('should generate correct query key structure with contextKey', () => {
const { key } = _useServiceQueryKey(
'dashboard',
'public-data-table',
'get',
{
contextKey: computed(() => 'table-123'),
params: computed(() => ({ id: 'table-123' })),
},
);

expect(key.value).toMatchInlineSnapshot(`
[
"workspace",
"workspace-123",
"dashboard",
"public-data-table",
"get",
"table-123",
{
"id": "table-123",
},
]
`);
});

it('should handle reactive values correctly', () => {
const params = computed(() => ({ id: 'table-123' }));
const contextKey = computed(() => 'table-123');

const { key } = _useServiceQueryKey(
'dashboard',
'public-data-table',
'get',
{
contextKey,
params,
},
);

expect(key.value).toMatchInlineSnapshot(`
[
"workspace",
"workspace-123",
"dashboard",
"public-data-table",
"get",
"table-123",
{
"id": "table-123",
},
]
`);
});

it('should handle function getters correctly', () => {
const { key } = _useServiceQueryKey(
'dashboard',
'public-data-table',
'get',
{
contextKey: () => 'table-123',
params: computed(() => ({ id: 'table-123' })),
},
);

expect(key.value).toMatchInlineSnapshot(`
[
"workspace",
"workspace-123",
"dashboard",
"public-data-table",
"get",
"table-123",
{
"id": "table-123",
},
]
`);
});

it('should maintain consistent key structure with different option orders', () => {
const params = computed(() => ({ id: 'table-123' }));
const contextKey = computed(() => 'table-123');

const { key: key1 } = _useServiceQueryKey(
'dashboard',
'public-data-table',
'get',
{ contextKey, params },
);

const { key: key2 } = _useServiceQueryKey(
'dashboard',
'public-data-table',
'get',
{ params, contextKey },
);

expect(key1.value).toEqual(key2.value);
expect(key1.value).toMatchInlineSnapshot(`
[
"workspace",
"workspace-123",
"dashboard",
"public-data-table",
"get",
"table-123",
{
"id": "table-123",
},
]
`);
});

it('should handle withSuffix correctly', () => {
const { withSuffix } = _useServiceQueryKey(
'dashboard',
'public-data-table',
'load',
);

const result = withSuffix('table-123');
expect(result).toMatchInlineSnapshot(`
[
"workspace",
"workspace-123",
"dashboard",
"public-data-table",
"load",
"table-123",
]
`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { computed } from 'vue';

import { useAppContextStore } from '@/store/app-context/app-context-store';
import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store';




interface AdminModeState {
isAdminMode: true;
workspaceId?: undefined;
}

interface WorkspaceModeState {
isAdminMode: false;
workspaceId: string;
}

type QueryKeyState = AdminModeState | WorkspaceModeState;


export const useQueryKeyAppContext = () => {
const appContextStore = useAppContextStore();
const userWorkspaceStore = useUserWorkspaceStore();

const _isAdminMode = computed<boolean>(() => appContextStore.getters.isAdminMode);
const _workspaceId = computed<string|undefined>(() => userWorkspaceStore.getters.currentWorkspaceId);

return computed(() => {
const state: QueryKeyState = _isAdminMode.value
? { isAdminMode: true }
: {
isAdminMode: false,
// workspaceId: _state.workspaceId!
workspaceId: _workspaceId.value ?? '',
};

return state.isAdminMode
? ['admin']
: ['workspace', state.workspaceId];
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const createImmutableObjectKeyItem = <T>(obj: T): T => {
if (obj === null || typeof obj !== 'object') {
return obj;
}

if (Array.isArray(obj)) {
return obj.map((item) => createImmutableObjectKeyItem(item)) as unknown as T;
}

const immutableObj = Object.entries(obj).reduce((acc, [key, value]) => ({
...acc,
[key]: createImmutableObjectKeyItem(value),
}), {});

return Object.freeze(immutableObj) as T;
};
15 changes: 15 additions & 0 deletions apps/web/src/query/query-key/_types/query-key-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { API_DOC } from '@/api-clients/_common/constants/api-doc-constant';


export type QueryKeyArray = unknown[];

export type QueryScope = 'service' | 'reference';



/**
* Extracts all possible keys for `{service-name}`, `{resource-name}`, and `{verb}`
*/
export type ServiceName = keyof typeof API_DOC;
export type ResourceName<S extends ServiceName> = keyof (typeof API_DOC)[S];
export type Verb<S extends ServiceName, R extends ResourceName<S>> = Extract<(typeof API_DOC)[S][R], string[]>[number] | string;
Loading
Loading