@asd20/ui-next 2.2.1 → 2.3.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/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/components/organisms/Asd20SiteSearch/index.vue +29 -18
- package/src/components/templates/Asd20404Template/index.vue +129 -9
- package/src/helpers/buildFileSearchUrl.js +37 -9
- package/src/helpers/logSearchAnalytics.js +16 -4
- package/src/helpers/mapPageQueryResultToPageTemplateProps.js +16 -6
- package/src/helpers/queryAiSite.js +9 -1
- package/src/helpers/queryDepartments.js +28 -12
- package/src/helpers/queryEvents.js +79 -31
- package/src/helpers/queryFiles.js +86 -25
- package/src/helpers/queryMessages.js +101 -34
- package/src/helpers/queryPages.js +87 -20
- package/src/helpers/runtimeConfig.js +80 -0
- package/src/helpers/searchProxyUrl.js +43 -0
- package/src/helpers/sendAccessibilityIssue.js +26 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
# [2.3.0](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.2.2...ui-next-v2.3.0) (2026-04-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* harden search functions for events, page, messages, etc. ([5c9893b](https://github.com/academydistrict20/asd20-ui-next/commit/5c9893b1c943ce1df54c35c97fc0d14b42c72221))
|
|
9
|
+
|
|
10
|
+
## [2.2.2](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.2.1...ui-next-v2.2.2) (2026-04-13)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* fix log search analytics not executing ([5c94742](https://github.com/academydistrict20/asd20-ui-next/commit/5c94742ed63517c5f5ba918583b052606951a842))
|
|
16
|
+
|
|
3
17
|
## [2.2.1](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.2.0...ui-next-v2.2.1) (2026-04-07)
|
|
4
18
|
|
|
5
19
|
|
package/package.json
CHANGED
|
@@ -979,8 +979,9 @@ export default {
|
|
|
979
979
|
},
|
|
980
980
|
feedbackFormUrlRaw() {
|
|
981
981
|
if (this.aiSearchFeedbackFormUrl) return this.aiSearchFeedbackFormUrl
|
|
982
|
-
|
|
983
|
-
|
|
982
|
+
const runtimeConfig = this.resolveRuntimeConfig()
|
|
983
|
+
if (runtimeConfig.aiSearchFeedbackFormUrl)
|
|
984
|
+
return runtimeConfig.aiSearchFeedbackFormUrl
|
|
984
985
|
return ''
|
|
985
986
|
},
|
|
986
987
|
|
|
@@ -2213,6 +2214,28 @@ export default {
|
|
|
2213
2214
|
this.$emit('update:active', false)
|
|
2214
2215
|
},
|
|
2215
2216
|
|
|
2217
|
+
resolveRuntimeConfig() {
|
|
2218
|
+
const runtimeConfig =
|
|
2219
|
+
this.$config ||
|
|
2220
|
+
this.$store?.$config ||
|
|
2221
|
+
this.$?.appContext?.config?.globalProperties?.$config ||
|
|
2222
|
+
{}
|
|
2223
|
+
|
|
2224
|
+
if (
|
|
2225
|
+
runtimeConfig.public &&
|
|
2226
|
+
typeof runtimeConfig.public === 'object' &&
|
|
2227
|
+
!Array.isArray(runtimeConfig.public)
|
|
2228
|
+
) {
|
|
2229
|
+
return runtimeConfig.public
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
return runtimeConfig
|
|
2233
|
+
},
|
|
2234
|
+
|
|
2235
|
+
resolveFunctionsEndpoint() {
|
|
2236
|
+
return this.resolveRuntimeConfig().functionsEndpoint || ''
|
|
2237
|
+
},
|
|
2238
|
+
|
|
2216
2239
|
toggleKeywords(turnId) {
|
|
2217
2240
|
this.expandedKeywordsTurnId =
|
|
2218
2241
|
this.expandedKeywordsTurnId === turnId ? null : turnId
|
|
@@ -2253,10 +2276,7 @@ export default {
|
|
|
2253
2276
|
const response = await logSearchFeedback({
|
|
2254
2277
|
searchLogId: turn.searchLogId,
|
|
2255
2278
|
feedbackValue,
|
|
2256
|
-
functionsEndpoint:
|
|
2257
|
-
this.$config && this.$config.functionsEndpoint
|
|
2258
|
-
? this.$config.functionsEndpoint
|
|
2259
|
-
: null,
|
|
2279
|
+
functionsEndpoint: this.resolveFunctionsEndpoint() || null,
|
|
2260
2280
|
})
|
|
2261
2281
|
|
|
2262
2282
|
if (response && response.ok) {
|
|
@@ -2330,10 +2350,7 @@ export default {
|
|
|
2330
2350
|
const response = await logSearchFeedback({
|
|
2331
2351
|
searchLogId: targetTurn.searchLogId,
|
|
2332
2352
|
improvementFeedback,
|
|
2333
|
-
functionsEndpoint:
|
|
2334
|
-
this.$config && this.$config.functionsEndpoint
|
|
2335
|
-
? this.$config.functionsEndpoint
|
|
2336
|
-
: null,
|
|
2353
|
+
functionsEndpoint: this.resolveFunctionsEndpoint() || null,
|
|
2337
2354
|
})
|
|
2338
2355
|
|
|
2339
2356
|
if (response && response.ok) {
|
|
@@ -2543,10 +2560,7 @@ export default {
|
|
|
2543
2560
|
: null,
|
|
2544
2561
|
languageCode: this.getSearchLanguageCode(),
|
|
2545
2562
|
isFollowup: false,
|
|
2546
|
-
functionsEndpoint:
|
|
2547
|
-
this.$config && this.$config.functionsEndpoint
|
|
2548
|
-
? this.$config.functionsEndpoint
|
|
2549
|
-
: null,
|
|
2563
|
+
functionsEndpoint: this.resolveFunctionsEndpoint() || null,
|
|
2550
2564
|
})
|
|
2551
2565
|
}
|
|
2552
2566
|
},
|
|
@@ -2797,10 +2811,7 @@ export default {
|
|
|
2797
2811
|
: null,
|
|
2798
2812
|
languageCode: this.getSearchLanguageCode(),
|
|
2799
2813
|
isFollowup: isFollowUpQuestion,
|
|
2800
|
-
functionsEndpoint:
|
|
2801
|
-
this.$config && this.$config.functionsEndpoint
|
|
2802
|
-
? this.$config.functionsEndpoint
|
|
2803
|
-
: null,
|
|
2814
|
+
functionsEndpoint: this.resolveFunctionsEndpoint() || null,
|
|
2804
2815
|
aiResponse: assistantResponseForAnalytics || null,
|
|
2805
2816
|
})
|
|
2806
2817
|
if (assistantTurnId) {
|
|
@@ -86,6 +86,50 @@ import pageTemplateMixin from '../../../mixins/pageTemplateMixin'
|
|
|
86
86
|
|
|
87
87
|
// import Asd20PageContent from '../../../components/organisms/Asd20PageContent'
|
|
88
88
|
|
|
89
|
+
const MAX_SUGGESTED_PAGES = 10
|
|
90
|
+
const NO_PAGE_RESULTS_FALLBACKS = [
|
|
91
|
+
{
|
|
92
|
+
id: 'calendar',
|
|
93
|
+
slug: 'calendar',
|
|
94
|
+
title: 'Calendar',
|
|
95
|
+
url: '/calendar',
|
|
96
|
+
isNoResultsFallback: true,
|
|
97
|
+
categories: ['App'],
|
|
98
|
+
pageTypeId: 'application',
|
|
99
|
+
metaDescription: 'View district and school calendars.',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'directory',
|
|
103
|
+
slug: 'directory',
|
|
104
|
+
title: 'Directory',
|
|
105
|
+
url: '/directory',
|
|
106
|
+
isNoResultsFallback: true,
|
|
107
|
+
categories: ['App'],
|
|
108
|
+
pageTypeId: 'application',
|
|
109
|
+
metaDescription: 'Find staff and department contacts.',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'school-directory',
|
|
113
|
+
slug: 'schools',
|
|
114
|
+
title: 'Schools',
|
|
115
|
+
url: '/schools',
|
|
116
|
+
isNoResultsFallback: true,
|
|
117
|
+
categories: ['App'],
|
|
118
|
+
pageTypeId: 'application',
|
|
119
|
+
metaDescription: 'Browse all Academy District 20 schools.',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: 'help-desk',
|
|
123
|
+
slug: 'help-desk',
|
|
124
|
+
title: 'Help Desk',
|
|
125
|
+
url: 'https://www.asd20.org/help-desk/',
|
|
126
|
+
isNoResultsFallback: true,
|
|
127
|
+
categories: ['Support'],
|
|
128
|
+
pageTypeId: 'detail-page',
|
|
129
|
+
metaDescription: 'Get support from the Academy District 20 Help Desk.',
|
|
130
|
+
},
|
|
131
|
+
]
|
|
132
|
+
|
|
89
133
|
export default {
|
|
90
134
|
components: {
|
|
91
135
|
Asd20List,
|
|
@@ -108,6 +152,83 @@ export default {
|
|
|
108
152
|
},
|
|
109
153
|
|
|
110
154
|
methods: {
|
|
155
|
+
cloneFallbackPages() {
|
|
156
|
+
return NO_PAGE_RESULTS_FALLBACKS.map(page => ({
|
|
157
|
+
...page,
|
|
158
|
+
categories: Array.isArray(page.categories) ? [...page.categories] : [],
|
|
159
|
+
}))
|
|
160
|
+
},
|
|
161
|
+
normalizeSearchText(value = '') {
|
|
162
|
+
return String(value)
|
|
163
|
+
.toLowerCase()
|
|
164
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
165
|
+
.trim()
|
|
166
|
+
},
|
|
167
|
+
getKeywordTokens(keywords = '') {
|
|
168
|
+
return Array.from(
|
|
169
|
+
new Set(
|
|
170
|
+
this.normalizeSearchText(keywords)
|
|
171
|
+
.split(' ')
|
|
172
|
+
.filter(token => token.length >= 3)
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
},
|
|
176
|
+
getSuggestedPageDedupKey(page = {}) {
|
|
177
|
+
const normalizedUrl = this.normalizeSearchText(page.url || '')
|
|
178
|
+
|
|
179
|
+
if (normalizedUrl) return `url:${normalizedUrl}`
|
|
180
|
+
|
|
181
|
+
const normalizedTitle = this.normalizeSearchText(page.title || '')
|
|
182
|
+
const normalizedSlug = this.normalizeSearchText(page.slug || '')
|
|
183
|
+
|
|
184
|
+
return `title:${normalizedTitle}|slug:${normalizedSlug}`
|
|
185
|
+
},
|
|
186
|
+
dedupeSuggestedPages(pages = []) {
|
|
187
|
+
const seen = new Set()
|
|
188
|
+
|
|
189
|
+
return pages.filter(page => {
|
|
190
|
+
const dedupKey = this.getSuggestedPageDedupKey(page)
|
|
191
|
+
|
|
192
|
+
if (!dedupKey || seen.has(dedupKey)) return false
|
|
193
|
+
|
|
194
|
+
seen.add(dedupKey)
|
|
195
|
+
return true
|
|
196
|
+
})
|
|
197
|
+
},
|
|
198
|
+
isRelevantSuggestedPage(page = {}, keywords = '') {
|
|
199
|
+
if (page.isNoResultsFallback) return true
|
|
200
|
+
|
|
201
|
+
const normalizedKeywords = this.normalizeSearchText(keywords)
|
|
202
|
+
const keywordTokens = this.getKeywordTokens(keywords)
|
|
203
|
+
|
|
204
|
+
if (!normalizedKeywords || keywordTokens.length === 0) return false
|
|
205
|
+
|
|
206
|
+
const haystack = this.normalizeSearchText([
|
|
207
|
+
page.title,
|
|
208
|
+
page.slug,
|
|
209
|
+
page.url,
|
|
210
|
+
page.metaDescription,
|
|
211
|
+
...(Array.isArray(page.categories) ? page.categories : []),
|
|
212
|
+
].join(' '))
|
|
213
|
+
|
|
214
|
+
if (!haystack) return false
|
|
215
|
+
if (haystack.includes(normalizedKeywords)) return true
|
|
216
|
+
|
|
217
|
+
return keywordTokens.some(token => haystack.includes(token))
|
|
218
|
+
},
|
|
219
|
+
sanitizeSuggestedPages(pages = [], keywords = '') {
|
|
220
|
+
const dedupedPages = this.dedupeSuggestedPages(
|
|
221
|
+
Array.isArray(pages) ? pages : []
|
|
222
|
+
)
|
|
223
|
+
const relevantPages = dedupedPages.filter(page =>
|
|
224
|
+
this.isRelevantSuggestedPage(page, keywords)
|
|
225
|
+
)
|
|
226
|
+
const resolvedPages = relevantPages.length
|
|
227
|
+
? relevantPages
|
|
228
|
+
: this.cloneFallbackPages()
|
|
229
|
+
|
|
230
|
+
return resolvedPages.slice(0, MAX_SUGGESTED_PAGES)
|
|
231
|
+
},
|
|
111
232
|
async findSuggestedPages() {
|
|
112
233
|
if (typeof window !== 'undefined') {
|
|
113
234
|
const keywords = window.location.pathname
|
|
@@ -115,17 +236,16 @@ export default {
|
|
|
115
236
|
.trim()
|
|
116
237
|
.toLowerCase()
|
|
117
238
|
try {
|
|
118
|
-
const { pages } = await queryPages({ keywords, top:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.
|
|
122
|
-
|
|
123
|
-
// console.warn('No pages found or pages is not an array')
|
|
124
|
-
this.suggestedPagesListItems = []
|
|
125
|
-
}
|
|
239
|
+
const { pages } = await queryPages({ keywords, top: MAX_SUGGESTED_PAGES })
|
|
240
|
+
|
|
241
|
+
this.suggestedPagesListItems = mapPagesToListItems(
|
|
242
|
+
this.sanitizeSuggestedPages(pages, keywords)
|
|
243
|
+
)
|
|
126
244
|
} catch (error) {
|
|
127
245
|
console.error('Error fetching suggested pages:', error)
|
|
128
|
-
this.suggestedPagesListItems =
|
|
246
|
+
this.suggestedPagesListItems = mapPagesToListItems(
|
|
247
|
+
this.cloneFallbackPages()
|
|
248
|
+
)
|
|
129
249
|
}
|
|
130
250
|
}
|
|
131
251
|
},
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { normalizeBaseUrl, resolveRuntimeConfigValue } from './runtimeConfig'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Creates a Azure Search payload
|
|
3
5
|
*
|
|
@@ -10,6 +12,11 @@ export default function buildFileSearchUrl({
|
|
|
10
12
|
owners = [],
|
|
11
13
|
tags = [],
|
|
12
14
|
top = 5,
|
|
15
|
+
runtimeConfig = null,
|
|
16
|
+
searchEndpoint = '',
|
|
17
|
+
endpoint = '',
|
|
18
|
+
searchKey = '',
|
|
19
|
+
apiVersion = '',
|
|
13
20
|
}) {
|
|
14
21
|
let query = keywords || '*'
|
|
15
22
|
|
|
@@ -35,21 +42,42 @@ export default function buildFileSearchUrl({
|
|
|
35
42
|
search: query,
|
|
36
43
|
$filter: filters.join(' and '),
|
|
37
44
|
$top: top,
|
|
38
|
-
'api-version':
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
'api-version': resolveRuntimeConfigValue({
|
|
46
|
+
explicitValue: apiVersion,
|
|
47
|
+
runtimeConfig,
|
|
48
|
+
runtimeConfigKeys: ['azureSearchVersion'],
|
|
49
|
+
envKeys: [
|
|
50
|
+
'VUE_APP_AZURE_SEARCH_API_VERSION',
|
|
51
|
+
'GRIDSOME_AZURE_SEARCH_API_VERSION',
|
|
52
|
+
],
|
|
53
|
+
}),
|
|
54
|
+
'api-key': resolveRuntimeConfigValue({
|
|
55
|
+
explicitValue: searchKey,
|
|
56
|
+
runtimeConfig,
|
|
57
|
+
runtimeConfigKeys: ['azureSearchKey'],
|
|
58
|
+
envKeys: ['VUE_APP_AZURE_SEARCH_KEY', 'GRIDSOME_AZURE_SEARCH_KEY'],
|
|
59
|
+
}),
|
|
44
60
|
}
|
|
45
61
|
|
|
46
62
|
const params = Object.entries(payload)
|
|
47
63
|
.map(([key, val]) => `${key}=${val}`)
|
|
48
64
|
.join('&')
|
|
49
65
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
66
|
+
const resolvedSearchEndpoint = resolveRuntimeConfigValue({
|
|
67
|
+
explicitValue: searchEndpoint || endpoint,
|
|
68
|
+
runtimeConfig,
|
|
69
|
+
runtimeConfigKeys: ['azureSearchEndpoint'],
|
|
70
|
+
envKeys: ['VUE_APP_AZURE_SEARCH_ENDPOINT', 'GRIDSOME_AZURE_SEARCH_ENDPOINT'],
|
|
71
|
+
})
|
|
72
|
+
if (!resolvedSearchEndpoint || !payload['api-version'] || !payload['api-key']) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
'Azure Search configuration is required for buildFileSearchUrl.'
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let url = `${normalizeBaseUrl(
|
|
79
|
+
resolvedSearchEndpoint
|
|
80
|
+
)}/indexes/files-index/docs?${params}`
|
|
53
81
|
|
|
54
82
|
return url
|
|
55
83
|
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import axios from 'axios'
|
|
2
2
|
|
|
3
|
+
function resolveNuxtWindowConfig() {
|
|
4
|
+
if (typeof window === 'undefined' || !window.__NUXT__) return {}
|
|
5
|
+
|
|
6
|
+
const runtimeConfig = window.__NUXT__.config
|
|
7
|
+
return runtimeConfig && typeof runtimeConfig === 'object' ? runtimeConfig : {}
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
export function resolveFunctionsEndpoint(payload) {
|
|
4
11
|
const explicitEndpoint =
|
|
5
12
|
payload && typeof payload.functionsEndpoint === 'string'
|
|
@@ -13,10 +20,15 @@ export function resolveFunctionsEndpoint(payload) {
|
|
|
13
20
|
if (processEndpoint) return processEndpoint
|
|
14
21
|
}
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
const nuxtConfig = resolveNuxtWindowConfig()
|
|
24
|
+
const nuxtEndpoints = [
|
|
25
|
+
nuxtConfig.functionsEndpoint,
|
|
26
|
+
nuxtConfig.public && nuxtConfig.public.functionsEndpoint,
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
for (const candidate of nuxtEndpoints) {
|
|
30
|
+
if (typeof candidate === 'string' && candidate.trim()) {
|
|
31
|
+
return candidate.trim()
|
|
20
32
|
}
|
|
21
33
|
}
|
|
22
34
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import _get from 'lodash/get'
|
|
2
2
|
|
|
3
|
+
import { resolveRuntimeConfigValue } from './runtimeConfig'
|
|
4
|
+
|
|
3
5
|
function getConfigurationValueOrDefault({
|
|
4
6
|
configurations,
|
|
5
7
|
category,
|
|
@@ -29,7 +31,10 @@ function getConfigurationValueOrDefault({
|
|
|
29
31
|
* @param {*} queryResult s
|
|
30
32
|
* @returns An object of common prop values for page templates
|
|
31
33
|
*/
|
|
32
|
-
export default function mapPageQueryResultToPageTemplateProps(
|
|
34
|
+
export default function mapPageQueryResultToPageTemplateProps(
|
|
35
|
+
queryResult,
|
|
36
|
+
options = {}
|
|
37
|
+
) {
|
|
33
38
|
// Ensure query results is defined with required properties
|
|
34
39
|
if (
|
|
35
40
|
!queryResult.page ||
|
|
@@ -189,6 +194,14 @@ export default function mapPageQueryResultToPageTemplateProps(queryResult) {
|
|
|
189
194
|
}
|
|
190
195
|
}
|
|
191
196
|
|
|
197
|
+
const defaultOrganizationId =
|
|
198
|
+
resolveRuntimeConfigValue({
|
|
199
|
+
explicitValue: options.organizationId,
|
|
200
|
+
runtimeConfig: options.runtimeConfig,
|
|
201
|
+
runtimeConfigKeys: ['organizationId'],
|
|
202
|
+
envKeys: ['GRIDSOME_ORGANIZATION_ID', 'VUE_APP_ORGANIZATION_ID'],
|
|
203
|
+
}) || '26eaf390-d8ab-11e9-a3a8-5de5bba4f125'
|
|
204
|
+
|
|
192
205
|
// Output page props
|
|
193
206
|
let pageProps = {}
|
|
194
207
|
try {
|
|
@@ -217,11 +230,8 @@ export default function mapPageQueryResultToPageTemplateProps(queryResult) {
|
|
|
217
230
|
.map(l => l.link)
|
|
218
231
|
.filter(l => l.type === 'Link'),
|
|
219
232
|
organization: pageData.ownerOrganization || {
|
|
220
|
-
id:
|
|
221
|
-
|
|
222
|
-
process.env.VUE_APP_ORGANIZATION_ID ||
|
|
223
|
-
'26eaf390-d8ab-11e9-a3a8-5de5bba4f125', // District Org Id
|
|
224
|
-
title: 'Academy District 20',
|
|
233
|
+
id: defaultOrganizationId, // District Org Id
|
|
234
|
+
title: options.organizationTitle || 'Academy District 20',
|
|
225
235
|
},
|
|
226
236
|
|
|
227
237
|
// TODO: add groups / department / committee table to Hasura
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// helpers/search/queryAiSite.js
|
|
2
2
|
import axios from 'axios'
|
|
3
3
|
|
|
4
|
+
import { resolveFunctionsEndpoint } from './logSearchAnalytics'
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Calls Azure Function / API for AI site search.
|
|
6
8
|
* Expected response shape:
|
|
@@ -14,14 +16,20 @@ export default async function queryAiSite({
|
|
|
14
16
|
organizationId = null,
|
|
15
17
|
organizationWebsite = null,
|
|
16
18
|
includeDistrictResults = true,
|
|
19
|
+
functionsEndpoint = null,
|
|
17
20
|
}) {
|
|
18
21
|
const q = (question || '').trim()
|
|
19
22
|
if (!q) {
|
|
20
23
|
return { answer: null, sources: [] }
|
|
21
24
|
}
|
|
22
25
|
|
|
26
|
+
const endpoint = resolveFunctionsEndpoint({ functionsEndpoint })
|
|
27
|
+
if (!endpoint) {
|
|
28
|
+
throw new Error('functionsEndpoint is required for AI site search.')
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
const { data } = await axios.post(
|
|
24
|
-
|
|
32
|
+
endpoint.replace(/\/$/, '') + '/ai-site-search',
|
|
25
33
|
{
|
|
26
34
|
question: q,
|
|
27
35
|
organizationId,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import arraySearch from './arraySearch'
|
|
2
2
|
import axios from 'axios'
|
|
3
|
+
import { normalizeBaseUrl, resolveRuntimeConfigValue } from './runtimeConfig'
|
|
3
4
|
// import departments from '../data/departments.json'
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
-
process.env.VUE_APP_API_ENDPOINT || process.env.GRIDSOME_API_ENDPOINT
|
|
7
|
-
let cachedDepartments = []
|
|
6
|
+
const cachedDepartmentsByEndpoint = new Map()
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Retrieve departments from API
|
|
@@ -13,15 +12,20 @@ let cachedDepartments = []
|
|
|
13
12
|
* @param {*} { commit }
|
|
14
13
|
* @returns
|
|
15
14
|
*/
|
|
16
|
-
async function getDepartments() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
async function getDepartments(apiEndpoint) {
|
|
16
|
+
const normalizedApiEndpoint = normalizeBaseUrl(apiEndpoint)
|
|
17
|
+
if (!normalizedApiEndpoint) {
|
|
18
|
+
throw new Error('apiEndpoint is required for queryDepartments.')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!cachedDepartmentsByEndpoint.has(normalizedApiEndpoint)) {
|
|
22
|
+
const { data: departments } = await axios.get(
|
|
23
|
+
`${normalizedApiEndpoint}/api/departments`
|
|
20
24
|
)
|
|
21
|
-
|
|
22
|
-
cachedDepartments = departments
|
|
25
|
+
cachedDepartmentsByEndpoint.set(normalizedApiEndpoint, departments)
|
|
23
26
|
}
|
|
24
|
-
|
|
27
|
+
|
|
28
|
+
return cachedDepartmentsByEndpoint.get(normalizedApiEndpoint) || []
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
/**
|
|
@@ -29,10 +33,22 @@ async function getDepartments() {
|
|
|
29
33
|
*
|
|
30
34
|
* @param {*} {keywords = '', top = 0}
|
|
31
35
|
*/
|
|
32
|
-
export default async function queryDepartments({
|
|
36
|
+
export default async function queryDepartments({
|
|
37
|
+
keywords = '',
|
|
38
|
+
top = 0,
|
|
39
|
+
apiEndpoint = '',
|
|
40
|
+
runtimeConfig = null,
|
|
41
|
+
}) {
|
|
33
42
|
if (!keywords) return []
|
|
34
43
|
|
|
35
|
-
|
|
44
|
+
const resolvedApiEndpoint = resolveRuntimeConfigValue({
|
|
45
|
+
explicitValue: apiEndpoint,
|
|
46
|
+
runtimeConfig,
|
|
47
|
+
runtimeConfigKeys: ['apiEndpoint'],
|
|
48
|
+
envKeys: ['VUE_APP_API_ENDPOINT', 'GRIDSOME_API_ENDPOINT'],
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
let departments = await getDepartments(resolvedApiEndpoint)
|
|
36
52
|
|
|
37
53
|
let results = departments.filter(d => {
|
|
38
54
|
let options = d.searchTerms
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import axios from 'axios'
|
|
2
2
|
|
|
3
|
+
import { hasSearchProxyRuntime, resolveSearchProxyUrl } from './searchProxyUrl'
|
|
4
|
+
import { normalizeBaseUrl, resolveRuntimeConfigValue } from './runtimeConfig'
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* Creates a Azure Search payload
|
|
5
8
|
*
|
|
@@ -73,18 +76,41 @@ export default async function queryEvents({
|
|
|
73
76
|
limit = 5,
|
|
74
77
|
requireKeywords = false,
|
|
75
78
|
indexName = 'events-index',
|
|
79
|
+
requestUrl = '',
|
|
80
|
+
runtimeConfig = null,
|
|
81
|
+
searchKey = '',
|
|
82
|
+
apiVersion = '',
|
|
83
|
+
searchEndpoint = '',
|
|
84
|
+
endpoint = '',
|
|
76
85
|
}) {
|
|
77
|
-
const
|
|
78
|
-
searchKey
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
const resolvedSearchKey = resolveRuntimeConfigValue({
|
|
87
|
+
explicitValue: searchKey,
|
|
88
|
+
runtimeConfig,
|
|
89
|
+
runtimeConfigKeys: ['azureSearchKey'],
|
|
90
|
+
envKeys: ['VUE_APP_AZURE_SEARCH_KEY', 'GRIDSOME_AZURE_SEARCH_KEY'],
|
|
91
|
+
})
|
|
92
|
+
const resolvedApiVersion = resolveRuntimeConfigValue({
|
|
93
|
+
explicitValue: apiVersion,
|
|
94
|
+
runtimeConfig,
|
|
95
|
+
runtimeConfigKeys: ['azureSearchVersion'],
|
|
96
|
+
envKeys: [
|
|
97
|
+
'VUE_APP_AZURE_SEARCH_API_VERSION',
|
|
98
|
+
'GRIDSOME_AZURE_SEARCH_API_VERSION',
|
|
99
|
+
],
|
|
100
|
+
})
|
|
101
|
+
const resolvedSearchEndpoint = resolveRuntimeConfigValue({
|
|
102
|
+
explicitValue: searchEndpoint || endpoint,
|
|
103
|
+
runtimeConfig,
|
|
104
|
+
runtimeConfigKeys: ['azureSearchEndpoint'],
|
|
105
|
+
envKeys: ['VUE_APP_AZURE_SEARCH_ENDPOINT', 'GRIDSOME_AZURE_SEARCH_ENDPOINT'],
|
|
106
|
+
})
|
|
107
|
+
const resolvedRequestUrl =
|
|
108
|
+
typeof requestUrl === 'string' && requestUrl.trim()
|
|
109
|
+
? requestUrl.trim()
|
|
110
|
+
: !resolvedSearchEndpoint &&
|
|
111
|
+
hasSearchProxyRuntime(runtimeConfig)
|
|
112
|
+
? resolveSearchProxyUrl('events', runtimeConfig)
|
|
113
|
+
: ''
|
|
88
114
|
|
|
89
115
|
if (!keywords && requireKeywords)
|
|
90
116
|
return {
|
|
@@ -92,27 +118,49 @@ export default async function queryEvents({
|
|
|
92
118
|
facets: [],
|
|
93
119
|
}
|
|
94
120
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
if (
|
|
122
|
+
!resolvedRequestUrl &&
|
|
123
|
+
(!resolvedSearchEndpoint || !resolvedSearchKey || !resolvedApiVersion)
|
|
124
|
+
) {
|
|
125
|
+
throw new Error('Azure Search or proxy configuration is required for queryEvents.')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const payload = {
|
|
129
|
+
startDate,
|
|
130
|
+
endDate,
|
|
131
|
+
keywords,
|
|
132
|
+
calendarIds,
|
|
133
|
+
limit,
|
|
134
|
+
indexName,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const result = resolvedRequestUrl
|
|
138
|
+
? await axios({
|
|
139
|
+
method: 'post',
|
|
140
|
+
url: resolvedRequestUrl,
|
|
141
|
+
data: payload,
|
|
142
|
+
})
|
|
143
|
+
: await axios({
|
|
144
|
+
method: 'post',
|
|
145
|
+
headers: {
|
|
146
|
+
'api-key': resolvedSearchKey,
|
|
147
|
+
},
|
|
148
|
+
params: {
|
|
149
|
+
'api-version': resolvedApiVersion,
|
|
150
|
+
},
|
|
151
|
+
url: `${normalizeBaseUrl(resolvedSearchEndpoint)}/indexes/${indexName}/docs/search`,
|
|
152
|
+
data: Object.assign(
|
|
153
|
+
{},
|
|
154
|
+
searchPayload({
|
|
155
|
+
startDate,
|
|
156
|
+
endDate,
|
|
157
|
+
keywords,
|
|
158
|
+
calendarIds,
|
|
159
|
+
enableFuzzySearch: false,
|
|
160
|
+
limit,
|
|
161
|
+
})
|
|
162
|
+
),
|
|
113
163
|
})
|
|
114
|
-
),
|
|
115
|
-
})
|
|
116
164
|
|
|
117
165
|
const { data } = result
|
|
118
166
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import axios from 'axios'
|
|
2
2
|
|
|
3
|
+
import { hasSearchProxyRuntime, resolveSearchProxyUrl } from './searchProxyUrl'
|
|
4
|
+
import { normalizeBaseUrl, resolveRuntimeConfigValue } from './runtimeConfig'
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* Creates a Azure Search payload
|
|
5
8
|
*
|
|
@@ -64,8 +67,17 @@ export default async function queryFiles({
|
|
|
64
67
|
keywords = '',
|
|
65
68
|
categories = [],
|
|
66
69
|
owners = [],
|
|
70
|
+
organizations = [],
|
|
67
71
|
top = 5,
|
|
72
|
+
limit = 0,
|
|
68
73
|
requireKeywords = false,
|
|
74
|
+
indexName = 'files-index',
|
|
75
|
+
requestUrl = '',
|
|
76
|
+
runtimeConfig = null,
|
|
77
|
+
searchKey = '',
|
|
78
|
+
apiVersion = '',
|
|
79
|
+
searchEndpoint = '',
|
|
80
|
+
endpoint = '',
|
|
69
81
|
}) {
|
|
70
82
|
if (!keywords && requireKeywords)
|
|
71
83
|
return {
|
|
@@ -73,32 +85,81 @@ export default async function queryFiles({
|
|
|
73
85
|
facets: [],
|
|
74
86
|
}
|
|
75
87
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
searchPayload({
|
|
94
|
-
keywords,
|
|
95
|
-
categories,
|
|
96
|
-
owners,
|
|
97
|
-
enableFuzzySearch: true,
|
|
98
|
-
}),
|
|
99
|
-
{ top }
|
|
100
|
-
),
|
|
88
|
+
const resolvedLimit = Number.isFinite(Number(limit)) && Number(limit) > 0
|
|
89
|
+
? Number(limit)
|
|
90
|
+
: top
|
|
91
|
+
const resolvedSearchKey = resolveRuntimeConfigValue({
|
|
92
|
+
explicitValue: searchKey,
|
|
93
|
+
runtimeConfig,
|
|
94
|
+
runtimeConfigKeys: ['azureSearchKey'],
|
|
95
|
+
envKeys: ['VUE_APP_AZURE_SEARCH_KEY', 'GRIDSOME_AZURE_SEARCH_KEY'],
|
|
96
|
+
})
|
|
97
|
+
const resolvedApiVersion = resolveRuntimeConfigValue({
|
|
98
|
+
explicitValue: apiVersion,
|
|
99
|
+
runtimeConfig,
|
|
100
|
+
runtimeConfigKeys: ['azureSearchVersion'],
|
|
101
|
+
envKeys: [
|
|
102
|
+
'VUE_APP_AZURE_SEARCH_API_VERSION',
|
|
103
|
+
'GRIDSOME_AZURE_SEARCH_API_VERSION',
|
|
104
|
+
],
|
|
101
105
|
})
|
|
106
|
+
const resolvedSearchEndpoint = resolveRuntimeConfigValue({
|
|
107
|
+
explicitValue: searchEndpoint || endpoint,
|
|
108
|
+
runtimeConfig,
|
|
109
|
+
runtimeConfigKeys: ['azureSearchEndpoint'],
|
|
110
|
+
envKeys: ['VUE_APP_AZURE_SEARCH_ENDPOINT', 'GRIDSOME_AZURE_SEARCH_ENDPOINT'],
|
|
111
|
+
})
|
|
112
|
+
const resolvedRequestUrl =
|
|
113
|
+
typeof requestUrl === 'string' && requestUrl.trim()
|
|
114
|
+
? requestUrl.trim()
|
|
115
|
+
: !resolvedSearchEndpoint &&
|
|
116
|
+
hasSearchProxyRuntime(runtimeConfig)
|
|
117
|
+
? resolveSearchProxyUrl('files', runtimeConfig)
|
|
118
|
+
: ''
|
|
119
|
+
|
|
120
|
+
if (
|
|
121
|
+
!resolvedRequestUrl &&
|
|
122
|
+
(!resolvedSearchEndpoint || !resolvedSearchKey || !resolvedApiVersion)
|
|
123
|
+
) {
|
|
124
|
+
throw new Error('Azure Search or proxy configuration is required for queryFiles.')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const payload = {
|
|
128
|
+
keywords,
|
|
129
|
+
categories,
|
|
130
|
+
owners,
|
|
131
|
+
organizations,
|
|
132
|
+
limit: resolvedLimit,
|
|
133
|
+
indexName,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const { data } = resolvedRequestUrl
|
|
137
|
+
? await axios({
|
|
138
|
+
method: 'post',
|
|
139
|
+
url: resolvedRequestUrl,
|
|
140
|
+
data: payload,
|
|
141
|
+
})
|
|
142
|
+
: await axios({
|
|
143
|
+
method: 'post',
|
|
144
|
+
headers: {
|
|
145
|
+
'api-key': resolvedSearchKey,
|
|
146
|
+
},
|
|
147
|
+
params: {
|
|
148
|
+
'api-version': resolvedApiVersion,
|
|
149
|
+
},
|
|
150
|
+
url: `${normalizeBaseUrl(resolvedSearchEndpoint)}/indexes/${indexName}/docs/search`,
|
|
151
|
+
data: Object.assign(
|
|
152
|
+
{},
|
|
153
|
+
searchPayload({
|
|
154
|
+
keywords,
|
|
155
|
+
categories,
|
|
156
|
+
owners,
|
|
157
|
+
organizations,
|
|
158
|
+
enableFuzzySearch: true,
|
|
159
|
+
}),
|
|
160
|
+
{ top: resolvedLimit }
|
|
161
|
+
),
|
|
162
|
+
})
|
|
102
163
|
|
|
103
164
|
// Return results
|
|
104
165
|
return {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import axios from 'axios'
|
|
2
2
|
|
|
3
|
+
import { hasSearchProxyRuntime, resolveSearchProxyUrl } from './searchProxyUrl'
|
|
4
|
+
import { normalizeBaseUrl, resolveRuntimeConfigValue } from './runtimeConfig'
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* Creates a Azure Search payload
|
|
5
8
|
*
|
|
@@ -83,22 +86,58 @@ export default async function queryMessages({
|
|
|
83
86
|
organizationIds = [],
|
|
84
87
|
categories = [],
|
|
85
88
|
channels = [],
|
|
89
|
+
destinationIds = [],
|
|
90
|
+
destinationTitle = [],
|
|
86
91
|
tags = [],
|
|
92
|
+
isInLeadershipGroup = false,
|
|
87
93
|
limit = 5,
|
|
88
94
|
requireKeywords = false,
|
|
89
95
|
indexName = 'messages-index',
|
|
96
|
+
requestUrl = '',
|
|
97
|
+
runtimeConfig = null,
|
|
98
|
+
searchKey = '',
|
|
99
|
+
apiVersion = '',
|
|
100
|
+
searchEndpoint = '',
|
|
101
|
+
endpoint = '',
|
|
90
102
|
}) {
|
|
91
|
-
const
|
|
92
|
-
searchKey
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
103
|
+
const resolvedSearchKey = resolveRuntimeConfigValue({
|
|
104
|
+
explicitValue: searchKey,
|
|
105
|
+
runtimeConfig,
|
|
106
|
+
runtimeConfigKeys: ['azureSearchMessagesKey', 'azureSearchKey'],
|
|
107
|
+
envKeys: [
|
|
108
|
+
'VUE_APP_AZURE_SEARCH_MESSAGES_KEY',
|
|
109
|
+
'GRIDSOME_AZURE_MESSAGES_SEARCH_KEY',
|
|
110
|
+
'VUE_APP_AZURE_SEARCH_KEY',
|
|
111
|
+
'GRIDSOME_AZURE_SEARCH_KEY',
|
|
112
|
+
],
|
|
113
|
+
})
|
|
114
|
+
const resolvedApiVersion = resolveRuntimeConfigValue({
|
|
115
|
+
explicitValue: apiVersion,
|
|
116
|
+
runtimeConfig,
|
|
117
|
+
runtimeConfigKeys: ['azureSearchVersion'],
|
|
118
|
+
envKeys: [
|
|
119
|
+
'VUE_APP_AZURE_SEARCH_API_VERSION',
|
|
120
|
+
'GRIDSOME_AZURE_SEARCH_API_VERSION',
|
|
121
|
+
],
|
|
122
|
+
})
|
|
123
|
+
const resolvedSearchEndpoint = resolveRuntimeConfigValue({
|
|
124
|
+
explicitValue: searchEndpoint || endpoint,
|
|
125
|
+
runtimeConfig,
|
|
126
|
+
runtimeConfigKeys: ['azureSearchMessagesEndpoint', 'azureSearchEndpoint'],
|
|
127
|
+
envKeys: [
|
|
128
|
+
'VUE_APP_AZURE_SEARCH_MESSAGES_ENDPOINT',
|
|
129
|
+
'GRIDSOME_AZURE_MESSAGES_SEARCH_ENDPOINT',
|
|
130
|
+
'VUE_APP_AZURE_SEARCH_ENDPOINT',
|
|
131
|
+
'GRIDSOME_AZURE_SEARCH_ENDPOINT',
|
|
132
|
+
],
|
|
133
|
+
})
|
|
134
|
+
const resolvedRequestUrl =
|
|
135
|
+
typeof requestUrl === 'string' && requestUrl.trim()
|
|
136
|
+
? requestUrl.trim()
|
|
137
|
+
: !resolvedSearchEndpoint &&
|
|
138
|
+
hasSearchProxyRuntime(runtimeConfig)
|
|
139
|
+
? resolveSearchProxyUrl('messages', runtimeConfig)
|
|
140
|
+
: ''
|
|
102
141
|
|
|
103
142
|
if (!keywords && requireKeywords)
|
|
104
143
|
return {
|
|
@@ -106,29 +145,57 @@ export default async function queryMessages({
|
|
|
106
145
|
facets: [],
|
|
107
146
|
}
|
|
108
147
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
148
|
+
if (
|
|
149
|
+
!resolvedRequestUrl &&
|
|
150
|
+
(!resolvedSearchEndpoint || !resolvedSearchKey || !resolvedApiVersion)
|
|
151
|
+
) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
'Azure Search or proxy configuration is required for queryMessages.'
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const payload = {
|
|
158
|
+
keywords,
|
|
159
|
+
organizationIds,
|
|
160
|
+
destinationIds,
|
|
161
|
+
destinationTitle,
|
|
162
|
+
categories,
|
|
163
|
+
channels,
|
|
164
|
+
tags,
|
|
165
|
+
isInLeadershipGroup,
|
|
166
|
+
limit,
|
|
167
|
+
indexName,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const { data } = resolvedRequestUrl
|
|
171
|
+
? await axios({
|
|
172
|
+
method: 'post',
|
|
173
|
+
url: resolvedRequestUrl,
|
|
174
|
+
data: payload,
|
|
175
|
+
})
|
|
176
|
+
: await axios({
|
|
177
|
+
method: 'post',
|
|
178
|
+
headers: {
|
|
179
|
+
'api-key': resolvedSearchKey,
|
|
180
|
+
},
|
|
181
|
+
params: {
|
|
182
|
+
'api-version': resolvedApiVersion,
|
|
183
|
+
},
|
|
184
|
+
url: `${normalizeBaseUrl(resolvedSearchEndpoint)}/indexes/${indexName}/docs/search`,
|
|
185
|
+
data: Object.assign(
|
|
186
|
+
{},
|
|
187
|
+
searchPayload({
|
|
188
|
+
keywords,
|
|
189
|
+
organizationIds,
|
|
190
|
+
categories,
|
|
191
|
+
channels,
|
|
192
|
+
tags,
|
|
193
|
+
enableFuzzySearch: true,
|
|
194
|
+
limit,
|
|
195
|
+
}),
|
|
196
|
+
{ top: limit }
|
|
197
|
+
),
|
|
198
|
+
})
|
|
132
199
|
|
|
133
200
|
// Return results
|
|
134
201
|
return {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import axios from 'axios'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import { hasSearchProxyRuntime, resolveSearchProxyUrl } from './searchProxyUrl'
|
|
4
|
+
import { resolveRuntimeConfigValue } from './runtimeConfig'
|
|
5
|
+
|
|
6
|
+
export const NO_PAGE_RESULTS_FALLBACKS = [
|
|
4
7
|
{
|
|
5
8
|
id: 'calendar',
|
|
6
9
|
slug: 'calendar',
|
|
@@ -106,6 +109,14 @@ export default async function queryPages({
|
|
|
106
109
|
keywords = '',
|
|
107
110
|
top = 25,
|
|
108
111
|
requireKeywords = false,
|
|
112
|
+
organizations = [],
|
|
113
|
+
requestUrl = '',
|
|
114
|
+
runtimeConfig = null,
|
|
115
|
+
searchEndpoint = '',
|
|
116
|
+
endpoint = '',
|
|
117
|
+
searchKey = '',
|
|
118
|
+
apiVersion = '',
|
|
119
|
+
searchVersion = '',
|
|
109
120
|
}) {
|
|
110
121
|
if (!keywords && requireKeywords)
|
|
111
122
|
return {
|
|
@@ -113,26 +124,82 @@ export default async function queryPages({
|
|
|
113
124
|
facets: [],
|
|
114
125
|
}
|
|
115
126
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
'
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
127
|
+
const resolvedSearchKey = resolveRuntimeConfigValue({
|
|
128
|
+
explicitValue: searchKey,
|
|
129
|
+
runtimeConfig,
|
|
130
|
+
runtimeConfigKeys: ['azureSearchKey'],
|
|
131
|
+
envKeys: [
|
|
132
|
+
'AZURE_SEARCH_KEY',
|
|
133
|
+
'VUE_APP_AZURE_SEARCH_KEY',
|
|
134
|
+
'GRIDSOME_AZURE_SEARCH_KEY',
|
|
135
|
+
],
|
|
136
|
+
})
|
|
137
|
+
const resolvedApiVersion = resolveRuntimeConfigValue({
|
|
138
|
+
explicitValue: apiVersion || searchVersion,
|
|
139
|
+
runtimeConfig,
|
|
140
|
+
runtimeConfigKeys: ['azureSearchVersion'],
|
|
141
|
+
envKeys: [
|
|
142
|
+
'AZURE_SEARCH_VERSION',
|
|
143
|
+
'VUE_APP_AZURE_SEARCH_API_VERSION',
|
|
144
|
+
'GRIDSOME_AZURE_SEARCH_API_VERSION',
|
|
145
|
+
],
|
|
146
|
+
})
|
|
147
|
+
const resolvedSearchEndpoint = resolveRuntimeConfigValue({
|
|
148
|
+
explicitValue: searchEndpoint || endpoint,
|
|
149
|
+
runtimeConfig,
|
|
150
|
+
runtimeConfigKeys: ['azureSearchEndpoint'],
|
|
151
|
+
envKeys: [
|
|
152
|
+
'AZURE_SEARCH_ENDPOINT',
|
|
153
|
+
'VUE_APP_AZURE_SEARCH_ENDPOINT',
|
|
154
|
+
'GRIDSOME_AZURE_SEARCH_ENDPOINT',
|
|
155
|
+
],
|
|
135
156
|
})
|
|
157
|
+
const resolvedRequestUrl =
|
|
158
|
+
typeof requestUrl === 'string' && requestUrl.trim()
|
|
159
|
+
? requestUrl.trim()
|
|
160
|
+
: !resolvedSearchEndpoint &&
|
|
161
|
+
hasSearchProxyRuntime(runtimeConfig)
|
|
162
|
+
? resolveSearchProxyUrl('pages', runtimeConfig)
|
|
163
|
+
: ''
|
|
164
|
+
|
|
165
|
+
if (
|
|
166
|
+
!resolvedRequestUrl &&
|
|
167
|
+
(!resolvedSearchEndpoint || !resolvedSearchKey || !resolvedApiVersion)
|
|
168
|
+
) {
|
|
169
|
+
throw new Error('Azure Search or proxy configuration is required for queryPages.')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const { data } = resolvedRequestUrl
|
|
173
|
+
? await axios({
|
|
174
|
+
method: 'post',
|
|
175
|
+
url: resolvedRequestUrl,
|
|
176
|
+
data: {
|
|
177
|
+
keywords,
|
|
178
|
+
organizations,
|
|
179
|
+
top,
|
|
180
|
+
},
|
|
181
|
+
})
|
|
182
|
+
: await axios({
|
|
183
|
+
method: 'post',
|
|
184
|
+
headers: {
|
|
185
|
+
'api-key': resolvedSearchKey,
|
|
186
|
+
},
|
|
187
|
+
params: {
|
|
188
|
+
'api-version': resolvedApiVersion,
|
|
189
|
+
},
|
|
190
|
+
url: `${resolvedSearchEndpoint}`,
|
|
191
|
+
data: Object.assign(
|
|
192
|
+
{},
|
|
193
|
+
searchPayload({
|
|
194
|
+
keywords,
|
|
195
|
+
enableFuzzySearch: true,
|
|
196
|
+
organizations,
|
|
197
|
+
}),
|
|
198
|
+
{
|
|
199
|
+
top,
|
|
200
|
+
}
|
|
201
|
+
),
|
|
202
|
+
})
|
|
136
203
|
|
|
137
204
|
const pages = Array.isArray(data.value) ? data.value : []
|
|
138
205
|
const resolvedPages = pages.length
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
function isPlainObject(value) {
|
|
2
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function normalizeConfigValue(value) {
|
|
6
|
+
return typeof value === 'string' ? value.trim() : value
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function hasConfigValue(value) {
|
|
10
|
+
if (typeof value === 'string') {
|
|
11
|
+
return value.trim().length > 0
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return value !== null && value !== undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function resolvePublicRuntimeConfig(runtimeConfig = {}) {
|
|
18
|
+
if (!isPlainObject(runtimeConfig)) {
|
|
19
|
+
return {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (isPlainObject(runtimeConfig.public)) {
|
|
23
|
+
return runtimeConfig.public
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return runtimeConfig
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resolveWindowPublicRuntimeConfig() {
|
|
30
|
+
if (typeof window === 'undefined' || !window.__NUXT__) {
|
|
31
|
+
return {}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return resolvePublicRuntimeConfig(window.__NUXT__.config)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function resolveRuntimeConfigValue({
|
|
38
|
+
explicitValue = null,
|
|
39
|
+
runtimeConfig = {},
|
|
40
|
+
runtimeConfigKeys = [],
|
|
41
|
+
envKeys = [],
|
|
42
|
+
} = {}) {
|
|
43
|
+
const normalizedExplicitValue = normalizeConfigValue(explicitValue)
|
|
44
|
+
if (hasConfigValue(normalizedExplicitValue)) {
|
|
45
|
+
return normalizedExplicitValue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const sources = [
|
|
49
|
+
resolvePublicRuntimeConfig(runtimeConfig),
|
|
50
|
+
resolveWindowPublicRuntimeConfig(),
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
for (const source of sources) {
|
|
54
|
+
for (const key of runtimeConfigKeys) {
|
|
55
|
+
const candidate = normalizeConfigValue(source[key])
|
|
56
|
+
if (hasConfigValue(candidate)) {
|
|
57
|
+
return candidate
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof process !== 'undefined' && process && process.env) {
|
|
63
|
+
for (const key of envKeys) {
|
|
64
|
+
const candidate = normalizeConfigValue(process.env[key])
|
|
65
|
+
if (hasConfigValue(candidate)) {
|
|
66
|
+
return candidate
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function normalizeBaseUrl(value) {
|
|
75
|
+
if (typeof value !== 'string') {
|
|
76
|
+
return ''
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return value.trim().replace(/\/+$/, '')
|
|
80
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { normalizeBaseUrl, resolveRuntimeConfigValue } from './runtimeConfig'
|
|
2
|
+
|
|
3
|
+
const SEARCH_PROXY_PATHS = Object.freeze({
|
|
4
|
+
messages: '/api/search/messages',
|
|
5
|
+
pages: '/api/search/pages',
|
|
6
|
+
files: '/api/search/files',
|
|
7
|
+
events: '/api/search/events',
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
export function resolveSearchProxyPath(kind) {
|
|
11
|
+
return SEARCH_PROXY_PATHS[kind] || ''
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function hasSearchProxyRuntime(runtimeConfig = {}) {
|
|
15
|
+
const functionsEndpoint = resolveRuntimeConfigValue({
|
|
16
|
+
runtimeConfig,
|
|
17
|
+
runtimeConfigKeys: ['functionsEndpoint'],
|
|
18
|
+
envKeys: [],
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
if (functionsEndpoint) {
|
|
22
|
+
return true
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return typeof window !== 'undefined' && !!window.__NUXT__
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function resolveSearchProxyUrl(kind, runtimeConfig = {}) {
|
|
29
|
+
const path = resolveSearchProxyPath(kind)
|
|
30
|
+
|
|
31
|
+
if (!path) {
|
|
32
|
+
throw new Error(`Unknown search proxy kind: ${String(kind)}`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const functionsEndpoint = resolveRuntimeConfigValue({
|
|
36
|
+
runtimeConfig,
|
|
37
|
+
runtimeConfigKeys: ['functionsEndpoint'],
|
|
38
|
+
envKeys: ['FUNCTIONS_ENDPOINT', 'VUE_APP_FUNCTIONS_ENDPOINT'],
|
|
39
|
+
})
|
|
40
|
+
const baseUrl = normalizeBaseUrl(functionsEndpoint)
|
|
41
|
+
|
|
42
|
+
return baseUrl ? `${baseUrl}${path}` : path
|
|
43
|
+
}
|
|
@@ -1,14 +1,38 @@
|
|
|
1
1
|
import axios from 'axios'
|
|
2
2
|
|
|
3
|
+
import { resolveRuntimeConfigValue } from './runtimeConfig'
|
|
4
|
+
|
|
3
5
|
function issueIsValid(issue) {
|
|
4
6
|
return !!issue.firstName && !!issue.lastName && !!issue.email && !!issue.issue
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
export default async function sendAccessibilityIssue(
|
|
9
|
+
export default async function sendAccessibilityIssue(
|
|
10
|
+
issue,
|
|
11
|
+
endpoint,
|
|
12
|
+
apiKey,
|
|
13
|
+
authCode,
|
|
14
|
+
options = {}
|
|
15
|
+
) {
|
|
8
16
|
if (!issueIsValid(issue)) {
|
|
9
17
|
throw new Error('The issue object is incomplete or invalid.')
|
|
10
18
|
}
|
|
11
19
|
|
|
20
|
+
const resolvedOptions =
|
|
21
|
+
options && typeof options === 'object' && !Array.isArray(options)
|
|
22
|
+
? options
|
|
23
|
+
: {
|
|
24
|
+
categoryId: options,
|
|
25
|
+
}
|
|
26
|
+
const categoryId = resolveRuntimeConfigValue({
|
|
27
|
+
explicitValue: resolvedOptions.categoryId,
|
|
28
|
+
runtimeConfig: resolvedOptions.runtimeConfig,
|
|
29
|
+
runtimeConfigKeys: ['happyFoxCategoryId'],
|
|
30
|
+
envKeys: ['HAPPYFOX_CATEGORY_ID'],
|
|
31
|
+
})
|
|
32
|
+
if (!categoryId) {
|
|
33
|
+
throw new Error('happyFoxCategoryId is required to send an accessibility issue.')
|
|
34
|
+
}
|
|
35
|
+
|
|
12
36
|
const payload = {
|
|
13
37
|
name: `${issue.firstName} ${issue.lastName}`,
|
|
14
38
|
email: issue.email,
|
|
@@ -20,7 +44,7 @@ export default async function sendAccessibilityIssue(issue, endpoint, apiKey, au
|
|
|
20
44
|
<p><strong>Mailing Address:</strong> ${issue.address || 'Not provided'}</p>
|
|
21
45
|
<p><strong>Organization:</strong> ${issue.organization || 'Not provided'}</p>
|
|
22
46
|
`,
|
|
23
|
-
category: Number(
|
|
47
|
+
category: Number(categoryId),
|
|
24
48
|
}
|
|
25
49
|
|
|
26
50
|
try {
|