@piveau/sdk-vue 0.0.0-alpha-2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -0
- package/dist/__tests__/defineResourceGetters.test.d.ts +1 -0
- package/dist/__tests__/defineResourceGetters.test.js +119 -0
- package/dist/__tests__/testUtils.d.ts +10 -0
- package/dist/__tests__/testUtils.js +10 -0
- package/dist/__tests__/useResourceFactory.test.d.ts +1 -0
- package/dist/__tests__/useResourceFactory.test.js +125 -0
- package/dist/__tests__/useSearchFactory.test.d.ts +1 -0
- package/dist/__tests__/useSearchFactory.test.js +102 -0
- package/dist/__tests__/useSearchQueryParams.test.d.ts +1 -0
- package/dist/__tests__/useSearchQueryParams.test.js +89 -0
- package/dist/composables/locale.d.ts +27 -0
- package/dist/composables/locale.js +8 -0
- package/dist/composables/usePropertyTable.d.ts +1 -0
- package/dist/composables/usePropertyTable.js +2 -0
- package/dist/defineHubSearch.d.ts +37 -0
- package/dist/defineHubSearch.js +91 -0
- package/dist/getters.d.ts +26 -0
- package/dist/getters.js +28 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +29 -0
- package/dist/integrations/__tests__/dcatAp.test.d.ts +1 -0
- package/dist/integrations/__tests__/dcatAp.test.js +36 -0
- package/dist/integrations/__tests__/https-data-paysdelaloire-fr-explore-dataset-agenda-culture-de-la-region-des-pays-de-la-loire-.json +647 -0
- package/dist/integrations/dcatAp/dcatAp.d.ts +1107 -0
- package/dist/integrations/dcatAp/dcatAp.js +185 -0
- package/dist/integrations/dcatAp/index.d.ts +2 -0
- package/dist/integrations/dcatAp/index.js +2 -0
- package/dist/integrations/dcatAp/properties.d.ts +9 -0
- package/dist/integrations/dcatAp/properties.js +142 -0
- package/dist/integrations/index.d.ts +1 -0
- package/dist/integrations/index.js +1 -0
- package/dist/locale/__tests__/index.test.d.ts +1 -0
- package/dist/locale/__tests__/index.test.js +12 -0
- package/dist/locale/adapters/default.d.ts +6 -0
- package/dist/locale/adapters/default.js +60 -0
- package/dist/locale/de.d.ts +2096 -0
- package/dist/locale/de.js +2096 -0
- package/dist/locale/en.d.ts +2101 -0
- package/dist/locale/en.js +2101 -0
- package/dist/locale/index.d.ts +2 -0
- package/dist/locale/index.js +2 -0
- package/dist/mock/db.json +10862 -0
- package/dist/mock/mockedHubSearch.d.ts +4 -0
- package/dist/mock/mockedHubSearch.js +40 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.js +0 -0
- package/dist/useGettersFactory.d.ts +7 -0
- package/dist/useGettersFactory.js +10 -0
- package/dist/useMetrics.d.ts +14 -0
- package/dist/useMetrics.js +22 -0
- package/dist/useResourceFactory.d.ts +41 -0
- package/dist/useResourceFactory.js +89 -0
- package/dist/useSearchFactory.d.ts +91 -0
- package/dist/useSearchFactory.js +161 -0
- package/dist/useSearchQueryParams.d.ts +223 -0
- package/dist/useSearchQueryParams.js +91 -0
- package/dist/utils/__tests__/propertyTable.test.d.ts +1 -0
- package/dist/utils/__tests__/propertyTable.test.js +372 -0
- package/dist/utils/helpers.d.ts +92 -0
- package/dist/utils/helpers.js +46 -0
- package/dist/utils/propertyTable.d.ts +82 -0
- package/dist/utils/propertyTable.js +113 -0
- package/dist/utils/vHelpers.d.ts +58 -0
- package/dist/utils/vHelpers.js +50 -0
- package/dist/utils.d.ts +61 -0
- package/dist/utils.js +37 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @piveau/sdk-vue
|
|
2
|
+
|
|
3
|
+
Asynchronous state management for interacting with piveau services in Vue.js.
|
|
4
|
+
|
|
5
|
+
Looking for a standalone API client? Try @piveau/sdk-core
|
|
6
|
+
|
|
7
|
+
## Quick Features
|
|
8
|
+
|
|
9
|
+
- Provides intelligent client-side caching, background updates, and optimistic updates of piveau backend data state in a declarative way, powered by [@tanstack/vue-query](https://github.com/TanStack/query)
|
|
10
|
+
- Composable utility functions to help you model real-world use cases easier, e.g., facets, search, and pagination
|
|
11
|
+
- Runtime resource schema validation and model type inference powered by [Zod](https://zod.dev/)
|
|
12
|
+
- Shape data from API to your data model using getters
|
|
13
|
+
- Vue 3 compatible
|
|
14
|
+
- Supports Server-Side Rendering with Nuxt
|
|
15
|
+
- Written with TypeScript
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
<table>
|
|
20
|
+
<tbody valign=top align=left>
|
|
21
|
+
<tr><th>
|
|
22
|
+
Node 20+, Vue 3
|
|
23
|
+
</th><td>
|
|
24
|
+
|
|
25
|
+
Install with <code>npm/pnpm install @piveau/sdk-vue</code>, or <code>yarn add @piveau/sdk-vue</code>. For better devtooling, we recommend installing @piveau/sdk-core and vue-query with <code>npm/pnpm install @piveau/sdk-core @tanstack/vue-query@5</code>.
|
|
26
|
+
|
|
27
|
+
**Recommended**: In `main.ts`, setup the plugin:
|
|
28
|
+
```js
|
|
29
|
+
import { createApp } from 'vue'
|
|
30
|
+
import { plugin } from '@piveau/sdk-vue'
|
|
31
|
+
import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query'
|
|
32
|
+
import App from './App.vue'
|
|
33
|
+
|
|
34
|
+
const qc = new QueryClient()
|
|
35
|
+
|
|
36
|
+
const app = createApp(App)
|
|
37
|
+
|
|
38
|
+
app.use(VueQueryPlugin, {
|
|
39
|
+
queryClient: qc,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
app.use(plugin, {
|
|
43
|
+
queryClient: qc,
|
|
44
|
+
})
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
</td></tr>
|
|
48
|
+
<tr><th>
|
|
49
|
+
Node 20+, Nuxt 3
|
|
50
|
+
</th><td>
|
|
51
|
+
*Work in Progress* See https://tanstack.com/query/latest/docs/framework/vue/guides/ssr and register the sdk-vue plugin as above
|
|
52
|
+
</td></tr>
|
|
53
|
+
</tbody>
|
|
54
|
+
</table>
|
|
55
|
+
|
|
56
|
+
## piveau API Support
|
|
57
|
+
|
|
58
|
+
- `/search/filter={filter}` for searching resources
|
|
59
|
+
- `/search/{index}/{id}` for fetching a single resource
|
|
60
|
+
|
|
61
|
+
## Work in Progress
|
|
62
|
+
|
|
63
|
+
- hub
|
|
64
|
+
- consus
|
|
65
|
+
- metrics
|
|
66
|
+
- statistics
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { nextTick, ref } from "vue-demi";
|
|
4
|
+
import { defineResourceSearchOptions } from "../createUseResourceSearch.js";
|
|
5
|
+
import { defineGetters, toComputedGetters } from "../getters.js";
|
|
6
|
+
describe("defineResourceGetters", () => {
|
|
7
|
+
it("should work", async () => {
|
|
8
|
+
const ctx = defineResourceSearchOptions({
|
|
9
|
+
baseUrl: `https://open.bydata.de/api/hub/search`,
|
|
10
|
+
index: "dataset",
|
|
11
|
+
schema: z.object({
|
|
12
|
+
foo: z.string(),
|
|
13
|
+
bar: z.number()
|
|
14
|
+
}),
|
|
15
|
+
facets: ["categories", "publisher", "catalog"]
|
|
16
|
+
});
|
|
17
|
+
const getters = defineGetters({
|
|
18
|
+
schema: z.object({
|
|
19
|
+
foo: z.string(),
|
|
20
|
+
bar: z.number()
|
|
21
|
+
}),
|
|
22
|
+
ctx,
|
|
23
|
+
getters: {
|
|
24
|
+
/**
|
|
25
|
+
* Gets the test result
|
|
26
|
+
*/
|
|
27
|
+
getTestResult: (data) => {
|
|
28
|
+
return `${data.foo}${data.bar}`;
|
|
29
|
+
},
|
|
30
|
+
getOtherTestResult: () => {
|
|
31
|
+
return "otherTest";
|
|
32
|
+
},
|
|
33
|
+
getBaseUrl: (_, ctx2) => {
|
|
34
|
+
return ctx2?.baseUrl;
|
|
35
|
+
},
|
|
36
|
+
getFullTestResult: (data, ctx2) => {
|
|
37
|
+
return `${data.foo}${data.bar}${ctx2?.baseUrl}`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
expect(
|
|
42
|
+
getters.getTestResult({
|
|
43
|
+
foo: "test",
|
|
44
|
+
bar: 123
|
|
45
|
+
})
|
|
46
|
+
).toBe("test123");
|
|
47
|
+
expect(getters.getOtherTestResult()).toBe("otherTest");
|
|
48
|
+
expect(
|
|
49
|
+
getters.getBaseUrl(
|
|
50
|
+
{
|
|
51
|
+
foo: "test",
|
|
52
|
+
bar: 123
|
|
53
|
+
},
|
|
54
|
+
ctx
|
|
55
|
+
)
|
|
56
|
+
).toBe(ctx.baseUrl);
|
|
57
|
+
expect(
|
|
58
|
+
getters.getFullTestResult(
|
|
59
|
+
{
|
|
60
|
+
foo: "test",
|
|
61
|
+
bar: 123
|
|
62
|
+
},
|
|
63
|
+
ctx
|
|
64
|
+
)
|
|
65
|
+
).toBe(`test123${ctx.baseUrl}`);
|
|
66
|
+
});
|
|
67
|
+
it("should work with computed getters", async () => {
|
|
68
|
+
const ctx = defineResourceSearchOptions({
|
|
69
|
+
baseUrl: `https://open.bydata.de/api/hub/search`,
|
|
70
|
+
index: "dataset",
|
|
71
|
+
schema: z.object({
|
|
72
|
+
foo: z.string(),
|
|
73
|
+
bar: z.number()
|
|
74
|
+
}),
|
|
75
|
+
facets: ["categories", "publisher", "catalog"]
|
|
76
|
+
});
|
|
77
|
+
const getters = defineGetters({
|
|
78
|
+
schema: z.object({
|
|
79
|
+
foo: z.string(),
|
|
80
|
+
bar: z.number()
|
|
81
|
+
}),
|
|
82
|
+
ctx,
|
|
83
|
+
getters: {
|
|
84
|
+
getTestResult: (data2) => {
|
|
85
|
+
return `${data2.foo}${data2.bar}`;
|
|
86
|
+
},
|
|
87
|
+
getOtherTestResult: () => {
|
|
88
|
+
return "otherTest";
|
|
89
|
+
},
|
|
90
|
+
getBaseUrl: (_, ctx2) => {
|
|
91
|
+
return ctx2?.baseUrl;
|
|
92
|
+
},
|
|
93
|
+
getFullTestResult: (data2, ctx2) => {
|
|
94
|
+
return `${data2.foo}${data2.bar}${ctx2?.baseUrl}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
const data = ref({
|
|
99
|
+
foo: "test",
|
|
100
|
+
bar: 123
|
|
101
|
+
});
|
|
102
|
+
const computedGetters = toComputedGetters(
|
|
103
|
+
{ data, getters, ctx }
|
|
104
|
+
);
|
|
105
|
+
expect(computedGetters.getTestResult.value).toBe("test123");
|
|
106
|
+
expect(computedGetters.getOtherTestResult.value).toBe("otherTest");
|
|
107
|
+
expect(computedGetters.getBaseUrl.value).toBe(ctx.baseUrl);
|
|
108
|
+
expect(computedGetters.getFullTestResult.value).toBe(`test123${ctx.baseUrl}`);
|
|
109
|
+
data.value = {
|
|
110
|
+
foo: "foo",
|
|
111
|
+
bar: 456
|
|
112
|
+
};
|
|
113
|
+
await nextTick();
|
|
114
|
+
expect(computedGetters.getTestResult.value).toBe("foo456");
|
|
115
|
+
expect(computedGetters.getOtherTestResult.value).toBe("otherTest");
|
|
116
|
+
expect(computedGetters.getBaseUrl.value).toBe(ctx.baseUrl);
|
|
117
|
+
expect(computedGetters.getFullTestResult.value).toBe(`foo456${ctx.baseUrl}`);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { QueryClient } from "@tanstack/vue-query";
|
|
4
|
+
import { computed, effectScope, nextTick, reactive, ref, toRef, toValue, watch } from "vue-demi";
|
|
5
|
+
import { syncRef, until, watchOnce } from "@vueuse/core";
|
|
6
|
+
import { useResourceFactory } from "../useResourceFactory.js";
|
|
7
|
+
import { defaultQueryClientConfig } from "./testUtils.js";
|
|
8
|
+
const schema = z.object({
|
|
9
|
+
foo: z.string()
|
|
10
|
+
});
|
|
11
|
+
function createUseResource(qc, cb) {
|
|
12
|
+
const useResource = useResourceFactory({
|
|
13
|
+
baseUrl: "https://example.org",
|
|
14
|
+
schema,
|
|
15
|
+
index: "dataset",
|
|
16
|
+
indexSearch: "dataset",
|
|
17
|
+
qc,
|
|
18
|
+
enabled: true,
|
|
19
|
+
ctx: {
|
|
20
|
+
locale: "en"
|
|
21
|
+
},
|
|
22
|
+
fetcherFn: cb
|
|
23
|
+
}, (data, ctx) => {
|
|
24
|
+
const getHelloFoo = computed(() => `$hello ${toValue(data)?.foo}`);
|
|
25
|
+
const getFoo = computed(() => toValue(data)?.foo);
|
|
26
|
+
const getFooCapitalized = computed(() => toValue(data)?.foo.toUpperCase());
|
|
27
|
+
const getContextualFoo = computed(() => `${toValue(data)?.foo}-${toValue(ctx)?.locale}`);
|
|
28
|
+
return {
|
|
29
|
+
getHelloFoo,
|
|
30
|
+
getFoo,
|
|
31
|
+
getFooCapitalized,
|
|
32
|
+
getContextualFoo
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
return useResource;
|
|
36
|
+
}
|
|
37
|
+
describe("useResourceFactory", () => {
|
|
38
|
+
const fetcherSpy = vi.fn(({ id }) => {
|
|
39
|
+
const mockResult = {
|
|
40
|
+
success: true,
|
|
41
|
+
result: {
|
|
42
|
+
foo: id ?? "bar"
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
return Promise.resolve(mockResult);
|
|
46
|
+
});
|
|
47
|
+
let useResource = {};
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
vi.clearAllMocks();
|
|
50
|
+
const qc = new QueryClient(defaultQueryClientConfig);
|
|
51
|
+
useResource = createUseResource(qc, fetcherSpy);
|
|
52
|
+
});
|
|
53
|
+
it("should return a function", async () => {
|
|
54
|
+
expect(useResource).toBeTypeOf("function");
|
|
55
|
+
});
|
|
56
|
+
it("should call fetcherFn", async () => {
|
|
57
|
+
const scope = effectScope(true);
|
|
58
|
+
const hasResult = ref(false);
|
|
59
|
+
const result = ref({});
|
|
60
|
+
const helloFoo = ref("");
|
|
61
|
+
const id = ref("some-id");
|
|
62
|
+
const headers = ref({ foo: "bar" });
|
|
63
|
+
await scope.run(async () => {
|
|
64
|
+
const resourceQuery = useResource(id, { headers });
|
|
65
|
+
syncRef(resourceQuery.result, result, { direction: "ltr" });
|
|
66
|
+
watchOnce(() => resourceQuery.query.isSuccess.value, async (_count) => {
|
|
67
|
+
if (resourceQuery.isSuccess.value === true)
|
|
68
|
+
resourceQuery.result.value;
|
|
69
|
+
await nextTick();
|
|
70
|
+
hasResult.value = true;
|
|
71
|
+
});
|
|
72
|
+
watch(() => resourceQuery.resultEnhanced.value?.getHelloFoo, (value) => {
|
|
73
|
+
helloFoo.value = value;
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
await until(() => !!hasResult.value).toBe(true);
|
|
77
|
+
expect(fetcherSpy).toHaveBeenCalledTimes(1);
|
|
78
|
+
expect(fetcherSpy).toHaveBeenCalledWith({
|
|
79
|
+
baseUrl: "https://example.org",
|
|
80
|
+
resource: "dataset",
|
|
81
|
+
id: "some-id",
|
|
82
|
+
headers: { foo: "bar" }
|
|
83
|
+
});
|
|
84
|
+
expect(result.value).toEqual({ foo: "some-id" });
|
|
85
|
+
id.value = "some-other-id";
|
|
86
|
+
headers.value = { foo: "baz" };
|
|
87
|
+
await nextTick();
|
|
88
|
+
await nextTick();
|
|
89
|
+
expect(fetcherSpy).toHaveBeenCalledTimes(2);
|
|
90
|
+
await until(() => !!result.value).toBe(true);
|
|
91
|
+
expect(result.value).toEqual({ foo: "some-other-id" });
|
|
92
|
+
expect(helloFoo.value).toBe("$hello some-other-id");
|
|
93
|
+
});
|
|
94
|
+
it("should properly apply setup", async () => {
|
|
95
|
+
const scope = effectScope(true);
|
|
96
|
+
const hasResult = ref(false);
|
|
97
|
+
const result = ref({});
|
|
98
|
+
const id = ref("some-id");
|
|
99
|
+
await scope.run(async () => {
|
|
100
|
+
const resourceQuery = reactive(useResource(id));
|
|
101
|
+
syncRef(toRef(resourceQuery.result), result, { direction: "ltr" });
|
|
102
|
+
watchOnce(() => resourceQuery.isSuccess, async (_count) => {
|
|
103
|
+
await nextTick();
|
|
104
|
+
hasResult.value = true;
|
|
105
|
+
});
|
|
106
|
+
await until(() => !!resourceQuery.isSuccess).toBe(true);
|
|
107
|
+
await nextTick();
|
|
108
|
+
expect(resourceQuery.isSuccess).toBe(true);
|
|
109
|
+
if (resourceQuery.isSuccess) {
|
|
110
|
+
expect(resourceQuery.resultEnhanced.getFoo).toBe("some-id");
|
|
111
|
+
expect(resourceQuery.resultEnhanced.getFooCapitalized).toBe("SOME-ID");
|
|
112
|
+
expect(resourceQuery.resultEnhanced.getContextualFoo).toBe("some-id-en");
|
|
113
|
+
}
|
|
114
|
+
id.value = "some-other-id";
|
|
115
|
+
await nextTick();
|
|
116
|
+
await until(() => !!resourceQuery.isSuccess).toBe(true);
|
|
117
|
+
expect(resourceQuery.isSuccess).toBe(true);
|
|
118
|
+
if (resourceQuery.isSuccess) {
|
|
119
|
+
expect(resourceQuery.resultEnhanced.getFoo).toBe("some-other-id");
|
|
120
|
+
expect(resourceQuery.resultEnhanced.getFooCapitalized).toBe("SOME-OTHER-ID");
|
|
121
|
+
expect(resourceQuery.resultEnhanced.getContextualFoo).toBe("some-other-id-en");
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { QueryClient } from "@tanstack/vue-query";
|
|
4
|
+
import { computed, effectScope, nextTick, ref, toValue } from "vue-demi";
|
|
5
|
+
import { until, watchOnce } from "@vueuse/core";
|
|
6
|
+
import { useSearchFactory } from "../useSearchFactory.js";
|
|
7
|
+
import { defaultQueryClientConfig } from "./testUtils.js";
|
|
8
|
+
function createUseSearch(qc, cb) {
|
|
9
|
+
const useSearch = useSearchFactory({
|
|
10
|
+
ctx: {
|
|
11
|
+
baseUrl: "https://foo.bar"
|
|
12
|
+
},
|
|
13
|
+
schema: z.object({
|
|
14
|
+
foo: z.string(),
|
|
15
|
+
bar: z.number()
|
|
16
|
+
}),
|
|
17
|
+
index: "dataset",
|
|
18
|
+
qc,
|
|
19
|
+
fetcherFn: cb
|
|
20
|
+
}, (data) => {
|
|
21
|
+
const v = toValue(data);
|
|
22
|
+
return {
|
|
23
|
+
foobar: computed(() => `${v?.foo}${v?.bar}`),
|
|
24
|
+
barfoo: computed(() => `${v?.bar}${v?.foo}`),
|
|
25
|
+
nono: 123
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
return useSearch;
|
|
29
|
+
}
|
|
30
|
+
describe("useSearchFactory", () => {
|
|
31
|
+
const fetcherSpy = vi.fn((_) => {
|
|
32
|
+
const mockResult = {
|
|
33
|
+
result: {
|
|
34
|
+
count: 1e3,
|
|
35
|
+
facets: [],
|
|
36
|
+
index: "dataset",
|
|
37
|
+
results: [{ foo: "test", bar: 123 }, { foo: "test2", bar: 456 }]
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
return Promise.resolve(mockResult);
|
|
41
|
+
});
|
|
42
|
+
let useSearch = {};
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
vi.clearAllMocks();
|
|
45
|
+
const qc = new QueryClient(defaultQueryClientConfig);
|
|
46
|
+
useSearch = createUseSearch(qc, fetcherSpy);
|
|
47
|
+
});
|
|
48
|
+
it("should return a function", async () => {
|
|
49
|
+
expect(useSearch).toBeTypeOf("function");
|
|
50
|
+
});
|
|
51
|
+
it("should call fetcherFn", async () => {
|
|
52
|
+
const scope = effectScope(true);
|
|
53
|
+
const qExpected = "query";
|
|
54
|
+
const limitExpected = 123;
|
|
55
|
+
const headerExpected = {
|
|
56
|
+
foo: "bar"
|
|
57
|
+
};
|
|
58
|
+
const q = ref(qExpected);
|
|
59
|
+
const limit = ref(limitExpected);
|
|
60
|
+
const headers = ref(headerExpected);
|
|
61
|
+
const count = ref(-1);
|
|
62
|
+
const results = ref([]);
|
|
63
|
+
await scope.run(async () => {
|
|
64
|
+
const { getSearchResultsCount, getSearchResultsEnhanced } = useSearch({
|
|
65
|
+
queryParams: {
|
|
66
|
+
q,
|
|
67
|
+
limit
|
|
68
|
+
},
|
|
69
|
+
headers
|
|
70
|
+
});
|
|
71
|
+
watchOnce(() => getSearchResultsCount.value, (_count) => {
|
|
72
|
+
count.value = _count;
|
|
73
|
+
if (getSearchResultsEnhanced.value) {
|
|
74
|
+
results.value = getSearchResultsEnhanced.value;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
await until(() => count.value !== -1).toBe(true);
|
|
79
|
+
expect(fetcherSpy).toHaveBeenCalledTimes(1);
|
|
80
|
+
expect(fetcherSpy.mock.calls[0]?.[0].params.q).toBe(qExpected);
|
|
81
|
+
expect(fetcherSpy.mock.calls[0]?.[0].params.limit).toBe(limitExpected);
|
|
82
|
+
expect(fetcherSpy.mock.calls[0]?.[0].params.facets).toEqual({});
|
|
83
|
+
expect(fetcherSpy.mock.calls[0]?.[0].headers.foo).toBe("bar");
|
|
84
|
+
expect(results.value).toHaveLength(2);
|
|
85
|
+
expect(results.value[0].foobar).toBe("test123");
|
|
86
|
+
expect(results.value[0].barfoo).toBe("123test");
|
|
87
|
+
expect(results.value[1].foobar).toBe("test2456");
|
|
88
|
+
expect(results.value[1].barfoo).toBe("456test2");
|
|
89
|
+
q.value = "foo";
|
|
90
|
+
q.value = "new query";
|
|
91
|
+
limit.value = 456;
|
|
92
|
+
headers.value = {
|
|
93
|
+
foo: "baz"
|
|
94
|
+
};
|
|
95
|
+
await nextTick();
|
|
96
|
+
expect(fetcherSpy).toHaveBeenCalledTimes(2);
|
|
97
|
+
expect(fetcherSpy.mock.calls[1]?.[0].params.q).toBe("new query");
|
|
98
|
+
expect(fetcherSpy.mock.calls[1]?.[0].params.limit).toBe(456);
|
|
99
|
+
expect(fetcherSpy.mock.calls[1]?.[0].params.facets).toEqual({});
|
|
100
|
+
expect(fetcherSpy.mock.calls[1]?.[0].headers.foo).toBe("baz");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { reactive, ref } from "vue-demi";
|
|
3
|
+
import { useSearchQueryParams } from "../useSearchQueryParams.js";
|
|
4
|
+
describe("useSearchQueryParams", () => {
|
|
5
|
+
it("should provide queryParams", () => {
|
|
6
|
+
const { queryParams } = useSearchQueryParams();
|
|
7
|
+
expect(queryParams).toBeDefined();
|
|
8
|
+
expect(reactive(queryParams)).toStrictEqual({
|
|
9
|
+
aggregation: null,
|
|
10
|
+
aggregationAllFields: null,
|
|
11
|
+
aggregationFields: null,
|
|
12
|
+
autocomplete: null,
|
|
13
|
+
bboxMaxLat: null,
|
|
14
|
+
bboxMaxLon: null,
|
|
15
|
+
bboxMinLat: null,
|
|
16
|
+
bboxMinLon: null,
|
|
17
|
+
boost: null,
|
|
18
|
+
countryData: null,
|
|
19
|
+
dataServices: null,
|
|
20
|
+
facetGroupOperator: null,
|
|
21
|
+
facetOperator: null,
|
|
22
|
+
fields: null,
|
|
23
|
+
filterDistributions: null,
|
|
24
|
+
globalAggregation: null,
|
|
25
|
+
includes: null,
|
|
26
|
+
limit: null,
|
|
27
|
+
maxDate: null,
|
|
28
|
+
minDate: null,
|
|
29
|
+
maxScoring: null,
|
|
30
|
+
minScoring: null,
|
|
31
|
+
page: null,
|
|
32
|
+
q: null,
|
|
33
|
+
scroll: null,
|
|
34
|
+
sort: null,
|
|
35
|
+
showScore: null,
|
|
36
|
+
vocabulary: null,
|
|
37
|
+
superCatalogue: null
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
it("should reactively update queryParams when using setters", () => {
|
|
41
|
+
const {
|
|
42
|
+
queryParams,
|
|
43
|
+
setFacetOperator,
|
|
44
|
+
setDataScope,
|
|
45
|
+
setDataServices,
|
|
46
|
+
setSuperCatalogue,
|
|
47
|
+
setDatasetGeoBounds,
|
|
48
|
+
setFacetGroupOperator,
|
|
49
|
+
setLimit,
|
|
50
|
+
setMinScoring,
|
|
51
|
+
setPage,
|
|
52
|
+
setQuery,
|
|
53
|
+
setSort
|
|
54
|
+
} = useSearchQueryParams();
|
|
55
|
+
setFacetOperator("AND");
|
|
56
|
+
setDataScope(true);
|
|
57
|
+
setDataServices(false);
|
|
58
|
+
setSuperCatalogue("test");
|
|
59
|
+
setDatasetGeoBounds({ minLon: 1, maxLon: 2, minLat: 3, maxLat: 4 });
|
|
60
|
+
setFacetGroupOperator("OR");
|
|
61
|
+
setLimit(10);
|
|
62
|
+
setMinScoring(0.5);
|
|
63
|
+
setPage(1);
|
|
64
|
+
setQuery("test");
|
|
65
|
+
setSort("title+asc");
|
|
66
|
+
expect(queryParams.facetOperator.value).toBe("AND");
|
|
67
|
+
expect(queryParams.countryData.value).toBe(true);
|
|
68
|
+
expect(queryParams.dataServices.value).toBe(false);
|
|
69
|
+
expect(queryParams.superCatalogue.value).toBe("test");
|
|
70
|
+
expect(queryParams.bboxMinLon.value).toBe(1);
|
|
71
|
+
expect(queryParams.bboxMaxLon.value).toBe(2);
|
|
72
|
+
expect(queryParams.bboxMinLat.value).toBe(3);
|
|
73
|
+
expect(queryParams.bboxMaxLat.value).toBe(4);
|
|
74
|
+
expect(queryParams.facetGroupOperator.value).toBe("OR");
|
|
75
|
+
expect(queryParams.limit.value).toBe(10);
|
|
76
|
+
expect(queryParams.minScoring.value).toBe(0.5);
|
|
77
|
+
expect(queryParams.page.value).toBe(1);
|
|
78
|
+
expect(queryParams.q.value).toBe("test");
|
|
79
|
+
expect(queryParams.sort.value).toBe("title+asc");
|
|
80
|
+
});
|
|
81
|
+
it("should reactively update queryParams when updating reactive params", () => {
|
|
82
|
+
const facetOperator = ref("AND");
|
|
83
|
+
const { queryParams } = useSearchQueryParams({
|
|
84
|
+
facetOperator
|
|
85
|
+
});
|
|
86
|
+
facetOperator.value = "OR";
|
|
87
|
+
expect(queryParams.facetOperator.value).toBe("OR");
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { InjectionKey, MaybeRefOrGetter } from 'vue-demi';
|
|
2
|
+
export interface LocaleMessages {
|
|
3
|
+
[key: string]: LocaleMessages | string;
|
|
4
|
+
}
|
|
5
|
+
export type DateFormats = 'short' | 'medium' | 'long';
|
|
6
|
+
export type DateLike = Date | string | number;
|
|
7
|
+
export type DateFormatStrings = {
|
|
8
|
+
[key in DateFormats]?: string;
|
|
9
|
+
};
|
|
10
|
+
export interface LocaleInstance {
|
|
11
|
+
name: string;
|
|
12
|
+
messages: MaybeRefOrGetter<LocaleMessages>;
|
|
13
|
+
currentLocale: MaybeRefOrGetter<string>;
|
|
14
|
+
fallbackLocale: MaybeRefOrGetter<string>;
|
|
15
|
+
t: (key: string, ...params: unknown[]) => string;
|
|
16
|
+
n: (value: number) => string;
|
|
17
|
+
d: (value: DateLike, format?: DateFormats, locale?: Intl.LocalesArgument) => string;
|
|
18
|
+
}
|
|
19
|
+
export interface LocaleOptions {
|
|
20
|
+
messages?: LocaleMessages;
|
|
21
|
+
locale?: string;
|
|
22
|
+
fallback?: string;
|
|
23
|
+
dateFormatStrings?: DateFormatStrings;
|
|
24
|
+
adapter?: LocaleInstance;
|
|
25
|
+
}
|
|
26
|
+
export declare const localeKey: InjectionKey<LocaleInstance>;
|
|
27
|
+
export declare function useLocale(): LocaleInstance;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { inject } from "vue-demi";
|
|
2
|
+
export const localeKey = Symbol("locale");
|
|
3
|
+
export function useLocale() {
|
|
4
|
+
const locale = inject(localeKey);
|
|
5
|
+
if (!locale)
|
|
6
|
+
throw new Error("[piveau-kit] Could not inject locale instance. Did you forget to install the `@piveau/sdk-vue` plugin?");
|
|
7
|
+
return locale;
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function usePropertyTable(): void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SearchParamsBase, SearchResult } from '@piveau/sdk-core';
|
|
2
|
+
import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue-demi';
|
|
3
|
+
import type * as z from 'zod';
|
|
4
|
+
import type { QueryClient } from '@tanstack/vue-query';
|
|
5
|
+
export interface DefineHubSearchOptions<TIndex extends string, TFacetNames extends string, TModelSchema> {
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
index: TIndex;
|
|
8
|
+
indexDetails?: string;
|
|
9
|
+
enabled?: MaybeRefOrGetter<boolean>;
|
|
10
|
+
schema: z.Schema<TModelSchema>;
|
|
11
|
+
facets?: TFacetNames[];
|
|
12
|
+
defaultOptions?: {
|
|
13
|
+
dateFormatter?: (datetime: string) => string;
|
|
14
|
+
locale?: MaybeRefOrGetter<string>;
|
|
15
|
+
searchFetcherFn?: () => Promise<SearchResult<TModelSchema>>;
|
|
16
|
+
resourceFetcherFn?: () => Promise<SearchResult<TModelSchema>>;
|
|
17
|
+
qc?: QueryClient;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export type _ExtractComputedRefsFromSetup<TSetup> = {
|
|
21
|
+
[K in keyof TSetup as TSetup[K] extends ComputedRef ? K : never]: TSetup[K];
|
|
22
|
+
};
|
|
23
|
+
export type _ExtractComputedRefsFromSetupUnrwapped<TSetup> = TSetup extends _ExtractComputedRefsFromSetup<TSetup> ? {
|
|
24
|
+
[K in keyof _ExtractComputedRefsFromSetup<TSetup>]: TSetup[K] extends ComputedRef<infer t> ? t : never;
|
|
25
|
+
} : never;
|
|
26
|
+
export declare function defineHubSearch<TIndex extends string, TFacetNames extends string, TModelSchema, TSetup extends object>(options: DefineHubSearchOptions<TIndex, TFacetNames, TModelSchema>, setup?: (data: MaybeRefOrGetter<TModelSchema | undefined>, ctx?: DefineHubSearchOptions<TIndex, TFacetNames, TModelSchema>) => TSetup): {
|
|
27
|
+
useSearch: (params: import("./useSearchFactory.js").UseSearchParams<TFacetNames>) => import("./useSearchFactory.js").UseSearchFactoryReturn<TFacetNames, TModelSchema, TSetup>;
|
|
28
|
+
useResource: import("./useResourceFactory.js").UseResourceFunction<TSetup, TModelSchema | undefined>;
|
|
29
|
+
refSyncedWithRouteQuery: <T extends string | number, K = T extends string ? string : T extends number ? number : never>(searchParam: keyof SearchParamsBase, defaultValue?: MaybeRefOrGetter<T> | undefined) => Ref<K>;
|
|
30
|
+
refSyncedWithRouteQueryFacet: <T_1 extends string[]>(searchParam: TFacetNames, defaultValue?: MaybeRefOrGetter<T_1> | undefined) => Ref<string[]>;
|
|
31
|
+
setup: ((data: MaybeRefOrGetter<TModelSchema | undefined>, ctx?: DefineHubSearchOptions<TIndex, TFacetNames, TModelSchema>) => TSetup) | undefined;
|
|
32
|
+
};
|
|
33
|
+
export type HubSearchIntegrationOptions<TModelSchema> = DefineHubSearchOptions<string, string, TModelSchema>;
|
|
34
|
+
export declare function defineHubSearchIntegration<TModelSchema, TSetup>(schema: z.Schema<TModelSchema>, setup: (data: MaybeRefOrGetter<TModelSchema | undefined>, ctx?: HubSearchIntegrationOptions<TModelSchema>) => TSetup): {
|
|
35
|
+
setup: (data: MaybeRefOrGetter<TModelSchema | undefined>, ctx?: HubSearchIntegrationOptions<TModelSchema>) => TSetup;
|
|
36
|
+
schema: z.ZodType<TModelSchema, z.ZodTypeDef, TModelSchema>;
|
|
37
|
+
};
|