Skip to content

Commit

Permalink
added cache
Browse files Browse the repository at this point in the history
  • Loading branch information
darkxanter committed Apr 22, 2022
1 parent 22577a4 commit ee1d088
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 15 deletions.
59 changes: 48 additions & 11 deletions src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,35 @@ import { DaDataBankInfo } from '../models/bank'
import { DaDataOrganizationInfo } from '../models/organization'
import { DaDataSuggestion } from '../models/suggestion'
import { validateSuggestions } from '../utils/guards'
import simpleHash from '../utils/hash'
import { camelCaseReviver } from '../utils/json'
import { DaDataBankRequest, DaDataOrganizationRequest } from './requests'
import { DaDataBankRequest, DaDataOrganizationRequest, DaDataQuery } from './requests'

export interface ApiClientOptions {
/**
* @default https://suggestions.dadata.ru/suggestions/api/4_1/rs
*/
endpoint?: string
token?: string
/** @default: true */
cache?: boolean
}

type MethodType = 'suggest' | 'findById'
type SuggestionsType = 'party' | 'bank'
export type DaDataSuggestionsMethod = `${MethodType}/${SuggestionsType}`

export default class ApiClient {
readonly endpoint: string
readonly token: string | undefined
readonly useCache: boolean
protected cache = new Map<string, object>()

constructor(options?: ApiClientOptions) {
const { endpoint = 'https://suggestions.dadata.ru/suggestions/api/4_1/rs', token } = options ?? {}
const { endpoint = 'https://suggestions.dadata.ru/suggestions/api/4_1/rs' } = options ?? {}
this.endpoint = endpoint.replace(/\/$/, '')
this.token = token
this.token = options?.token
this.useCache = options?.cache ?? true
}

/**
Expand All @@ -40,7 +50,7 @@ export default class ApiClient {
*
*/
suggestBank(query: string, options?: DaDataBankRequest): Promise<DaDataSuggestion<DaDataBankInfo>[]> {
return this.makeRequest('suggest/bank', { ...options, query })
return this.request('suggest/bank', { ...options, query })
}

/**
Expand All @@ -54,7 +64,7 @@ export default class ApiClient {
* Ищет только по точному совпадению, для частичного совпадения используйте метод @see suggestBank.
*/
findBankById(query: string): Promise<DaDataSuggestion<DaDataBankInfo>[]> {
return this.makeRequest('findById/bank', { query })
return this.request('findById/bank', { query })
}

/**
Expand All @@ -69,20 +79,47 @@ export default class ApiClient {
query: string,
options?: DaDataOrganizationRequest,
): Promise<DaDataSuggestion<DaDataOrganizationInfo>[]> {
return this.makeRequest('suggest/party', { ...options, query })
return this.request('suggest/party', { ...options, query })
}

/**
* Находит компанию или ИП по ИНН или ОГРН.
* Возвращает все доступные сведения о компании, в отличие от метода suggest, который возвращает только базовые поля.
*/
findOrganizationById(query: string): Promise<DaDataSuggestion<DaDataOrganizationInfo>[]> {
return this.makeRequest('findById/party', { query })
return this.request('findById/party', { query })
}

clearCache() {
this.cache.clear()
}

async request<TResult extends object>(
method: DaDataSuggestionsMethod,
params: DaDataQuery & Record<string, unknown>,
): Promise<DaDataSuggestion<TResult>[]> {
if (!params.query) {
return []
}
if (this.useCache) {
const cacheKey = this.getCacheKey(method, params)
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey) as DaDataSuggestion<TResult>[]
}
const response = this.doRequest<TResult>(method, params)
this.cache.set(cacheKey, response)
return response
}
return this.doRequest<TResult>(method, params)
}

protected getCacheKey(method: DaDataSuggestionsMethod, params: Record<string, unknown>) {
return `${method}/${simpleHash(params)}`
}

protected async makeRequest<TRequest extends object, TResult extends object>(
requestPath: string,
request: TRequest,
protected async doRequest<TResult extends object>(
requestMethod: DaDataSuggestionsMethod,
request: Record<string, unknown>,
): Promise<DaDataSuggestion<TResult>[]> {
const headers = new Headers({
'Content-Type': 'application/json',
Expand All @@ -93,7 +130,7 @@ export default class ApiClient {
headers.set('Authorization', `Token ${this.token}`)
}

const response = await fetch(`${this.endpoint}/${requestPath}`, {
const response = await fetch(`${this.endpoint}/${requestMethod}`, {
headers,
method: 'POST',
body: JSON.stringify(request),
Expand Down
5 changes: 5 additions & 0 deletions src/api/requests.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
export interface DaDataQuery {
/** Запрос */
query: string
}


export interface DaDataRequest {
/**
Expand Down
4 changes: 4 additions & 0 deletions src/utils/hash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default function simpleHash(obj: object) {
const entries = Object.entries(obj).sort(([a], [b]) => a.localeCompare(b))
return JSON.stringify(entries)
}
11 changes: 11 additions & 0 deletions tests/hash.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { describe, it, expect } from 'vitest'
import simpleHash from '../src/utils/hash'

describe('hash', () => {
it('compare two simple objects', () => {
const hash1 = simpleHash({ param1: 'a', param2: 'b' })
const hash2 = simpleHash({ param2: 'b', param1: 'a' })

expect(hash2).toBe(hash1)
})
})
5 changes: 4 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true
"noImplicitReturns": true,
"types": [
"vitest/importMeta"
]
},
"include": [
"src",
Expand Down
6 changes: 3 additions & 3 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ import { defineConfig } from 'vite'
import * as path from 'path'
import dts from 'vite-plugin-dts'

export default defineConfig({
export default defineConfig(() => ({
plugins: [
dts({
exclude: './src/vite-env.d.ts',
}),
],
test: {
environment: 'jsdom',
includeSource: ['src/**/*.{js,ts}'],
},
build: {
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
name: 'dadata',
formats: ['cjs', 'es'],
},
minify: false,
},
})
}))

0 comments on commit ee1d088

Please sign in to comment.