import { FormComponentType, InputDataType, Plugin, PluginResponseType, PluginType } from '../../types/index.js'; import { PUBLIC_INTEGRATIONS_LOGO_URL } from './constants.js'; const RedisPluginVersions = { V1: '0.0.1' }; const CONNECTION_METHODS_AND_DISPLAY_NAMES: Record = { url: 'Connection string', fields: 'Form' }; const EXPIRE_OPTIONS_AND_PROPERTIES: Record = { OPTION_UNSPECIFIED: { displayName: 'None', description: '' }, // NOTE (joey) do we really not have a better way of doing this? OPTION_NX: { displayName: 'NX', description: 'Set expiry only when the key has no expiry.' }, OPTION_XX: { displayName: 'XX', description: 'Set expiry only when the key has an existing expiry' }, OPTION_GT: { displayName: 'GT', description: 'Set expiry only when the new expiry is greater than current one' }, OPTION_LT: { displayName: 'LT', description: 'Set expiry only when the new expiry is less than current one' } }; // NOTE: (joey) is there an order we want these to be in? // alphabetical vs most common vs a mix of both const ACTION_DROPDOWN: { label?: string; value: string; children: { label: string; value: string; description?: string }[] }[] = [ { value: 'raw', children: [{ label: 'Query', value: 'singleton', description: 'Send a raw Redis query.' }] }, { value: 'structured', children: [ { label: 'GET', value: 'get', description: 'Retrieve the value of a key.' }, { label: 'SET', value: 'set', description: 'Set the value of a key.' }, { label: 'DEL', value: 'del', description: 'Delete a key.' }, { label: 'KEYS', value: 'keys', description: 'List all keys matching a pattern.' }, { label: 'EXPIRE', value: 'expire', description: `Set a key's time to live in seconds.` }, { label: 'TTL', value: 'ttl', description: 'Get the remaining time to live of a key.' }, { label: 'MGET', value: 'mget', description: 'Retrieve values of multiple keys.' }, { label: 'HGET', value: 'hget', description: 'Get the value from a hash field.' }, { label: 'HMGET', value: 'hmget', description: 'Get the values of multiple hash fields.' }, { label: 'HMGETALL', value: 'hmgetall', description: 'Get all fields and values in a hash.' }, { label: 'HSET', value: 'hset', description: 'Set the value in a hash field.' }, { label: 'HSETNX', value: 'hsetnx', description: `Set the value in a hash field if it doesn't exist.` }, { label: 'HLEN', value: 'hlen', description: 'Get the number of fields in a hash.' }, { label: 'HDEL', value: 'hdel', description: 'Delete a hash field.' }, { label: 'HKEYS', value: 'hkeys', description: 'Get all field names in a hash.' }, { label: 'HVALS', value: 'hvals', description: 'Get all values in a hash.' }, { label: 'LINDEX', value: 'lindex', description: 'Get an element from a list by index.' }, { label: 'LLEN', value: 'llen', description: 'Get the length of a list.' }, { label: 'LPUSH', value: 'lpush', description: 'Add an element to a list.' }, { label: 'LREM', value: 'lrem', description: 'Remove an element from a list.' }, { label: 'LRANGE', value: 'lrange', description: 'Get the range of elements from a list.' }, { label: 'SADD', value: 'sadd', description: 'Add a member to a set.' }, { label: 'SCARD', value: 'scard', description: 'Get the number of members in a set.' }, { label: 'SMEMBERS', value: 'smembers', description: 'Get all members in a set.' }, { label: 'SISMEMBER', value: 'sismember', description: 'Check the membership of an item in a set.' }, { label: 'SRANDMEMBER', value: 'srandmember', description: 'Get a random member from a set.' }, { label: 'SREM', value: 'srem', description: 'Remove a member from a set.' }, { label: 'ZADD', value: 'zadd', description: 'Add a member to a sorted set.' }, { label: 'ZCARD', value: 'zcard', description: 'Get the number of members in a sorted set.' }, { label: 'ZCOUNT', value: 'zcount', description: 'Count the members in a score range in sorted set.' }, { label: 'ZRANGE', value: 'zrange', description: 'Get the members in a score range from sorted set.' }, { label: 'ZRANK', value: 'zrank', description: 'Get the index rank of a member in sorted set.' }, { label: 'ZREM', value: 'zrem', description: 'Remove a member from sorted set.' }, { label: 'ZSCORE', value: 'zscore', description: 'Get the score of a member in sorted set.' } ] } ]; const makeDropdownItem = (value: string, displayName?: string, subText?: string) => { return { key: value, value: value, displayName: displayName ?? value, subText: subText ?? '' }; }; // TODO(docs-v2): no redis page exists in docs-v2; add one and restore docsUrl export const RedisPlugin = generateRedisPlugin('redis', 'Redis', `${PUBLIC_INTEGRATIONS_LOGO_URL}/redis.png`); function generateRedisPlugin(id: string, name: string, iconLocation: string, docsUrl?: string): Plugin { return { id, name, moduleName: 'RedisPlugin', modulePath: 'plugins/redis/RedisPlugin', iconLocation, docsUrl, type: PluginType.API, responseType: PluginResponseType.JSON, hasRawRequest: false, datasourceTemplate: { sections: [ { name: 'main', items: [ { label: 'Display name', name: 'name', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, placeholder: `${name} Prod`, rules: [{ required: true, message: 'Display name is required' }] }, { label: 'Connection method', name: 'connection.connectionType.case', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.DROPDOWN, initialValue: 'url', rules: [{ required: true }], options: Object.entries(CONNECTION_METHODS_AND_DISPLAY_NAMES).map(([value, displayName]) => makeDropdownItem(value, displayName) ) }, { label: 'URI', name: 'connection.connectionType.value.urlString', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.PASSWORD, // since this can sometimes contain a password placeholder: `redis://[:password@]host[:port][/db-number]`, rules: [{ required: true, message: 'URI is required' }], display: { show: { 'connection.connectionType.case': ['url'] } } }, { label: 'Host', name: 'connection.connectionType.value.host', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, rules: [{ required: true, message: 'Host is required' }], display: { show: { 'connection.connectionType.case': ['fields'] } } }, { label: 'Port', name: 'connection.connectionType.value.port', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, initialValue: 6379, rules: [{ required: true, message: 'Port is required' }], display: { show: { 'connection.connectionType.case': ['fields'] } } }, { label: 'Database number', name: 'connection.connectionType.value.databaseNumber', startVersion: RedisPluginVersions.V1, dataType: InputDataType.NUMBER, componentType: FormComponentType.INPUT_TEXT, initialValue: 0, rules: [{ required: true, message: 'Database number is required' }], display: { show: { 'connection.connectionType.case': ['fields'] } } }, { label: 'Username (optional)', name: 'connection.connectionType.value.username', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, display: { show: { 'connection.connectionType.case': ['fields'] } } }, { label: 'Password (optional)', name: 'connection.connectionType.value.password', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.PASSWORD, display: { show: { 'connection.connectionType.case': ['fields'] } } } // TODO re-enable this when we allow user to provide certs // { // label: 'Enable SSL', // name: 'connection.connectionType.value.enableSsl', // startVersion: RedisPluginVersions.V1, // componentType: FormComponentType.CHECKBOX, // initialValue: true, // display: { // show: { // 'connection.connectionType.case': ['fields'] // } // } // } ] } ] }, actionTemplate: { sections: [ { name: 'main', items: [ { label: 'Action', name: 'commandType.value.action.case', secondaryName: 'commandType.case', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.ONEOF_ACTION_DROPDOWN, rules: [{ required: true }], initialValue: 'get', secondaryInitialValue: 'structured', actionOptions: ACTION_DROPDOWN }, // raw command { label: 'Query', name: 'commandType.value.action.value.query', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, placeholder: 'SET myKey 1', display: { show: { 'commandType.case': ['raw'] } }, rules: [{ required: true, message: 'Query is required' }] }, // structured commands { label: 'Key', name: 'commandType.value.action.value.key', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': [ 'get', 'set', 'del', 'expire', 'ttl', 'hget', 'hmget', 'hgetall', 'hset', 'hsetnx', 'hlen', 'hdel', 'hkeys', 'hvals', 'lindex', 'llen', 'lpush', 'lrem', 'lrange', 'sadd', 'scard', 'smembers', 'sismember', 'srandmember', 'srem', 'zadd', 'zcard', 'zcount', 'zrange', 'zrank', 'zrem', 'zscore' ] } }, rules: [{ required: true, message: 'Key is required' }] }, { label: 'Value', name: 'commandType.value.action.value.value', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['set', 'hset', 'hsentx', 'lpush', 'lrem', 'lrange'] } }, rules: [{ required: true, message: 'Value is required' }] }, { label: 'Expiration (ms) (optional)', name: 'commandType.value.action.value.expirationMs', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['set'] } } }, { label: 'Pattern', name: 'commandType.value.action.value.pattern', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['keys'] } }, rules: [{ required: true, message: 'Pattern is required' }] }, { label: 'Seconds', name: 'commandType.value.action.value.seconds', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['expire'] } }, rules: [{ required: true, message: 'Seconds is required' }] }, { label: 'Option', name: 'commandType.value.action.value.option', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.DROPDOWN, options: Object.entries(EXPIRE_OPTIONS_AND_PROPERTIES).map(([value, obj]) => makeDropdownItem(value, obj.displayName, obj.description) ), display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['expire'] } } }, { label: 'Keys', name: 'commandType.value.action.value.keys', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['mget'] } }, rules: [{ required: true, message: 'Keys are required' }], tooltip: { markdownText: 'A comma-separated list of keys' } }, { label: 'Field', name: 'commandType.value.action.value.field', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['hget', 'hset', 'hsetnx', 'hdel'] } }, rules: [{ required: true, message: 'Field is required' }] }, { label: 'Fields', name: 'commandType.value.action.value.fields', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['hmget'] } }, rules: [{ required: true, message: 'Fields are required' }], tooltip: { markdownText: 'A comma-separated list of keys' } }, { label: 'Index', name: 'commandType.value.action.value.index', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['lindex'] } }, rules: [{ required: true, message: 'Index is required' }] }, // NOTE: (joey) we need 2 "Count" items since it is required for lrem but not for srandmember { label: 'Count', name: 'commandType.value.action.value.count', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['lrem'] } }, rules: [{ required: true, message: 'Count is required' }] }, { label: 'Count (optional)', name: 'commandType.value.action.value.count', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['srandmember'] } } }, { label: 'Start', name: 'commandType.value.action.value.start', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['lrange'] } }, rules: [{ required: true, message: 'Start is required' }] }, { label: 'Member', name: 'commandType.value.action.value.member', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['sadd', 'sismember', 'srem', 'zadd', 'zrank', 'zrem', 'zscore'] } }, rules: [{ required: true, message: 'Member is required' }] }, { label: 'Score', name: 'commandType.value.action.value.score', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['zadd'] } }, rules: [{ required: true, message: 'Score is required' }] }, { label: 'Min', name: 'commandType.value.action.value.min', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['zcount'] } }, rules: [{ required: true, message: 'Min is required' }] }, { label: 'Max', name: 'commandType.value.action.value.max', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['zcount'] } }, rules: [{ required: true, message: 'Max is required' }] }, { label: 'Start', name: 'commandType.value.action.value.min', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['zrange'] } }, rules: [{ required: true, message: 'Start is required' }] }, { label: 'Stop', name: 'commandType.value.action.value.max', startVersion: RedisPluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, dataType: InputDataType.NUMBER, display: { show: { 'commandType.case': ['structured'], 'commandType.value.action.case': ['zrange'] } }, rules: [{ required: true, message: 'Stop is required' }] } ] } ] } }; }