Skip to content

Commit 37b0788

Browse files
committed
feat: split api between client and core admin and simplify internal design
BREAKING CHANGE: - config: - removed parameters debug, apiPrefix - urlConfig moved up one level - printing of debugging messages removed - prepareSolrClient accepts first parameter `core` and optinoal parameter `userConfig` - added function `commit` - added function `solrListFields` - moved function `ping` to `prepareCoreAdmin` - added function `solrDeleteCore` to `prepareCoreAdmin` - functions `deleteField` and `deleteFieldType` now accepts a single `name` parameter rather than object
1 parent 0193083 commit 37b0788

3 files changed

Lines changed: 119 additions & 106 deletions

File tree

src/index.js

Lines changed: 107 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,139 +3,163 @@
33
* Support for type checking and intellisense in vscode:
44
* @typedef {import("./solr").SolrConfig} SolrConfig
55
* @typedef {import("./solr").ConfigRequest} ConfigRequest
6-
* @typedef {import("./solr").SolrData} SolrData
6+
* @typedef {import("./solr").SolrException} SolrException
77
* @typedef {import("./solr").SolrDocument} SolrDocument
88
* @typedef {import("./solr").SolrResponse} SolrResponse
99
* @typedef {import("./solr").DeleteRequest} DeleteRequest
1010
* @typedef {import("./solr").FieldProperties} FieldProperties
1111
* @typedef {import("./solr").FieldTypeProperties} FieldTypeProperties
1212
* @typedef {import("./solr").QueryRequest} QueryRequest
13+
* @typedef {{name:string, type?:string, [key:string]:any}} FieldDef
14+
* @typedef {import("axios").AxiosResponse} AxiosResponse
1315
*/
1416

1517
const url = require("url")
1618
const { default: axios } = require("axios")
17-
const { mergeConfig, ensureArray, isEmptyObject } = require("./utils")
19+
const { mergeConfig, ensureArray } = require("./utils")
1820

1921
/** @type {SolrConfig} */
2022
const defaultConfig = {
21-
urlConfig: {
22-
hostname: "localhost",
23-
port: 8983,
24-
protocol: "http",
25-
query: {
26-
commitWithin: 500,
27-
overwrite: true,
28-
wt: "json"
29-
}
30-
},
31-
debug: false,
32-
apiPrefix: "api/cores"
23+
hostname: "localhost",
24+
port: 8983,
25+
protocol: "http",
26+
query: {
27+
commitWithin: 500,
28+
overwrite: true
29+
}
3330
}
3431

