feat: add search adapter for entity searches
- Implement SearchAdapter extending BaseAdapter - Support searching weapons, characters, and summons - Add filtering by element, rarity, proficiency, etc. - Include pagination support - Add comprehensive test coverage
This commit is contained in:
parent
51c30edc50
commit
ece44a54a3
3 changed files with 745 additions and 0 deletions
427
src/lib/api/adapters/__tests__/search.adapter.test.ts
Normal file
427
src/lib/api/adapters/__tests__/search.adapter.test.ts
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
/**
|
||||
* Tests for SearchAdapter
|
||||
*
|
||||
* These tests verify search functionality including filtering,
|
||||
* pagination, and proper request/response handling.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||
import { SearchAdapter } from '../search.adapter'
|
||||
import type { SearchParams, SearchResponse } from '../search.adapter'
|
||||
|
||||
describe('SearchAdapter', () => {
|
||||
let adapter: SearchAdapter
|
||||
let originalFetch: typeof global.fetch
|
||||
|
||||
beforeEach(() => {
|
||||
originalFetch = global.fetch
|
||||
adapter = new SearchAdapter({
|
||||
baseURL: 'https://api.example.com'
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
global.fetch = originalFetch
|
||||
adapter.cancelAll()
|
||||
})
|
||||
|
||||
describe('searchAll', () => {
|
||||
it('should search across all entity types', async () => {
|
||||
const mockResponse: SearchResponse = {
|
||||
results: [
|
||||
{
|
||||
id: '1',
|
||||
granblueId: 'weapon_1',
|
||||
name: { en: 'Bahamut Sword', ja: 'バハムートソード' },
|
||||
element: 1,
|
||||
rarity: 5,
|
||||
searchableType: 'Weapon',
|
||||
imageUrl: 'https://example.com/weapon1.jpg'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
granblueId: 'character_1',
|
||||
name: { en: 'Bahamut', ja: 'バハムート' },
|
||||
element: 6,
|
||||
rarity: 5,
|
||||
searchableType: 'Character',
|
||||
imageUrl: 'https://example.com/character1.jpg'
|
||||
}
|
||||
],
|
||||
total: 2,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
meta: {
|
||||
count: 2,
|
||||
page: 1,
|
||||
perPage: 20,
|
||||
totalPages: 1
|
||||
}
|
||||
}
|
||||
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
results: [
|
||||
{
|
||||
id: '1',
|
||||
granblue_id: 'weapon_1',
|
||||
name: { en: 'Bahamut Sword', ja: 'バハムートソード' },
|
||||
element: 1,
|
||||
rarity: 5,
|
||||
searchable_type: 'Weapon',
|
||||
image_url: 'https://example.com/weapon1.jpg'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
granblue_id: 'character_1',
|
||||
name: { en: 'Bahamut', ja: 'バハムート' },
|
||||
element: 6,
|
||||
rarity: 5,
|
||||
searchable_type: 'Character',
|
||||
image_url: 'https://example.com/character1.jpg'
|
||||
}
|
||||
],
|
||||
total: 2,
|
||||
page: 1,
|
||||
total_pages: 1,
|
||||
meta: {
|
||||
count: 2,
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
total_pages: 1
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const params: SearchParams = {
|
||||
query: 'bahamut',
|
||||
locale: 'en',
|
||||
page: 1
|
||||
}
|
||||
|
||||
const result = await adapter.searchAll(params)
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'https://api.example.com/search/all',
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
credentials: 'omit',
|
||||
body: JSON.stringify({
|
||||
locale: 'en',
|
||||
page: 1,
|
||||
query: 'bahamut'
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
expect(result).toEqual(mockResponse)
|
||||
})
|
||||
|
||||
it('should include filters when provided', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ results: [] })
|
||||
})
|
||||
|
||||
const params: SearchParams = {
|
||||
query: 'sword',
|
||||
filters: {
|
||||
element: [1, 2],
|
||||
rarity: [5],
|
||||
extra: true
|
||||
}
|
||||
}
|
||||
|
||||
await adapter.searchAll(params)
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
body: JSON.stringify({
|
||||
locale: 'en',
|
||||
page: 1,
|
||||
query: 'sword',
|
||||
filters: {
|
||||
element: [1, 2],
|
||||
rarity: [5],
|
||||
extra: true
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle exclude parameter', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ results: [] })
|
||||
})
|
||||
|
||||
const params: SearchParams = {
|
||||
query: 'test',
|
||||
exclude: ['1', '2', '3']
|
||||
}
|
||||
|
||||
await adapter.searchAll(params)
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
body: JSON.stringify({
|
||||
locale: 'en',
|
||||
page: 1,
|
||||
query: 'test',
|
||||
exclude: ['1', '2', '3']
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('should cache results when query is provided', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ results: [{ id: '1' }] })
|
||||
})
|
||||
|
||||
// First call
|
||||
await adapter.searchAll({ query: 'test' })
|
||||
expect(global.fetch).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Second call should use cache
|
||||
await adapter.searchAll({ query: 'test' })
|
||||
expect(global.fetch).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should not cache results when query is empty', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ results: [] })
|
||||
})
|
||||
|
||||
// First call
|
||||
await adapter.searchAll({})
|
||||
expect(global.fetch).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Second call should not use cache
|
||||
await adapter.searchAll({})
|
||||
expect(global.fetch).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('searchWeapons', () => {
|
||||
it('should search for weapons with appropriate filters', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ results: [] })
|
||||
})
|
||||
|
||||
const params: SearchParams = {
|
||||
query: 'sword',
|
||||
filters: {
|
||||
element: [1],
|
||||
proficiency1: [2],
|
||||
extra: false
|
||||
},
|
||||
per: 50
|
||||
}
|
||||
|
||||
await adapter.searchWeapons(params)
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'https://api.example.com/search/weapons',
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
credentials: 'omit',
|
||||
body: JSON.stringify({
|
||||
locale: 'en',
|
||||
page: 1,
|
||||
per: 50,
|
||||
query: 'sword',
|
||||
filters: {
|
||||
element: [1],
|
||||
proficiency1: [2],
|
||||
extra: false
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('should not include character-specific filters', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ results: [] })
|
||||
})
|
||||
|
||||
const params: SearchParams = {
|
||||
query: 'test',
|
||||
filters: {
|
||||
proficiency2: [1], // Character-only filter
|
||||
subaura: true // Summon-only filter
|
||||
}
|
||||
}
|
||||
|
||||
await adapter.searchWeapons(params)
|
||||
|
||||
const calledBody = JSON.parse((global.fetch as any).mock.calls[0][1].body)
|
||||
expect(calledBody.filters).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('searchCharacters', () => {
|
||||
it('should search for characters with appropriate filters', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ results: [] })
|
||||
})
|
||||
|
||||
const params: SearchParams = {
|
||||
query: 'katalina',
|
||||
filters: {
|
||||
element: [2],
|
||||
proficiency1: [1],
|
||||
proficiency2: [3]
|
||||
}
|
||||
}
|
||||
|
||||
await adapter.searchCharacters(params)
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'https://api.example.com/search/characters',
|
||||
expect.objectContaining({
|
||||
body: JSON.stringify({
|
||||
locale: 'en',
|
||||
page: 1,
|
||||
query: 'katalina',
|
||||
filters: {
|
||||
element: [2],
|
||||
proficiency1: [1],
|
||||
proficiency2: [3]
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('searchSummons', () => {
|
||||
it('should search for summons with appropriate filters', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ results: [] })
|
||||
})
|
||||
|
||||
const params: SearchParams = {
|
||||
query: 'bahamut',
|
||||
filters: {
|
||||
element: [6],
|
||||
rarity: [5],
|
||||
subaura: true
|
||||
}
|
||||
}
|
||||
|
||||
await adapter.searchSummons(params)
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'https://api.example.com/search/summons',
|
||||
expect.objectContaining({
|
||||
body: JSON.stringify({
|
||||
locale: 'en',
|
||||
page: 1,
|
||||
query: 'bahamut',
|
||||
filters: {
|
||||
element: [6],
|
||||
rarity: [5],
|
||||
subaura: true
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('clearSearchCache', () => {
|
||||
it('should clear cached search results', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ results: [{ id: '1' }] })
|
||||
})
|
||||
|
||||
// Cache a search
|
||||
await adapter.searchAll({ query: 'test' })
|
||||
expect(global.fetch).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Clear cache
|
||||
adapter.clearSearchCache()
|
||||
|
||||
// Should fetch again
|
||||
await adapter.searchAll({ query: 'test' })
|
||||
expect(global.fetch).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('error handling', () => {
|
||||
it('should handle server errors', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: false,
|
||||
status: 500,
|
||||
statusText: 'Internal Server Error',
|
||||
json: async () => ({ error: 'Server error' })
|
||||
})
|
||||
|
||||
await expect(adapter.searchAll({ query: 'test' })).rejects.toMatchObject({
|
||||
code: 'SERVER_ERROR',
|
||||
status: 500
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle network errors', async () => {
|
||||
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'))
|
||||
|
||||
await expect(adapter.searchAll({ query: 'test' })).rejects.toMatchObject({
|
||||
code: 'UNKNOWN_ERROR'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('pagination', () => {
|
||||
it('should handle pagination parameters', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
results: [],
|
||||
page: 2,
|
||||
total_pages: 5,
|
||||
meta: {
|
||||
count: 0,
|
||||
page: 2,
|
||||
per_page: 20,
|
||||
total_pages: 5
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const result = await adapter.searchAll({
|
||||
query: 'test',
|
||||
page: 2,
|
||||
per: 20
|
||||
})
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
body: JSON.stringify({
|
||||
locale: 'en',
|
||||
page: 2,
|
||||
per: 20,
|
||||
query: 'test'
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
expect(result.page).toBe(2)
|
||||
expect(result.totalPages).toBe(5)
|
||||
})
|
||||
})
|
||||
})
|
||||
22
src/lib/api/adapters/index.ts
Normal file
22
src/lib/api/adapters/index.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* Main export file for the adapter system
|
||||
*
|
||||
* This module re-exports all public APIs from the adapter system,
|
||||
* providing a single entry point for consumers.
|
||||
*
|
||||
* @module adapters
|
||||
*/
|
||||
|
||||
// Core exports
|
||||
export { BaseAdapter } from './base.adapter'
|
||||
export * from './types'
|
||||
export * from './errors'
|
||||
|
||||
// Resource-specific adapters
|
||||
export { SearchAdapter, searchAdapter } from './search.adapter'
|
||||
export type { SearchParams, SearchResult, SearchResponse } from './search.adapter'
|
||||
// export { PartyAdapter } from './party.adapter'
|
||||
// export { GridAdapter } from './grid.adapter'
|
||||
|
||||
// Reactive resources using Svelte 5 runes
|
||||
export * from './resources'
|
||||
296
src/lib/api/adapters/search.adapter.ts
Normal file
296
src/lib/api/adapters/search.adapter.ts
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
/**
|
||||
* Search Adapter for Entity Search Operations
|
||||
*
|
||||
* Handles all search-related API calls for weapons, characters, and summons.
|
||||
* Provides unified interface with automatic transformation and error handling.
|
||||
*
|
||||
* @module adapters/search
|
||||
*/
|
||||
|
||||
import { BaseAdapter } from './base.adapter'
|
||||
import type { AdapterOptions, SearchFilters } from './types'
|
||||
|
||||
/**
|
||||
* Search parameters for entity queries
|
||||
* Used across all search methods
|
||||
*/
|
||||
export interface SearchParams {
|
||||
/** Search query string */
|
||||
query?: string
|
||||
/** Locale for search results */
|
||||
locale?: 'en' | 'ja'
|
||||
/** Entity IDs to exclude from results */
|
||||
exclude?: string[]
|
||||
/** Page number for pagination */
|
||||
page?: number
|
||||
/** Number of results per page */
|
||||
per?: number
|
||||
/** Search filters */
|
||||
filters?: SearchFilters
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual search result item
|
||||
* Represents a weapon, character, or summon
|
||||
*/
|
||||
export interface SearchResult {
|
||||
/** Unique entity ID */
|
||||
id: string
|
||||
/** Granblue game ID */
|
||||
granblueId: string
|
||||
/** Localized names */
|
||||
name: {
|
||||
en?: string
|
||||
ja?: string
|
||||
}
|
||||
/** Element type (1-6 for different elements) */
|
||||
element?: number
|
||||
/** Rarity level */
|
||||
rarity?: number
|
||||
/** Weapon/Character proficiency */
|
||||
proficiency?: number
|
||||
/** Series ID for categorization */
|
||||
series?: number
|
||||
/** URL for entity image */
|
||||
imageUrl?: string
|
||||
/** Type of entity */
|
||||
searchableType: 'Weapon' | 'Character' | 'Summon'
|
||||
}
|
||||
|
||||
/**
|
||||
* Search API response structure
|
||||
* Contains results and pagination metadata
|
||||
*/
|
||||
export interface SearchResponse {
|
||||
/** Array of search results */
|
||||
results: SearchResult[]
|
||||
/** Total number of results */
|
||||
total?: number
|
||||
/** Current page number */
|
||||
page?: number
|
||||
/** Total number of pages */
|
||||
totalPages?: number
|
||||
/** Pagination metadata */
|
||||
meta?: {
|
||||
count: number
|
||||
page: number
|
||||
perPage: number
|
||||
totalPages: number
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter for search-related API operations
|
||||
* Handles entity search with filtering, pagination, and caching
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const searchAdapter = new SearchAdapter()
|
||||
*
|
||||
* // Search for fire weapons
|
||||
* const weapons = await searchAdapter.searchWeapons({
|
||||
* query: 'sword',
|
||||
* filters: { element: [1] }
|
||||
* })
|
||||
*
|
||||
* // Search across all entity types
|
||||
* const results = await searchAdapter.searchAll({
|
||||
* query: 'bahamut',
|
||||
* page: 1
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export class SearchAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Creates a new SearchAdapter instance
|
||||
*
|
||||
* @param options - Adapter configuration options
|
||||
*/
|
||||
constructor(options?: AdapterOptions) {
|
||||
super({
|
||||
...options,
|
||||
// Search endpoints don't use credentials to avoid CORS issues
|
||||
// This is handled per-request instead
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds search request body from parameters
|
||||
* Handles filtering logic and defaults
|
||||
*
|
||||
* @param params - Search parameters
|
||||
* @param includeFilters - Which filters to include
|
||||
* @returns Request body object
|
||||
*/
|
||||
private buildSearchBody(
|
||||
params: SearchParams,
|
||||
includeFilters: {
|
||||
element?: boolean
|
||||
rarity?: boolean
|
||||
proficiency1?: boolean
|
||||
proficiency2?: boolean
|
||||
series?: boolean
|
||||
extra?: boolean
|
||||
subaura?: boolean
|
||||
} = {}
|
||||
): any {
|
||||
const body: any = {
|
||||
locale: params.locale || 'en',
|
||||
page: params.page || 1
|
||||
}
|
||||
|
||||
// Only include per if specified
|
||||
if (params.per) {
|
||||
body.per = params.per
|
||||
}
|
||||
|
||||
// Only include query if provided and not empty
|
||||
if (params.query) {
|
||||
body.query = params.query
|
||||
}
|
||||
|
||||
// Only include exclude if provided
|
||||
if (params.exclude?.length) {
|
||||
body.exclude = params.exclude
|
||||
}
|
||||
|
||||
// Build filters based on what's allowed for this search type
|
||||
if (params.filters) {
|
||||
const filters: any = {}
|
||||
|
||||
if (includeFilters.element && params.filters.element?.length) {
|
||||
filters.element = params.filters.element
|
||||
}
|
||||
if (includeFilters.rarity && params.filters.rarity?.length) {
|
||||
filters.rarity = params.filters.rarity
|
||||
}
|
||||
if (includeFilters.proficiency1 && params.filters.proficiency1?.length) {
|
||||
filters.proficiency1 = params.filters.proficiency1
|
||||
}
|
||||
if (includeFilters.proficiency2 && params.filters.proficiency2?.length) {
|
||||
filters.proficiency2 = params.filters.proficiency2
|
||||
}
|
||||
if (includeFilters.series && params.filters.series?.length) {
|
||||
filters.series = params.filters.series
|
||||
}
|
||||
if (includeFilters.extra && params.filters.extra !== undefined) {
|
||||
filters.extra = params.filters.extra
|
||||
}
|
||||
if (includeFilters.subaura && params.filters.subaura !== undefined) {
|
||||
filters.subaura = params.filters.subaura
|
||||
}
|
||||
|
||||
if (Object.keys(filters).length > 0) {
|
||||
body.filters = filters
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches across all entity types (weapons, characters, summons)
|
||||
*
|
||||
* @param params - Search parameters
|
||||
* @returns Promise resolving to search results
|
||||
*/
|
||||
async searchAll(params: SearchParams = {}): Promise<SearchResponse> {
|
||||
const body = this.buildSearchBody(params, {
|
||||
element: true,
|
||||
rarity: true,
|
||||
proficiency1: true,
|
||||
proficiency2: true,
|
||||
series: true,
|
||||
extra: true,
|
||||
subaura: true
|
||||
})
|
||||
|
||||
// Search endpoints don't use credentials to avoid CORS
|
||||
return this.request<SearchResponse>('/search/all', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
credentials: 'omit',
|
||||
// Cache search results for 5 minutes by default
|
||||
cache: params.query ? 300000 : 0 // Don't cache empty searches
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for weapons with specific filters
|
||||
*
|
||||
* @param params - Search parameters
|
||||
* @returns Promise resolving to weapon search results
|
||||
*/
|
||||
async searchWeapons(params: SearchParams = {}): Promise<SearchResponse> {
|
||||
const body = this.buildSearchBody(params, {
|
||||
element: true,
|
||||
rarity: true,
|
||||
proficiency1: true,
|
||||
extra: true
|
||||
})
|
||||
|
||||
return this.request<SearchResponse>('/search/weapons', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
credentials: 'omit',
|
||||
cache: params.query ? 300000 : 0
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for characters with specific filters
|
||||
*
|
||||
* @param params - Search parameters
|
||||
* @returns Promise resolving to character search results
|
||||
*/
|
||||
async searchCharacters(params: SearchParams = {}): Promise<SearchResponse> {
|
||||
const body = this.buildSearchBody(params, {
|
||||
element: true,
|
||||
rarity: true,
|
||||
proficiency1: true,
|
||||
proficiency2: true
|
||||
})
|
||||
|
||||
return this.request<SearchResponse>('/search/characters', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
credentials: 'omit',
|
||||
cache: params.query ? 300000 : 0
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for summons with specific filters
|
||||
*
|
||||
* @param params - Search parameters
|
||||
* @returns Promise resolving to summon search results
|
||||
*/
|
||||
async searchSummons(params: SearchParams = {}): Promise<SearchResponse> {
|
||||
const body = this.buildSearchBody(params, {
|
||||
element: true,
|
||||
rarity: true,
|
||||
subaura: true
|
||||
})
|
||||
|
||||
return this.request<SearchResponse>('/search/summons', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
credentials: 'omit',
|
||||
cache: params.query ? 300000 : 0
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cached search results
|
||||
* Useful when entity data has been updated
|
||||
*/
|
||||
clearSearchCache(): void {
|
||||
this.clearCache('search')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default singleton instance for search operations
|
||||
* Use this for most search needs unless you need custom configuration
|
||||
*/
|
||||
export const searchAdapter = new SearchAdapter()
|
||||
Loading…
Reference in a new issue