I have this simple method which listens for Redis Pub/Sub message and emits Server sent event but for some reason it is not working. This is what I have.
#GetMapping
fun getNotifications(#RequestParam(name = "token", required = true) token: String): Flux<ServerSentEvent<Notification>> {
if (token.trim().isEmpty()) {
return Flux.error(ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid token"))
}
val claims = jwtService.getAllClaimsFromToken(token) ?: return Flux.error(ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid token"))
val userName = jwtService.getUsernameFromToken(claims) ?: return Flux.error(ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid token"))
return userRepository.findOneByEmail(userName)
.filter { user -> checkAccess(user) }
.thenMany<ServerSentEvent<Notification>> {
notificationService.subscribe()
.map { ServerSentEvent.builder<Notification>()
.event("notification")
.data(it)
.build()
}.asFlux()
}.switchIfEmpty {
Flux.error<ResponseStatusException>(ResponseStatusException(HttpStatus.UNAUTHORIZED, "Token expired"))
}
}
private fun checkAccess(user: User): Boolean {
if (!user.enabled || !user.account.enabled) {
return false
}
/// More checking... logic
return true
}
Is there anything that I am missing?
Related
I am trying to save oauth login credentials in redis . Using spring oauth2 library, and spring redis.
My saveAuthorizedClient method should be saving the key/val pair in redis, but nothing is being saved. Have attached a debugger and the key value pairs are correctly assigned, its the reactiveOauthRedisTemplate.opsForValue().set(key, authorizedClient) which seems to be not doing anything.
class CustomClientService(
val reactiveClientRegistrationRepository: ReactiveClientRegistrationRepository,
val reactiveOauthRedisTemplate: ReactiveRedisTemplate<String, OAuth2AuthorizedClient>
) : ReactiveOAuth2AuthorizedClientService {
override fun <T : OAuth2AuthorizedClient> loadAuthorizedClient(clientRegistrationId: String, principalName: String): Mono<T> {
Assert.hasText(clientRegistrationId, "clientRegistrationId cannot be empty")
Assert.hasText(principalName, "principalName cannot be empty")
return clientRegistrationRepository.findByRegistrationId(clientRegistrationId)
.map { rediskey(clientRegistrationId, principalName) }
.flatMap { key: String ->
reactiveOauthRedisTemplate.opsForValue().get(key) as Mono<T>
}
}
override fun saveAuthorizedClient(authorizedClient: OAuth2AuthorizedClient, principal: Authentication): Mono<Void> {
val key = rediskey(authorizedClient.clientRegistration.registrationId, principal.name)
reactiveOauthRedisTemplate.opsForValue().set(key, authorizedClient)
.let {
val x = reactiveOauthRedisTemplate.opsForValue().get(key)
println("my redis data is $x")
x.map {
println("my data value is $it")
val token = x as OAuth2AuthorizedClient
}
}
return Mono.empty()
}
private fun rediskey(clientRegistrationId: String, principalName: String): String {
return "something..."
}
}
Here's my redis config
#Configuration
class RedisConfig {
#Bean
fun reactiveOauthRedisTemplate(reactiveRedisConnectionFactory: ReactiveRedisConnectionFactory,
resourceLoader: ResourceLoader): ReactiveRedisTemplate<String, OAuth2AuthorizedClient> {
val keySerializer: RedisSerializer<String> = StringRedisSerializer()
val defaultSerializer = JdkSerializationRedisSerializer(resourceLoader.classLoader)
val serializationContext = RedisSerializationContext
.newSerializationContext<String, OAuth2AuthorizedClient>(defaultSerializer).key(keySerializer).hashKey(keySerializer)
.build()
return ReactiveRedisTemplate(reactiveRedisConnectionFactory, serializationContext)
}
}
redis yaml config
spring:
redis:
host: localhost
From debugger , the redis connect has host and port correct. Any help here is appreciated.
Here's my redis client object from debugger
redisURI = {RedisURI#12018} "redis://127.0.0.1"
host = "127.0.0.1"
socket = null
sentinelMasterId = null
port = 6379
database = 0
clientName = null
username = null
password = null
ssl = false
verifyMode = {SslVerifyMode#12033} "FULL"
startTls = false
timeout = {Duration#12010} "PT1M"
sentinels = {ArrayList#12034} size = 0
Your usage of react client is not correct, you should not return Mono.empty() from saveAuthorizedClient, you should return Mono<Boolean> and subscribe to it. You can ignore the return value if you do not need the result, but you must subscribe it so that it will execute the business logic.
We have both asynchronous and synchronous calls implemented using retrofit and Either to map success/error. After adding the network interceptor asynchronous calls are returning bad responses(works fine on postman). I have tried adding a general error JSON response thinking Either is not able to catch the exceptions but still no luck. please suggest a fix or new approach
Interceptorclass -
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
try {
val response = chain.proceed(request)
val bodyString = response.body!!.string()
return response.newBuilder()
.body(ResponseBody.create(response.body?.contentType(), bodyString))
.build()
} catch (e: Exception) {
e.printStackTrace()
var msg = ""
when (e) {
is SocketTimeoutException -> {
msg = "Timeout - Please check your internet connection"
}
is UnknownHostException -> {
msg = "Unable to make a connection. Please check your internet"
}
is ConnectionShutdownException -> {
msg = "Connection shutdown. Please check your internet"
}
is IOException -> {
msg = "Server is unreachable, please try again later."
}
is IllegalStateException -> {
msg = "${e.message}"
}
else -> {
msg = "${e.message}"
}
}
return Response.Builder()
.request(request)
.protocol(Protocol.HTTP_1_1)
.code(999)
.message(msg)
.body(ResponseBody.create(null, "{${e}}")).build()
}
}`
client - val client1 = OkHttpClient.Builder() .addInterceptor(Interceptor) .build()
ServiceConfig.kt - Adding client as below
#Singleton
#Provides
#BaseUrl(BaseUrlType.SERVICES)
fun provideSupportRetrofit(
jsonConverters: Converter.Factory,
#HttpClient(ClientType.OAUTH) client: Call.Factory
): ServicesFactory {
return fakeServicesFactory ?: Retrofit.Builder()
.callFactory(client)
.baseUrl(baseUrlServices)
.addCallAdapterFactory(EitherCallAdapterFactory())
.addConverterFactory(EitherConverterFactory())
.addConverterFactory(FiberErrorConverterFactory())
.addConverterFactory(jsonConverters)
.addConverterFactory(primitiveTypeConverters)
.client(client1)
.build()
.asFactory
}
EitherCovertor.kt -
`override fun enqueue(callback: Callback<Either<*, *>>) {
call.enqueue(object : Callback<Either<*, *>> {
override fun onResponse(call: Call<Either<*, *>>, response: Response<Either<*, *>>) {
callback.onResponse(this#EitherCall, response.asEither)
}
}
override fun onFailure(call: Call<Either<*, *>>, t: Throwable) {
when (t) {
is Error -> {
Timber.e("Failure Error from API")
callback.onFailure(call, t)
}
else -> callback.onResponse(this#EitherCall, t.asEither)
}
}
})
}`
To get the error text in the header section
headers["Accept"] = "application/json"
and process the error in json form, this way worked for me
Response.ErrorListener { error: VolleyError? ->
if (error is TimeoutError || error is NoConnectionError) {
Constants.checkInternet(context)
} else if (error is ServerError) {
val responseBody = String(error.networkResponse.data, Charsets.UTF_8)
val errorMsg: String =
JSONObject(responseBody).getJSONObject(TAG_META).getJSONObject(
TAG_STATUS)
.getString(TAG_MESSAGE)
Toast.makeText(context, errorMsg + "", Toast.LENGTH_SHORT)
.show()
Log.e("responseBody", responseBody)
Log.e("errorMsg", errorMsg)
}
Loading.hide(loading)
}
I want to show errors messages that come from my API response but every time onError() function is called inside RXJava It shows built-in Errors something Like time exception, network error, etc. please tell me how can I Show Api Response messages Like message.type , message.text these are my error messages. thanks
here is Api Response
{
"code": 400,
"status": "Bad Request",
"message": {
"type": "WARNING",
"text": "User was not found"
},
"data": {
"generatedMessage": false,
"code": "ERR_ASSERTION",
"actual": null,
"expected": true,
"operator": "=="
}
}
Here is api call
#POST("api/auth/login")
fun login(#Body loginModel: loginModel): Observable<LoginResponseModel>
Here is Base URl Code
private val CLIENT_URL = "http://rmms.dummy.org:3001"
private val client = OkHttpClient.Builder().build()
private val retrofit = Retrofit.Builder()
.baseUrl(CLIENT_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build()
fun<T> buildService(service: Class<T>): T{
return retrofit.create(service)
}
Here is ViewModel
class LoginViewModel : ViewModel() {
var disposable = CompositeDisposable()
var serviceBuilder = ServiceBuilder.buildService(ApiServices::class.java)
var loginResponse: MutableLiveData<LoginResponseModel> = MutableLiveData()
fun refresh(requirContext: Context, email: String, password: String) {
login(requirContext, email, password)
}
fun login(requirContext: Context, email: String, password: String) {
var login = loginModel(email, password)
disposable.add(
serviceBuilder.login(login)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.subscribeWith(object : DisposableSingleObserver<LoginResponseModel>(),
io.reactivex.Observer<LoginResponseModel> {
override fun onSuccess(value: LoginResponseModel?) {
}
override fun onError(e: Throwable?) {
if (e is retrofit2.HttpException) {
AlertMessageDialog.genaricAlertDialog(requirContext, "WARNING", "Invalid Credentials Provided"
)
} else if (e is SocketTimeoutException) {
Log.e("time exception******>", e.message.toString())
} else if (e is IOException) {
Log.e("network error******>", e.message.toString())
} else {
Log.e("unknown error******>", e?.message.toString())
}
}
override fun onNext(value: LoginResponseModel?) {
value?.let {
if (value.code == 200) {
}else{
AlertMessageDialog.genaricAlertDialog(requirContext,"${value.message.type}","${value.message.text}")
}
}
}
override fun onComplete() {
}
})
)
}
override fun onCleared() {
super.onCleared()
disposable.clear()
}
}
I have a stream:
val symbols: Single<List<Symbol>>
Now I want to transform the stream into a UI State with map():
private fun cool(): Single<SymbolContract.State> =
symbols.map { SymbolContract.State.Symbols(it) }
What I want to do is catch an error on the upstream symbols single, so that I can catch any errors and then return SymbolContract.State.GeneralError().
I want something like a onErrorMap() or something. Unfortunately, putting onErrorResumeItem on symbols doesn't work because it needs to return a List<Symbol>.
I can think of a few ugly ways to do this, but what's the cleanest?
I suggest you to use global handling error. I give you a sample so you can get the idea. (It is kotlin) and you can catch as many as exception you would like, some of them are my custom exceptions. Just bear in mind, this sample is about Reactive Webflux but you get the idea. It would be similar in others
#Configuration
class ExceptionTranslator {
#Bean
#Order(-1)
fun handle(objectMapper: ObjectMapper): ErrorWebExceptionHandler {
return ErrorWebExceptionHandler { exchange, ex ->
if (exchange.response.isCommitted) {
return#ErrorWebExceptionHandler Mono.error(ex)
}
val response = exchange.response
response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR
response.headers.contentType = MediaType.APPLICATION_PROBLEM_JSON_UTF8
val url: String
var message = ex.message
var params = mutableMapOf<String, Any>()
when (ex) {
is ParametricException -> {
url = ex.url
params = ex.params
}
is BaseException -> {
url = ex.url
}
is BadCredentialsException -> {
url = INVALID_CREDENTIAL_TYPE
message = ex.message ?: "Wrong Credentials"
}
is ConcurrencyFailureException -> {
url = INTERNAL_TYPE
message = ERR_CONCURRENCY_FAILURE
}
is MethodArgumentNotValidException -> {
val result = ex.bindingResult
val fieldErrors = result.fieldErrors.map {
FieldErrorVM(it.objectName, it.field, it.code ?: "Unknown")
}
url = CONSTRAINT_VIOLATION_TYPE
message = ERR_VALIDATION
params = Collections.singletonMap("errors", fieldErrors)
}
else -> url = INTERNAL_TYPE
}
if (ex is BaseException) {
response.statusCode = HttpStatus.valueOf(ex.status.code())
}
val bytes = objectMapper.writeValueAsBytes(ProblemVM(url, message ?: "Internal Error", params))
val buffer = response.bufferFactory().wrap(bytes)
response.writeWith(Mono.just(buffer))
}
}
}
Found a clean answer:
private fun symbols(): Single<SymbolContract.State> =
symbols.map<SymbolContract.State> { SymbolContract.State.Symbols(it) }
.onErrorReturnItem(SymbolContract.State.Error(SymbolContract.State.Error.Type.Network))
The onErrorReturnItem has to come after the map, and the map needs explicit type parameters.
When registering in the application user gets 2 tokens. Access (lives 1 day) and Refresh (lives 6 months). At a certain point, the Access token will come-one day there will be a custom error. At this , we need to call the refreshToken method and the updated , with which the work will go on.
We call the method, for example getdata , checking for errors, if custom error refreshToken we keep both tokens getdata already with the updated token.
i try but how to rerty call method getdata after refresh token?
mAllApi.getData(new Request().getRequestData())
.flatMap(response -> {
if (response.getError().equals(ECode.ERROR_TOKEN.getCode())) {
mAllApi.getRefreshToken(new String()).flatMap(new Function<AccessToken, ObservableSource<AccessToken>>() {
#Override
public ObservableSource<AccessToken> apply(AccessToken accessToken) throws Exception {
AccessTokenManager.saveNewAccessToken(accessToken);
return null;
}
});
} else {
return Observable.just(response);
}
});
What we did in our app - we created custom OkHttp Interceptor which checks for Access Token each time we do Auth Request and if it's corrupted Interceptor change it with RefreshToken, Add new updated value to Authorization Header and retries Request.
Here is example in Kotlin:
class RefreshAccessTokenInterceptor
#Inject constructor() : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(retryRequest(chain))
return if (response.noAuthError()) {
response
} else {
updateIfNeededOrProcessWithNewToken(chain)
}
}
private fun retryRequest(chain: Interceptor.Chain): Request {
val builder = chain.request().newBuilder()
addAuthHeaders(builder)
return builder.build()
}
private fun Response.noAuthError() = code() != HttpErrorChecker.HTTP_AUTHENTICATION_TIMEOUT
private fun addAuthHeaders(builder: Request.Builder) {
val accessToken = getAccessToken()
if (!accessToken.isNullOrEmpty()) {
builder.header("Authorization", "Bearer $accessToken")
}
}
private fun updateIfNeededOrProcessWithNewToken(chain: Interceptor.Chain): Response {
//here you update your token, add new header and retries request
return chain.proceed(retryRequest(chain))
}
}