Skip to content

Commit

Permalink
refactor: all backend endpoints in API type
Browse files Browse the repository at this point in the history
  • Loading branch information
arildm committed Jan 7, 2025
1 parent 6baa113 commit aeabde9
Show file tree
Hide file tree
Showing 22 changed files with 172 additions and 150 deletions.
8 changes: 4 additions & 4 deletions app/scripts/backend/base-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import * as authenticationProxy from "@/components/auth/auth"
import { expandOperators } from "@/cqp_parser/cqp"
import settings from "@/settings"
import _ from "lodash"
import { ProgressReport, ProgressResponse, ResponseBase } from "@/backend/types"
import { API, ProgressReport, ProgressResponse, ResponseBase } from "@/backend/types"

/** The Proxy classes wrap API requests with pre-/postprocessing and progress reporting. */
export default abstract class BaseProxy<R extends {} = {}> {
export default abstract class BaseProxy<K extends keyof API> {
pendingRequests: JQuery.jqXHR[]

constructor() {
Expand Down Expand Up @@ -59,9 +59,9 @@ export default abstract class BaseProxy<R extends {} = {}> {
_.toPairs(header).forEach(([name, value]) => req.setRequestHeader(name, value))
}

calcProgress(e: ProgressEvent): ProgressReport<R> {
calcProgress(e: ProgressEvent): ProgressReport<K> {
const xhr = e.target as XMLHttpRequest
type PartialResponse = ResponseBase & ProgressResponse & Partial<R>
type PartialResponse = ResponseBase & ProgressResponse & Partial<API[K]["response"]>
let struct: PartialResponse = {}

try {
Expand Down
46 changes: 7 additions & 39 deletions app/scripts/backend/graph-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
import _ from "lodash"
import settings from "@/settings"
import BaseProxy from "@/backend/base-proxy"
import { AbsRelTuple, Granularity, Histogram, Response, NumericString } from "@/backend/types"
import { Granularity, Response, NumericString } from "@/backend/types"
import { ajaxConfAddMethod, Factory } from "@/util"
import { AjaxSettings } from "@/jquery.types"
import { CountTimeParams, CountTimeResponse } from "./types/count-time"

export class GraphProxy extends BaseProxy<KorpCountTimeResponse> {
export class GraphProxy extends BaseProxy<"count_time"> {
granularity: Granularity
prevParams: KorpCountTimeParams | null
prevParams: CountTimeParams | null

constructor() {
super()
Expand All @@ -32,10 +33,10 @@ export class GraphProxy extends BaseProxy<KorpCountTimeResponse> {
corpora: string,
from: NumericString,
to: NumericString
): JQuery.Promise<Response<KorpCountTimeResponse>> {
): JQuery.Promise<Response<CountTimeResponse>> {
this.resetRequest()
const self = this
const params: KorpCountTimeParams = {
const params: CountTimeParams = {
cqp: this.expandCQP(cqp),
corpus: corpora,
granularity: this.granularity,
Expand Down Expand Up @@ -75,7 +76,7 @@ export class GraphProxy extends BaseProxy<KorpCountTimeResponse> {
error(jqXHR, textStatus, errorThrown) {
def.reject(textStatus)
},
success(data: Response<KorpCountTimeResponse>) {
success(data: Response<CountTimeResponse>) {
def.resolve(data)
self.cleanup()
},
Expand All @@ -89,36 +90,3 @@ export class GraphProxy extends BaseProxy<KorpCountTimeResponse> {

const graphProxyFactory = new Factory(GraphProxy)
export default graphProxyFactory

/** @see https://ws.spraakbanken.gu.se/docs/korp#tag/Statistics/paths/~1count_time/get */
type KorpCountTimeParams = {
corpus: string
cqp: string
default_within?: string
with?: string
[subcqpn: `subcqp${string}`]: string
granularity?: Granularity
from?: NumericString
to?: NumericString
strategy?: 1 | 2 | 3
per_corpus?: boolean
combined?: boolean
[cqpn: `cqp${number}`]: string
expand_prequeries?: boolean
incremental?: boolean
}

/** @see https://ws.spraakbanken.gu.se/docs/korp#tag/Statistics/paths/~1count_time/get */
export type KorpCountTimeResponse = {
corpora: Record<string, KorpGraphStats | (KorpGraphStats | KorpGraphStatsCqp)[]>
combined: KorpGraphStats | (KorpGraphStats | KorpGraphStatsCqp)[]
}

export type KorpGraphStats = {
absolute: Histogram
relative: Histogram
sums: AbsRelTuple
}

/** Stats contains subquery if graph was created with multiple rows selected in the stats table. */
export type KorpGraphStatsCqp = KorpGraphStats & { cqp: string }
4 changes: 2 additions & 2 deletions app/scripts/backend/kwic-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ProgressReport, Response } from "./types"
import { QueryParams, QueryResponse } from "./types/query"
import { AjaxSettings } from "@/jquery.types"

export class KwicProxy extends BaseProxy<QueryResponse> {
export class KwicProxy extends BaseProxy<"query"> {
prevCQP?: string
prevParams: QueryParams | null
prevUrl?: string // Used for download
Expand All @@ -22,7 +22,7 @@ export class KwicProxy extends BaseProxy<QueryResponse> {
makeRequest(
options: KorpQueryRequestOptions,
page: number | undefined,
progressCallback: (data: ProgressReport<QueryResponse>) => void,
progressCallback: (data: ProgressReport<"query">) => void,
kwicCallback: (data: Response<QueryResponse>) => void
): JQuery.jqXHR<Response<QueryResponse>> {
const self = this
Expand Down
41 changes: 6 additions & 35 deletions app/scripts/backend/lemgram-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import BaseProxy from "@/backend/base-proxy"
import type { Response, ProgressReport } from "@/backend/types"
import { ajaxConfAddMethod, Factory } from "@/util"
import { AjaxSettings } from "@/jquery.types"
import { RelationsParams, RelationsResponse } from "./types/relations"

export class LemgramProxy extends BaseProxy<KorpRelationsResponse> {
prevParams?: KorpRelationsParams
export class LemgramProxy extends BaseProxy<"relations"> {
prevParams?: RelationsParams
prevUrl?: string

makeRequest(
word: string,
type: string,
callback: (data: ProgressReport<KorpRelationsResponse>) => void
): JQuery.jqXHR<Response<KorpRelationsResponse>> {
callback: (data: ProgressReport<"relations">) => void
): JQuery.jqXHR<Response<RelationsResponse>> {
this.resetRequest()
const self = this

Expand Down Expand Up @@ -48,41 +49,11 @@ export class LemgramProxy extends BaseProxy<KorpRelationsResponse> {
},
} satisfies AjaxSettings

const def = $.ajax(ajaxConfAddMethod(ajaxSettings)) as JQuery.jqXHR<Response<KorpRelationsResponse>>
const def = $.ajax(ajaxConfAddMethod(ajaxSettings)) as JQuery.jqXHR<Response<RelationsResponse>>
this.pendingRequests.push(def)
return def
}
}

const lemgramProxyFactory = new Factory(LemgramProxy)
export default lemgramProxyFactory

/** @see https://ws.spraakbanken.gu.se/docs/korp#tag/Word-Picture */
type KorpRelationsParams = {
corpus: string
word: string
type?: string
min?: number
max?: number
incremental?: boolean
}

export type KorpRelationsResponse = {
relations: ApiRelation[]
/** Execution time in seconds */
time: number
}

export type ApiRelation = {
dep: string
depextra: string
deppos: string
freq: number
head: string
headpos: string
/** Lexicographer's mutual information score */
mi: number
rel: string
/** List of IDs, for getting the source sentences */
source: string[]
}
16 changes: 13 additions & 3 deletions app/scripts/backend/stats-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@ import { StatisticsWorkerResult } from "@/statistics.types"
import { locationSearchGet, Factory, ajaxConfAddMethod } from "@/util"
import { statisticsService } from "@/statistics"
import { AjaxSettings } from "@/jquery.types"
import { CountParams, CountResponse, StatsColumn, StatsNormalized } from "./types/count"
import { CountParams, CountResponse, StatsColumn } from "./types/count"

/** Like `CountResponse` but the stats are necessarily arrays. */
export type StatsNormalized = {
corpora: {
[name: string]: StatsColumn[]
}
combined: StatsColumn[]
count: number
time: number
}

/**
* Stats in the response can be split by subqueries if the `subcqp#` param is used, but otherwise not.
Expand All @@ -27,7 +37,7 @@ export function normalizeStatsData(data: CountResponse): StatsNormalized {
return { ...data, combined, corpora }
}

export class StatsProxy extends BaseProxy<CountResponse> {
export class StatsProxy extends BaseProxy<"count"> {
prevParams: CountParams | null
prevUrl?: string

Expand Down Expand Up @@ -66,7 +76,7 @@ export class StatsProxy extends BaseProxy<CountResponse> {

makeRequest(
cqp: string,
callback: (data: ProgressReport<CountResponse>) => void
callback: (data: ProgressReport<"count">) => void
): JQuery.Promise<StatisticsWorkerResult> {
const self = this
this.resetRequest()
Expand Down
45 changes: 12 additions & 33 deletions app/scripts/backend/time-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@
import _ from "lodash"
import settings from "@/settings"
import BaseProxy from "@/backend/base-proxy"
import type { Granularity, Histogram, Response, NumericString } from "@/backend/types"
import type { Histogram } from "@/backend/types"
import { ajaxConfAddMethod, Factory } from "@/util"
import { AjaxSettings } from "@/jquery.types"
import { TimespanParams, TimespanResponse } from "./types/timespan"

export class TimeProxy extends BaseProxy<KorpTimespanResponse> {
/** Data returned after slight mangling. */
type TimeData = [
Record<string, Histogram>, // Same as KorpTimespanResponse.corpora
[number, number][], // Tokens per time period, as pairs ordered by time period
number // Tokens in undated material
]

export class TimeProxy extends BaseProxy<"timespan"> {
makeRequest(): JQueryDeferred<TimeData> {
const data: KorpTimespanParams = {
const data: TimespanParams = {
granularity: "y",
corpus: settings.corpusListing.stringifyAll(),
}
Expand All @@ -18,7 +26,7 @@ export class TimeProxy extends BaseProxy<KorpTimespanResponse> {
url: settings.korp_backend_url + "/timespan",
data,
} satisfies AjaxSettings
const xhr = $.ajax(ajaxConfAddMethod(ajaxSettings)) as JQuery.jqXHR<KorpTimespanResponse>
const xhr = $.ajax(ajaxConfAddMethod(ajaxSettings)) as JQuery.jqXHR<TimespanResponse>

xhr.done((data) => {
if ("ERROR" in data) {
Expand Down Expand Up @@ -87,32 +95,3 @@ export class TimeProxy extends BaseProxy<KorpTimespanResponse> {

const timeProxyFactory = new Factory(TimeProxy)
export default timeProxyFactory

/** @see https://ws.spraakbanken.gu.se/docs/korp#tag/Statistics/paths/~1timespan/get */
type KorpTimespanParams = {
corpus: string
granularity?: Granularity
from?: NumericString
to?: NumericString
strategy?: 1 | 2 | 3
per_corpus?: boolean
combined?: boolean
incremental?: boolean
}

/** @see https://ws.spraakbanken.gu.se/docs/korp#tag/Statistics/paths/~1timespan/get */
type KorpTimespanResponse = Response<{
/** An object with corpus names as keys and time statistics objects as values */
corpora: Record<string, Histogram>
/** Number of tokens per time period */
combined: Histogram
/** Execution time in seconds */
time: number
}>

/** Data returned after slight mangling. */
export type TimeData = [
Record<string, Histogram>, // Same as KorpTimespanResponse.corpora
[number, number][], // Tokens per time period, as pairs ordered by time period
number // Tokens in undated material
]
6 changes: 4 additions & 2 deletions app/scripts/backend/types/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/** @format */

import { API } from "."

/** A Korp response is either successful or has error info */
export type Response<R> = ResponseBase & (R | ErrorResponse)

Expand Down Expand Up @@ -33,9 +35,9 @@ export type ProgressResponse = {
[progress_n: `progress_${number}`]: string | { corpus: string; hits: number }
}

export type ProgressReport<R = {}> = {
export type ProgressReport<K extends keyof API> = {
/** Response data */
struct: ResponseBase & ProgressResponse & Partial<R>
struct: ResponseBase & ProgressResponse & Partial<API[K]["response"]>
/** How many percent of the material has been searched. */
stats: number
/** How many search hits so far. */
Expand Down
35 changes: 35 additions & 0 deletions app/scripts/backend/types/count-time.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/** @format */
/** @see https://ws.spraakbanken.gu.se/docs/korp#tag/Statistics/paths/~1count_time/get */

import { AbsRelTuple, Granularity, Histogram, NumericString } from "./common"

export type CountTimeParams = {
corpus: string
cqp: string
default_within?: string
with?: string
[subcqpn: `subcqp${string}`]: string
granularity?: Granularity
from?: NumericString
to?: NumericString
strategy?: 1 | 2 | 3
per_corpus?: boolean
combined?: boolean
[cqpn: `cqp${number}`]: string
expand_prequeries?: boolean
incremental?: boolean
}

export type CountTimeResponse = {
corpora: Record<string, GraphStats | (GraphStats | GraphStatsCqp)[]>
combined: GraphStats | (GraphStats | GraphStatsCqp)[]
}

export type GraphStats = {
absolute: Histogram
relative: Histogram
sums: AbsRelTuple
}

/** Stats contains subquery if graph was created with multiple rows selected in the stats table. */
export type GraphStatsCqp = GraphStats & { cqp: string }
10 changes: 0 additions & 10 deletions app/scripts/backend/types/count.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,6 @@ export type CountResponse = {
time: number
}

/** Like `CountResponse` but the stats are necessarily arrays. */
export type StatsNormalized = {
corpora: {
[name: string]: StatsColumn[]
}
combined: StatsColumn[]
count: number
time: number
}

export type StatsColumn = {
sums: AbsRelTuple
rows: StatsRow[]
Expand Down
15 changes: 15 additions & 0 deletions app/scripts/backend/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import { AttrValuesParams, AttrValuesResponseDeep, AttrValuesResponseFlat } from
import { CorpusConfigParams, CorpusConfigResponse } from "./corpus-config"
import { CorpusInfoParams, CorpusInfoResponse } from "./corpus-info"
import { CountParams, CountResponse } from "./count"
import { CountTimeParams, CountTimeResponse } from "./count-time"
import { LemgramCountParams, LemgramCountResponse } from "./lemgram-count"
import { LoglikeParams, LoglikeResponse } from "./loglike"
import { QueryParams, QueryResponse } from "./query"
import { RelationsParams, RelationsResponse } from "./relations"
import { TimespanParams, TimespanResponse } from "./timespan"

export * from "./common"

Expand All @@ -29,6 +32,10 @@ export type API = {
params: CountParams
response: CountResponse
}
count_time: {
params: CountTimeParams
response: CountTimeResponse
}
lemgram_count: {
params: LemgramCountParams
response: LemgramCountResponse
Expand All @@ -41,4 +48,12 @@ export type API = {
params: QueryParams
response: QueryResponse
}
relations: {
params: RelationsParams
response: RelationsResponse
}
timespan: {
params: TimespanParams
response: TimespanResponse
}
}
Loading

0 comments on commit aeabde9

Please sign in to comment.