Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: search request amount made on page arrival #486

Merged
merged 5 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions cypress/e2e/test-api-key-query-param.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ describe(`Test API key required with query params`, () => {
})

it('Should display the movies', () => {
cy.wait(WAITING_TIME)
cy.get('ul')
.children()
.should(($p) => {
Expand Down
2 changes: 2 additions & 0 deletions cypress/e2e/test-interface.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ describe(`Test interface`, () => {

it('Shouldn’t contain a "Show more" button if a document has less than 6 fields', () => {
cy.get('button[aria-haspopup=menu]').click()
cy.wait(WAITING_TIME)
cy.get('button[role=menuitem]').contains('pokemon').click()
cy.get('ul')
.children()
Expand All @@ -70,6 +71,7 @@ describe(`Test interface`, () => {

it('Should display more fields if the user clicks on the "Show more" button', () => {
cy.get('button[aria-haspopup=menu]').click()
cy.wait(WAITING_TIME)
cy.get('button[role=menuitem]').contains('movies').click()
cy.get('ul')
.children()
Expand Down
5 changes: 5 additions & 0 deletions cypress/e2e/test-select-indexes.cy.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const WAITING_TIME = Cypress.env('waitingTime')

// TODO: refacto tests to get rid of the WAITING_TIME

describe(`Test indexes`, () => {
before(() => {
cy.deleteAllIndexes()
Expand Down Expand Up @@ -39,6 +41,7 @@ describe(`Test indexes`, () => {

it('Should list all the indexes inside the select', () => {
cy.get('button[aria-haspopup=menu]').click()
cy.wait(WAITING_TIME)
cy.get('div[role=menu]')
.children()
.should(($p) => {
Expand All @@ -55,6 +58,7 @@ describe(`Test indexes`, () => {

it('Should display an indexes documents', () => {
cy.get('button[aria-haspopup=menu]').click()
cy.wait(WAITING_TIME)
cy.get('button[role=menuitem]').contains('movies').click()
cy.get('ul')
.children()
Expand All @@ -65,6 +69,7 @@ describe(`Test indexes`, () => {

it('Should display the documents of an other index on click on it', () => {
cy.get('button[aria-haspopup=menu]').click()
cy.wait(WAITING_TIME)
cy.get('button[role=menuitem]').contains('pokemon').click()
cy.get('ul').children().should('have.length', 3)
cy.get('ul')
Expand Down
278 changes: 76 additions & 202 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
/* eslint-disable no-unused-vars */
mdubus marked this conversation as resolved.
Show resolved Hide resolved
/* eslint-disable react/jsx-no-constructed-context-values */
/* eslint-disable no-console */
import React from 'react'
import React, { useState, useEffect, useCallback } from 'react'
import styled from 'styled-components'
import { InstantSearch } from 'react-instantsearch-dom'
import { instantMeiliSearch as instantMeilisearch } from '@meilisearch/instant-meilisearch'
import { useDialogState } from 'reakit/Dialog'
import { MeiliSearch as Meilisearch } from 'meilisearch'

import ApiKeyContext from 'context/ApiKeyContext'
import { useMeilisearchClientContext } from 'context/MeilisearchClientContext'
import useLocalStorage from 'hooks/useLocalStorage'
import ApiKeyModalContent from 'components/ApiKeyModalContent'
import Box from 'components/Box'
import EmptyView from 'components/EmptyView'
import Header from 'components/Header/index'
import Body from 'components/Body'
import CloudBanner from 'components/CloudBanner'
import Modal from 'components/Modal'
import OnBoarding from 'components/OnBoarding'
import Results from 'components/Results'
import ApiKeyContext from 'context/ApiKeyContext'
import Typography from 'components/Typography'
import { MeiliSearch as Meilisearch } from 'meilisearch'

import NoMeilisearchRunning from 'components/NoMeilisearchRunning'
import ApiKeyAwarenessBanner from 'components/ApiKeyAwarenessBanner'
import getIndexesListWithStats from 'utils/getIndexesListWithStats'
import shouldDisplayCloudBanner from 'utils/shouldDisplayCloudBanner'
import shouldDisplayApiKeyModal from 'utils/shouldDisplayApiKeyModal'
import hasAnApiKeySet from 'utils/hasAnApiKeySet'
import clientAgents from './version/client-agents'

export const baseUrl =
Expand All @@ -33,116 +33,31 @@ const Wrapper = styled.div`
min-height: 100vh;
`

const Body = styled.div`
display: flex;
flex: 1;
width: 100%;
min-height: calc(100vh - 120px);
`

const Content = ({ currentIndex }) => {
if (!currentIndex) return <OnBoarding />
if (currentIndex?.stats?.numberOfDocuments > 0) return <Results />
return (
<EmptyView buttonLink="https://docs.meilisearch.com/reference/api/documents.html">
<Typography
variant="typo8"
style={{ textAlign: 'center' }}
mb={32}
color="gray.0"
>
There’s no document in the selected index
</Typography>
</EmptyView>
)
}

const NoMeilisearchRunning = () => (
<EmptyView buttonLink="https://docs.meilisearch.com/learn/getting_started/quick_start.html">
<Typography
variant="typo8"
style={{ textAlign: 'center' }}
mb={3}
color="gray.0"
>
It seems like Meilisearch isn’t running, did you forget to start it?
</Typography>
<Typography
variant="typo8"
style={{ textAlign: 'center' }}
mb={32}
color="gray.2"
>
(Don’t forget to set an API Key if you want one)
</Typography>
<Typography
variant="typo8"
style={{ textAlign: 'center', fontSize: 40 }}
mb={56}
>
<span role="img" aria-label="face-with-monocle">
🧐
</span>
</Typography>
</EmptyView>
)

const App = () => {
const [apiKey, setApiKey] = useLocalStorage('apiKey')
// eslint-disable-next-line no-unused-vars
const [indexes, setIndexes] = React.useState()
const [isMeilisearchRunning, setIsMeilisearchRunning] = React.useState(true)
const [requireApiKeyToWork, setRequireApiKeyToWork] = React.useState(false)
const [indexes, setIndexes] = useState()
const [isMeilisearchRunning, setIsMeilisearchRunning] = useState(false)
const [requireApiKeyToWork, setRequireApiKeyToWork] = useState(false)
const [currentIndex, setCurrentIndex] = useLocalStorage('currentIndex')
const [showCloudBanner, setShowCloudBanner] = React.useState(true)

const [ISClient, setISClient] = React.useState(
instantMeilisearch(baseUrl, apiKey, {
primaryKey: 'id',
clientAgents,
}).searchClient
)
const [MSClient, setMSClient] = React.useState(
new Meilisearch({
host: baseUrl,
apiKey,
clientAgents,
})
)
const [isApiKeyBannerVisible, setIsApiKeyBannerVisible] =
React.useState(false)

const handleBannerClose = () => {
setIsApiKeyBannerVisible(false)
}

const [showCloudBanner, setShowCloudBanner] = useState(false)
const [isApiKeyBannerVisible, setIsApiKeyBannerVisible] = useState(false)
const dialog = useDialogState({ animated: true, visible: false })

const getIndexesList = async () => {
try {
const res = await MSClient.getStats()
const array = Object.entries(res.indexes)
const options = array
.reduce((prev, curr) => {
const currentOption = { uid: curr[0], stats: curr[1] }
return [...prev, currentOption]
}, [])
.sort((a, b) => a.uid.localeCompare(b.uid))
const {
meilisearchJsClient,
setMeilisearchJsClient,
setInstantMeilisearchClient,
} = useMeilisearchClientContext()

setIndexes(options)
if (options.length) {
if (currentIndex) {
setCurrentIndex(
options.find((option) => option.uid === currentIndex.uid)
)
} else {
setCurrentIndex(options[0])
}
setISClient(
instantMeilisearch(baseUrl, apiKey, {
primaryKey: 'id',
clientAgents,
}).searchClient
const getIndexesList = useCallback(async () => {
try {
const indexesList = await getIndexesListWithStats(meilisearchJsClient)
setIndexes(indexesList)
if (indexesList && indexesList?.length > 0) {
setCurrentIndex(
currentIndex
? indexesList.find((option) => option.uid === currentIndex.uid)
: indexesList[0]
)
} else {
setCurrentIndex(null)
Expand All @@ -151,115 +66,74 @@ const App = () => {
setCurrentIndex(null)
console.log(error)
}
}
}, [meilisearchJsClient, currentIndex])

React.useEffect(() => {
// Check if the API key is present on the url then put it in the local storage
// Check if the API key is present on the url then put it in the local storage
const getApiKeyFromUrl = useCallback(() => {
const urlParams = new URLSearchParams(window.location.search)
const apiKeyParam = urlParams.get('api_key')
const cloudBannerQueryParam = urlParams.get('cloud_banner')

if (cloudBannerQueryParam === 'false') {
setShowCloudBanner(false)
}
if (apiKeyParam) {
setApiKey(apiKeyParam)
setIsApiKeyBannerVisible(true)
}
}, [])

// Check if an API key is required / a masterKey was set
const fetchWithoutApiKey = async () => {
try {
const tempClient = new Meilisearch({ host: baseUrl, clientAgents })
await tempClient.getIndexes()
} catch (err) {
console.log(err)
if (err.code === 'missing_authorization_header') {
setRequireApiKeyToWork(true)
} else {
setIsMeilisearchRunning(await MSClient.isHealthy())
}
}
useEffect(() => {
const shouldCloudBannerBeDisplayed = shouldDisplayCloudBanner()
if (shouldCloudBannerBeDisplayed) {
setShowCloudBanner(shouldCloudBannerBeDisplayed)
}

fetchWithoutApiKey()
getIndexesList()
getApiKeyFromUrl()
}, [])

React.useEffect(() => {
if (apiKey) {
setISClient(
instantMeilisearch(baseUrl, apiKey, {
primaryKey: 'id',
clientAgents,
}).searchClient
)

setMSClient(
new Meilisearch({
host: baseUrl,
apiKey,
clientAgents,
})
)
}
useEffect(() => {
setInstantMeilisearchClient(
instantMeilisearch(baseUrl, apiKey, {
primaryKey: 'id',
clientAgents,
}).searchClient
)

setMeilisearchJsClient(
new Meilisearch({
host: baseUrl,
apiKey,
clientAgents,
})
)
}, [apiKey])

// Check if a modal asking for API Key should be displayed
React.useEffect(() => {
const shouldDisplayModal = async () => {
try {
await MSClient.getIndexes()
} catch (err) {
console.log(err)
dialog.show()
}
useEffect(async () => {
const isInstanceRunning = await meilisearchJsClient.isHealthy()
setIsMeilisearchRunning(isInstanceRunning)
if (isInstanceRunning) {
setRequireApiKeyToWork(await hasAnApiKeySet())
dialog.setVisible(await shouldDisplayApiKeyModal(meilisearchJsClient))
getIndexesList()
}
if (requireApiKeyToWork) shouldDisplayModal()
}, [requireApiKeyToWork])

// Get the list of indexes
React.useEffect(() => {
getIndexesList()
}, [MSClient, currentIndex?.uid])
}, [meilisearchJsClient])

return (
<ApiKeyContext.Provider value={{ apiKey, setApiKey }}>
<Wrapper>
<InstantSearch
indexName={currentIndex ? currentIndex.uid : ''}
searchClient={ISClient}
>
{isApiKeyBannerVisible && (
<ApiKeyAwarenessBanner onClose={handleBannerClose} />
)}
{showCloudBanner && <CloudBanner />}
<Header
indexes={indexes}
{isApiKeyBannerVisible && (
<ApiKeyAwarenessBanner
onClose={() => setIsApiKeyBannerVisible(false)}
/>
)}
{showCloudBanner && <CloudBanner />}
{isMeilisearchRunning ? (
<Body
currentIndex={currentIndex}
indexes={indexes}
setCurrentIndex={setCurrentIndex}
requireApiKeyToWork={requireApiKeyToWork}
client={MSClient}
refreshIndexes={getIndexesList}
isBannerVisible={isApiKeyBannerVisible}
getIndexesList={getIndexesList}
isApiKeyBannerVisible={isApiKeyBannerVisible}
/>
<Body>
{/* <Sidebar /> */}
<Box
width={928}
m="0 auto"
py={4}
display="flex"
flexDirection="column"
>
{isMeilisearchRunning ? (
<Content currentIndex={currentIndex} />
) : (
<NoMeilisearchRunning />
)}
</Box>
</Body>
</InstantSearch>
) : (
<NoMeilisearchRunning />
atoulmet marked this conversation as resolved.
Show resolved Hide resolved
)}
<Modal
title={`Enter your admin API key${
requireApiKeyToWork ? '' : ' (optional)'
Expand Down
Loading
Loading