2023-05-05 16:47:55 +02:00
|
|
|
import {
|
2023-05-15 18:36:16 +02:00
|
|
|
ConnectionInfo,
|
2023-05-05 16:47:55 +02:00
|
|
|
DatasourceFeature,
|
|
|
|
DatasourceFieldType,
|
|
|
|
Integration,
|
|
|
|
QueryType,
|
|
|
|
} from "@budibase/types"
|
2022-03-31 16:44:06 +02:00
|
|
|
import Redis from "ioredis"
|
2022-03-31 11:56:16 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
interface RedisConfig {
|
|
|
|
host: string
|
|
|
|
port: number
|
|
|
|
username: string
|
|
|
|
password?: string
|
2023-04-12 14:51:40 +02:00
|
|
|
db?: number
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2022-03-31 11:56:16 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
const SCHEMA: Integration = {
|
|
|
|
docs: "https://redis.io/docs/",
|
2023-05-05 16:47:55 +02:00
|
|
|
description:
|
|
|
|
"Redis is a caching tool, providing powerful key-value store capabilities.",
|
2022-08-12 18:03:06 +02:00
|
|
|
friendlyName: "Redis",
|
|
|
|
type: "Non-relational",
|
2023-05-24 10:50:51 +02:00
|
|
|
features: {
|
|
|
|
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
datasource: {
|
|
|
|
host: {
|
2023-06-28 16:16:47 +02:00
|
|
|
type: DatasourceFieldType.STRING,
|
2022-08-12 18:03:06 +02:00
|
|
|
required: true,
|
|
|
|
default: "localhost",
|
|
|
|
},
|
|
|
|
port: {
|
2023-06-28 16:16:47 +02:00
|
|
|
type: DatasourceFieldType.NUMBER,
|
2022-08-12 18:03:06 +02:00
|
|
|
required: true,
|
|
|
|
default: 6379,
|
|
|
|
},
|
|
|
|
username: {
|
2023-06-28 16:16:47 +02:00
|
|
|
type: DatasourceFieldType.STRING,
|
2022-08-12 18:03:06 +02:00
|
|
|
required: false,
|
|
|
|
},
|
|
|
|
password: {
|
2023-06-28 16:16:47 +02:00
|
|
|
type: DatasourceFieldType.PASSWORD,
|
2022-08-12 18:03:06 +02:00
|
|
|
required: false,
|
2022-03-31 11:56:16 +02:00
|
|
|
},
|
2023-04-12 14:51:40 +02:00
|
|
|
db: {
|
2023-06-28 16:16:47 +02:00
|
|
|
type: DatasourceFieldType.NUMBER,
|
2023-04-12 14:51:40 +02:00
|
|
|
required: false,
|
|
|
|
display: "DB",
|
|
|
|
default: 0,
|
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
},
|
|
|
|
query: {
|
|
|
|
create: {
|
|
|
|
type: QueryType.FIELDS,
|
|
|
|
fields: {
|
|
|
|
key: {
|
|
|
|
type: DatasourceFieldType.STRING,
|
|
|
|
required: true,
|
2022-03-31 16:44:06 +02:00
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
value: {
|
|
|
|
type: DatasourceFieldType.STRING,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
ttl: {
|
|
|
|
type: DatasourceFieldType.NUMBER,
|
2022-03-31 16:44:06 +02:00
|
|
|
},
|
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
},
|
|
|
|
read: {
|
|
|
|
readable: true,
|
|
|
|
type: QueryType.FIELDS,
|
|
|
|
fields: {
|
|
|
|
key: {
|
|
|
|
type: DatasourceFieldType.STRING,
|
|
|
|
required: true,
|
2022-03-31 16:44:06 +02:00
|
|
|
},
|
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
},
|
|
|
|
delete: {
|
|
|
|
type: QueryType.FIELDS,
|
|
|
|
fields: {
|
|
|
|
key: {
|
|
|
|
type: DatasourceFieldType.STRING,
|
|
|
|
required: true,
|
|
|
|
},
|
2022-03-31 11:56:16 +02:00
|
|
|
},
|
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
command: {
|
|
|
|
readable: true,
|
|
|
|
displayName: "Redis Command",
|
|
|
|
type: QueryType.JSON,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2022-03-31 11:56:16 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
class RedisIntegration {
|
|
|
|
private readonly config: RedisConfig
|
2023-05-15 10:16:06 +02:00
|
|
|
private client
|
2022-03-31 16:44:06 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
constructor(config: RedisConfig) {
|
|
|
|
this.config = config
|
|
|
|
this.client = new Redis({
|
|
|
|
host: this.config.host,
|
|
|
|
port: this.config.port,
|
|
|
|
username: this.config.username,
|
|
|
|
password: this.config.password,
|
2023-04-12 14:51:40 +02:00
|
|
|
db: this.config.db,
|
2022-08-12 18:03:06 +02:00
|
|
|
})
|
|
|
|
}
|
2022-03-31 16:44:06 +02:00
|
|
|
|
2023-05-15 10:16:06 +02:00
|
|
|
async testConnection() {
|
2023-05-15 18:36:16 +02:00
|
|
|
const response: ConnectionInfo = {
|
|
|
|
connected: false,
|
|
|
|
}
|
2023-05-15 10:16:06 +02:00
|
|
|
try {
|
|
|
|
await this.client.ping()
|
2023-05-15 18:36:16 +02:00
|
|
|
response.connected = true
|
2023-05-15 10:16:06 +02:00
|
|
|
} catch (e: any) {
|
2023-05-15 18:36:16 +02:00
|
|
|
response.error = e.message as string
|
2023-05-15 10:16:06 +02:00
|
|
|
} finally {
|
|
|
|
await this.disconnect()
|
|
|
|
}
|
2023-05-15 18:36:16 +02:00
|
|
|
return response
|
2023-05-15 10:16:06 +02:00
|
|
|
}
|
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async disconnect() {
|
2023-03-27 20:38:49 +02:00
|
|
|
return this.client.quit()
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2022-03-31 11:56:16 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async redisContext(query: Function) {
|
|
|
|
try {
|
|
|
|
return await query()
|
|
|
|
} catch (err) {
|
|
|
|
throw new Error(`Redis error: ${err}`)
|
|
|
|
} finally {
|
2022-09-07 13:40:45 +02:00
|
|
|
await this.disconnect()
|
2022-03-31 16:44:06 +02:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2022-03-31 16:44:06 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async create(query: { key: string; value: string; ttl: number }) {
|
|
|
|
return this.redisContext(async () => {
|
|
|
|
const response = await this.client.set(query.key, query.value)
|
|
|
|
if (query.ttl) {
|
|
|
|
await this.client.expire(query.key, query.ttl)
|
|
|
|
}
|
|
|
|
return response
|
|
|
|
})
|
|
|
|
}
|
2022-03-31 16:44:06 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async read(query: { key: string }) {
|
|
|
|
return this.redisContext(async () => {
|
2022-09-07 13:40:45 +02:00
|
|
|
return await this.client.get(query.key)
|
2022-08-12 18:03:06 +02:00
|
|
|
})
|
|
|
|
}
|
2022-03-31 16:44:06 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async delete(query: { key: string }) {
|
|
|
|
return this.redisContext(async () => {
|
2022-09-07 13:40:45 +02:00
|
|
|
return await this.client.del(query.key)
|
2022-08-12 18:03:06 +02:00
|
|
|
})
|
2022-03-31 11:56:16 +02:00
|
|
|
}
|
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async command(query: { json: string }) {
|
|
|
|
return this.redisContext(async () => {
|
2022-09-07 13:40:45 +02:00
|
|
|
// commands split line by line
|
|
|
|
const commands = query.json.trim().split("\n")
|
|
|
|
let pipelineCommands = []
|
2022-09-02 19:35:06 +02:00
|
|
|
|
2022-09-07 13:40:45 +02:00
|
|
|
// process each command separately
|
|
|
|
for (let command of commands) {
|
|
|
|
const tokenised = command.trim().split(" ")
|
|
|
|
// Pipeline only accepts lower case commands
|
|
|
|
tokenised[0] = tokenised[0].toLowerCase()
|
|
|
|
pipelineCommands.push(tokenised)
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2022-09-02 19:35:06 +02:00
|
|
|
|
2022-09-07 13:40:45 +02:00
|
|
|
const pipeline = this.client.pipeline(pipelineCommands)
|
|
|
|
const result = await pipeline.exec()
|
2022-09-02 19:35:06 +02:00
|
|
|
|
2023-06-01 12:10:39 +02:00
|
|
|
return result?.map((output: any) => {
|
|
|
|
if (typeof output === "string") {
|
|
|
|
return output
|
|
|
|
} else if (Array.isArray(output)) {
|
|
|
|
return output[1]
|
|
|
|
}
|
|
|
|
})
|
2022-08-12 18:03:06 +02:00
|
|
|
})
|
2022-03-31 11:56:16 +02:00
|
|
|
}
|
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
|
|
|
|
export default {
|
|
|
|
schema: SCHEMA,
|
|
|
|
integration: RedisIntegration,
|
|
|
|
}
|