/**
 * Copyright 2018 Twitter, Inc.
 * Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 */ import { addressToHostPort } from "../addressToHostPort" ; import * as Redis from "ioredis" ; import { Context } from "sqrl" ; import { invariant } from "sqrl-common" ; import { rateLimitFetchLua } from "../lua/rateLimitFetchLua" ; import { sessionizeLua } from "../lua/sessionizeLua" ; import { RedisInterface , RateLimitOptions } from "./RedisInterface" ; export class RedisService implements RedisInterface { private conn : any ; constructor ( address ? ) { const [ host , port ] = addressToHostPort ( address || "localhost" , 6379 ) ; this . conn = new Redis ( { host , port } ) ; this . conn . defineCommand ( "rateLimitFetch" , { lua : rateLimitFetchLua ( ) , numberOfKeys : 1 , } ) ; this . conn . defineCommand ( "sessionize" , { lua : sessionizeLua ( ) , numberOfKeys : 1 , } ) ; } async ping ( ctx ) { return this . conn . ping ( ) ; } async increment ( ctx , key , amount = null ) { return this . conn . incrby ( key , amount == null ? 1 : amount ) ; } async get ( ctx , key ) { return this . conn . get ( key ) ; } async del ( ctx , ... keys ) { return this . conn . del ( ... keys ) ; } async set ( ctx , key , value , mode ? ) { const args = mode ? [ mode ] : [ ] ; const rv = await this . conn . set ( key , value , ... args ) ; if ( rv === "OK" ) { return true ; } else if ( rv === null ) { return false ; } else { throw new Error ( "Unknown response from Redis set: " + rv ) ; } } async getList ( ctx , key ) { return this . conn . lrange ( key , 0 , - 1 ) ; } async listPush ( ctx , key , ... values ) { await this . conn . lpush ( key , ... values ) ; } async pfcount ( ctx , keys ) { return this . conn . pfcount ( ... keys ) ; } async pfadd ( ctx , key , values ) { await this . conn . pfaddBuffer ( key , ... values ) ; } async expire ( ctx , key , seconds ) { await this . conn . expire ( key , seconds ) ; } async mgetNumbers ( ctx , keys ) { const values = await this . conn . mget ( ... keys ) ; return values . map ( ( value ) => { if ( value === null ) { return value ; } const rv = parseInt ( value , 10 ) ; invariant ( ! isNaN ( rv ) , "Got invalid number in mgetNumbers" ) ; return rv ; } ) ; } async rateLimitFetch ( ctx , key , opt ) { return this . conn . rateLimitFetch ( key , opt . maxAmount , opt . take , opt . at , opt . refillTimeMs , opt . refillAmount , opt . strict ? 1 : 0 ) ; } async sessionize ( ctx , key , opt ) { return this . conn . sessionize ( key , opt . maxAmount , opt . take , opt . at , opt . refillTimeMs , opt . refillAmount ) ; } close ( ) { this . conn . disconnect ( ) ; } }