35-
/** @type {(config:SolrConfig) => (path:string) => (data:SolrData) => Promise<SolrResponse>} */
36-
const solrPost = config => path => async data => {
37-
const { core, apiPrefix } = config
38-
const solrUrl = url.format({
39-
...config.urlConfig,
40-
pathname: `${apiPrefix}/${core}/${path}`
41-
})
42-
43-
if (config.debug) {
44-
const dataPart = isEmptyObject(data) ? "" : `-d '${JSON.stringify(data)}'`
45-
console.debug(
46-
`\ncurl -X POST '${solrUrl}' -H 'Content-Type: application/json' ${dataPart}\n`
47-
)
32+
/** @type {(axiosResp:AxiosResponse) => SolrResponse} */
33+
const convertSolrResponse = axiosResp => axiosResp.data
34+
35+
// converting error response from solr
36+
/** @param {SolrException} reason */
37+
const convertSolrError = reason => {
38+
switch (reason.response.status) {
39+
case 400:
40+
throw Error(reason.response.data.error.msg)
41+
case 404:
42+
throw Error(`${reason.message}: ${reason.response.statusText}`)
43+
default:
44+
throw reason
4845
}
49-
50-
const response = await axios
51-
.post(solrUrl, data, {
52-
headers: { "Content-Type": "application/json" }
53-
})
54-
.catch(reason => {
55-
// converting error response from solr
56-
switch (reason.response.status) {
57-
case 400:
58-
throw Error(reason.response.data.error.msg)
59-
case 404:
60-
throw Error(`${reason.message}: ${reason.response.statusText}`)
61-
default:
62-
throw reason
63-
}
64-
})
65-
66-
return response.data
6746
}
6847

69-
/** @param {SolrConfig} userConfig */
70-
const prepareSolrClient = (userConfig = {}) => {
48+
/**
49+
* @param {SolrConfig} userConfig This is merged with the defaultConfig
50+
* @param {string} core
51+
*/
52+
const prepareSolrClient = (core, userConfig = {}) => {
53+
console.assert(core, "Missing 'core' parameter")
54+
7155
const config = mergeConfig(defaultConfig, userConfig)
7256

73-
// sanity checks
74-
if (!config.core) {
75-
throw Error("missing 'core' parameter in your config")
76-
}
57+
/** @type {(pathname:string) => (data:any) => Promise<SolrResponse>} */
58+
const httpPostReq = pathname => data =>
59+
axios
60+
.post(url.format({ ...config, pathname }), data)
61+
.then(convertSolrResponse)
62+
.catch(convertSolrError)
7763

78-
// some functions require /solr prefix instead of /api/cores/
79-
const configWithSolrPrefix = { ...config, apiPrefix: "solr" }
64+
/** @type {(pathname:string) => (query:any) => Promise<SolrResponse>} */
65+
const httpGetReq = pathname => query =>
66+
axios
67+
.get(url.format({ ...mergeConfig(config, { query }), pathname }))
68+
.then(convertSolrResponse)
69+
.catch(convertSolrError)
8070

81-
// we already use the variable config, therefore solrConfigRequest
82-
// represents the "config" API call from Solr
83-
const solrConfigRequest = solrPost(configWithSolrPrefix)("config")
71+
/** @type {(op:string) => (fieldDef:FieldDef) => Promise<SolrResponse>} */
72+
const solrSchemaReq = op => fieldDef =>
73+
httpPostReq(`/api/cores/${core}/schema`)({ [op]: fieldDef })
8474

85-
const solrSchemaRequest = op => data =>
86-
solrPost(config)("schema")({ [op]: data })
75+
/** @type {(data:ConfigRequest) => Promise<SolrResponse> } */
76+
const configReq = httpPostReq(`/api/cores/${core}/config`)
8777

8878
// now creating the API
8979
return {
9080
mergedConfig: () => config,
91-
ping: () =>
92-
solrPost(configWithSolrPrefix)("admin/ping")({})
93-
.then(value => {
94-
return value.status === "OK"
95-
})
96-
.catch(() => false),
81+
82+
commit: () => httpGetReq(`/solr/${core}/update`)({ commit: true }),
83+
84+
/** @type {(data:QueryRequest) => Promise<SolrResponse>} */
85+
query: httpPostReq(`/api/cores/${core}/query`),
9786

9887
/** @param {SolrDocument | SolrDocument[]} data */
99-
add: data => solrPost(config)("update")(ensureArray(data)),
88+
add: data => httpPostReq(`/api/cores/${core}/update`)(ensureArray(data)),
10089

10190
/** @param {DeleteRequest} deleteQuery */
10291
delete: deleteQuery =>
103-
solrPost(configWithSolrPrefix)("update")({
104-
delete: deleteQuery
105-
}),
92+
httpPostReq(`/api/cores/${core}/update`)({ delete: deleteQuery }),
93+
94+
/** @param {string} name */
95+
deleteField: name => solrSchemaReq("delete-field")({ name }),
96+
97+
/** @param {string} name */
98+
deleteFieldType: name => solrSchemaReq("delete-field-type")({ name }),
10699

107100
/** @type {(data:FieldProperties) => Promise<SolrResponse>} */
108-
addField: solrSchemaRequest("add-field"),
101+
addField: solrSchemaReq("add-field"),
109102

110103
/** @type {(data:FieldTypeProperties) => Promise<SolrResponse>} */
111-
addFieldType: solrSchemaRequest("add-field-type"),
104+
addFieldType: solrSchemaReq("add-field-type"),
112105

113-
/** @type {({name:string}) => Promise<SolrResponse>} */
114-
deleteField: solrSchemaRequest("delete-field"),
106+
/** @type {(data:FieldTypeProperties) => Promise<SolrResponse>} */
107+
replaceField: solrSchemaReq("replace-field"),
115108

116-
/** @type {({name:string}) => Promise<SolrResponse>} */
117-
deleteFieldType: solrSchemaRequest("delete-field-type"),
109+
config: configReq,
118110

119-
/** @type {(data:QueryRequest) => Promise<SolrResponse>} */
120-
query: solrPost(config)("query"),
111+
/** @type {() => Promise<string[]>} */
112+
solrListFields: () =>
113+
httpGetReq(`/solr/${core}/schema/fields`)({}).then(data =>
114+
data.fields.map(field => field.name)
115+
),
121116

122-
config: solrConfigRequest,
123-
124-
/**
125-
* Convenience function to set the `update.autoCreateFields` user property.
126-
* @param {boolean} enable
127-
*/
117+
/** @param {boolean} enable */
128118
configAutoEnableFields: enable =>
129-
solrConfigRequest({
119+
configReq({
130120
"set-user-property": {
131121
"update.autoCreateFields": enable ? "true" : "false"
132122
}
133123
})
134124
}
135125
}
136126

127+
/** @param {SolrConfig} userConfig */
128+
const prepareCoreAdmin = userConfig => {
129+
const config = mergeConfig(defaultConfig, userConfig)
130+
return {
131+
/** @type {(core:string) => Promise<SolrResponse>} */
132+
ping: core =>
133+
axios
134+
.get(url.format({ ...config, pathname: `/solr/${core}/admin/ping` }))
135+
.then(convertSolrResponse)
136+
.catch(convertSolrError),
137+
138+
/** @type {(core:string) => Promise<SolrResponse>} */
139+
solrDeleteCore: core =>
140+
axios
141+
.get(
142+
url.format({
143+
...mergeConfig(config, {
144+
query: {
145+
core,
146+
action: "UNLOAD",
147+
deleteIndex: true,
148+
deleteDataDir: true,
149+
deleteInstanceDir: true
150+
}
151+
}),
152+
pathname: "/solr/admin/cores"
153+
})
154+
)
155+
.then(convertSolrResponse)
156+
.catch(convertSolrError)
157+
}
158+
}
159+
137160
module.exports = {
138161
prepareSolrClient,
162+
prepareCoreAdmin,
139163
defaultConfig,
140164
mergeConfig
141165
}

src/solr.d.ts

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ interface SolrResponseHeader {
308308
*/
309309
export interface SolrResponse {
310310
status?: string
311+
fields?: any
311312
facets?: {
312313
count?: number
313314
} & {
@@ -332,11 +333,8 @@ export interface SolrException {
332333
config
333334
data: {
334335
error: {
336+
msg: string
335337
code: number
336-
details: {
337-
errorMessages: string[]
338-
[key: string]: object
339-
}[]
340338
metadata: string[]
341339
}
342340
responseHeader: SolrResponseHeader
@@ -365,18 +363,13 @@ export interface QueryRequest {
365363
}
366364
}
367365

368-
export interface SolrConfig {
369-
urlConfig?: UrlObject & {
370-
query?: {
371-
overwrite?: boolean
372-
commitWithin?: number
373-
wt?: "json" | "xml" | "python" | "ruby" | "php" | "csv"
374-
[key: string]: any
375-
}
366+
export type SolrConfig = UrlObject & {
367+
query?: {
368+
overwrite?: boolean
369+
commitWithin?: number
370+
wt?: "json" | "xml" | "python" | "ruby" | "php" | "csv"
371+
[key: string]: any
376372
}
377-
debug?: boolean
378-
core?: string
379-
apiPrefix?: string
380373
}
381374

382375
export interface DeleteRequest {
@@ -435,11 +428,11 @@ export type ConfigRequest = {
435428
// Commands for User-Defined Properties:
436429
// https://lucene.apache.org/solr/guide/7_5/config-api.html#commands-for-user-defined-properties
437430
"set-user-property"?: {
438-
"update.autoCreateFields"?: TrueOrFalseString // boolean does not work, perhaps a bug in solr
431+
"update.autoCreateFields"?: TrueFalseString // boolean does not work, perhaps a bug in solr
439432
[variableName: string]: any
440433
}
441434
"unset-user-property"?: string
442435
}
443436

444-
type TrueOrFalseString = "true" | "false"
445-
type OnOrOffString = "on" | "off"
437+
type TrueFalseString = "true" | "false"
438+
type OnOffString = "on" | "off"

src/utils.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,8 @@ function mergeConfigImpure (target, source) {
4545
*/
4646
const ensureArray = x => x instanceof Array ? x : [x]
4747

48-
const isEmptyObject = obj =>
49-
Object.keys(obj).length === 0 && obj.constructor === Object
50-
5148
module.exports = {
5249
mergeConfig,
5350
mergeConfigImpure,
54-
ensureArray,
55-
isEmptyObject
51+
ensureArray
5652
}

0 commit comments

Comments
 (0)