confirmBtn.addTarget(self, action: #selector(changePassword(email:currentPassword:newPassword:completion:)), for: .touchUpInside)
}
func changePassword(email: String, currentPassword: String, newPassword: String, completion: #escaping (Error?) -> Void) {
let credential = EmailAuthProvider.credential(withEmail: email, password: currentPassword)
Auth.auth().currentUser?.reauthenticate(with: credential, completion: { (result, error) in
if let error = error {
completion(error)
}
else {
Auth.auth().currentUser?.updatePassword(to: newPassword, completion: { (error) in
completion(error)
})
}
})
}
how Selector send parameter error is Argument of '#selector' refers to instance method 'changePassword(email:currentPassword:newPassword:completion:)' that is not exposed to Objective-C
i don't understand
Related
I try to use RSocketRequester to send a message from the server to the specific client, but I don't know how to handle it on the frontend. The server is Spring Webflux with the controller like this:
data class Message(val message: String)
#Controller
class RSocketController {
private val log = LoggerFactory.getLogger(RSocketController::class.java)
#MessageMapping("say.hello")
fun sayHello(message: String): Flux<Message> {
log.info("say hello {}", message)
return Flux.just(Message("server says hello"))
}
#MessageMapping("say.hi")
fun sayHi(message: String, rSocketRequester: RSocketRequester): Flux<Message> {
log.info("say hi {}", message)
rSocketRequester
.route("say.hello")
.data(Message("server says hi hello ;)"))
.send()
.subscribe()
return Flux.just(Message("server says hi!!"))
}
}
On the frontend I use rsocket-js. The sayHello method works just fine (request-stream), but when I call the sayHi method I want to send two messages from the server. The first one to say.hello endpoint, and the second to say.hi endpoint. I've got rsocket-js implementation like this:
sayHello() {
console.log("say hello");
this.requestStream("say.hello");
},
sayHi() {
console.log("say hi");
this.requestStream("say.hi");
},
connect() {
const transport = new RSocketWebSocketClient({
url: "ws://localhost:8080/rsocket"
});
const client = new RSocketClient({
serializers: {
data: JsonSerializer,
metadata: IdentitySerializer
},
setup: {
keepAlive: 60000,
lifetime: 180000,
dataMimeType: "application/json",
metadataMimeType: "message/x.rsocket.routing.v0"
},
transport
});
client.connect().subscribe({
onComplete: socket => {
this.socket = socket;
console.log("complete connection");
},
onError: error => {
console.log("got connection error");
console.error(error);
},
onSubscribe: cancel => {
console.log("subscribe connection");
console.log(cancel);
}
});
},
requestStream(url) {
if (this.socket) {
this.socket
.requestStream({
data: url + " from client",
metadata: String.fromCharCode(url.length) + url
})
.subscribe({
onComplete: () => console.log("requestStream done"),
onError: error => {
console.log("got error with requestStream");
console.error(error);
},
onNext: value => {
// console.log("got next value in requestStream..");
console.log("got data from sever");
console.log(value.data);
},
// Nothing happens until `request(n)` is called
onSubscribe: sub => {
console.log("subscribe request Stream!");
sub.request(2147483647);
// sub.request(3);
}
});
} else {
console.log("not connected...");
}
}
I can see both messages in Google Chrome DevTools -> Network -> rsocket. So the client receives them but I can't catch in the code the one sent by RSocketRequester.
It seems that the server uses fireAndForget method. How to handle it on the client side?
As #VladMamaev said, we can provide a responder to the client like in this example https://github.com/rsocket/rsocket-js/blob/master/packages/rsocket-examples/src/LeaseClientExample.js#L104
For me, fireAndForget method is enough.
export class EchoResponder {
constructor(callback) {
this.callback = callback;
}
fireAndForget(payload) {
this.callback(payload);
}
}
import { EchoResponder } from "~/assets/EchoResponder";
...
const messageReceiver = payload => {
//do what you want to do with received message
console.log(payload)
};
const responder = new EchoResponder(messageReceiver);
connect() {
const transport = new RSocketWebSocketClient({
url: "ws://localhost:8080/rsocket"
});
const client = new RSocketClient({
serializers: {
data: JsonSerializer,
metadata: IdentitySerializer
},
setup: {
keepAlive: 60000,
lifetime: 180000,
dataMimeType: "application/json",
metadataMimeType: "message/x.rsocket.routing.v0"
},
responder: responder,
transport
});
I am trying to implement expressjs like features to httprouter package .
I create a struct type mounter
type Mounter struct {
BasePath string
Routes []*Route
}
and a Route struct which represents subRoutes
type Route struct {
Path string
Method string
Func Handle
}
type Handle func(http.ResponseWriter, *http.Request, Params)
type Params interface{}
i have a NewRoutes Function which is the main thing i wanted to port from expressjs new routes does the same thing as express.Router
func NewRoutes(base string) (mounter *Mounter) {
mounter = &Mounter{
BasePath: base,
}
return
}
and i have get post put delete methods under *Mounter
//GET request handler
func (mounter *Mounter) GET(path string, Func Handle) {
mounter.Routes = append(mounter.Routes, &Route{path, "get", Func})
}
//POST request handler
func (mounter *Mounter) POST(path string, Func Handle) {
mounter.Routes = append(mounter.Routes, &Route{path, "post", Func})
}
//PUT request handler
func (mounter *Mounter) PUT(path string, Func Handle) {
mounter.Routes = append(mounter.Routes, &Route{path, "put", Func})
}
//DELETE request handler
func (mounter *Mounter) DELETE(path string, Func Handle) {
mounter.Routes = append(mounter.Routes, &Route{path, "delete", Func})
}
and finally i have a Mount method which mounts the router to the actual router
func (mounter *Mounter) Mount(router *rtr.Router) {
mounter.BasePath = strings.TrimSuffix(mounter.BasePath, "/")
for _, route := range mounter.Routes {
path := route.Path
if !strings.HasSuffix(path, "/") {
path += "/"
}
path = mounter.BasePath + path
switch strings.ToLower(route.Method) {
case "get":
router.GET(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
route.Func(res, req, params)
})
case "post":
router.POST(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
route.Func(res, req, params)
})
case "delete":
router.DELETE(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
route.Func(res, req, params)
})
case "put":
router.PUT(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
route.Func(res, req, params)
})
}
}
}
everything works pretty nice and the methods are working fine too if i try to send a post request to a get endpoint it gives a nice 404 but the only issue is it always responds with the handler of last added member regardless of subpath so
package api
var ApiRouter = express.NewRoutes("/api/")
func init() {
ApiRouter.GET("/", func(res http.ResponseWriter, req *http.Request, _ express.Params) {
fmt.Fprintln(res, "testget/")
})
ApiRouter.GET("/pt", func(res http.ResponseWriter, req *http.Request, _ express.Params) {
fmt.Fprintln(res, "pt")
})
ApiRouter.POST("/test", func(res http.ResponseWriter, req *http.Request, _ express.Params) {
fmt.Fprintln(res, "test/post")
})
}
package main
func main() {
router := express.New()
api.ApiRouter.Mount(router)
for _, route := range api.ApiRouter.Routes {
fmt.Println(*route)
}
router.ServeFiles("/public/*filepath", http.Dir("./public/"))
http.ListenAndServe(":1024", router)
}
Will always respond test/post and the output of the range i am doing above for test purposes is
So do you have any idea why it uses always the same function to respond but recognizes paths perfectly?
The problem lies in Mount method, which is known as Iteration Variables and Closures. Declare a new variable for capturing route e.g.
thisRoute := route //capture iteration variable
switch strings.ToLower(route.Method) {
case "get":
router.GET(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
thisRoute.Func(res, req, params)
})
case "post":
router.POST(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
thisRoute.Func(res, req, params)
})
//...
}
I had this little extension on Alamofire 3, it was used to get certain tokens and data from the default response.
public extension Alamofire.Request {
private func authorizationHandler(queue: dispatch_queue_t? = nil, completionHandler: (NSURLRequest, NSHTTPURLResponse?, NSData?, NSError?) -> Void) -> Self {
return response { (req, res, data, error) in
if let headers = res?.allHeaderFields {
if let id = headers["x-uid"] as? String {
oHTTPEnvironment.x_uid = id
}
if let authToken = headers["x-access-token"] as? String {
oHTTPEnvironment.currentMutableToken = oHTTPEnvironment.nextMutableToken
oHTTPEnvironment.nextMutableToken = authToken
}
}
dispatch_async(queue ?? dispatch_get_main_queue(), {
completionHandler(req!, res, data, error)
})
}
}
}
And now in Alamofire 4 and Swift 3 it get's an error on the "return response { (req, res, data, error) in"
The error is:
Cannot call value of non-function type 'HTTPURLResponse'
I even tried to do:
private func authorizationHandler(queue: DispatchQueue? = nil, completionHandler: (NSURLRequest, HTTPURLResponse?, NSData?, NSError?) -> Void) -> Self {
return response { resp in
debugPrint(resp)
}
}
But it gives the same error.
I have created angular service, where I'm registering challengeHandler this way:
azureChallengeHandler = WL.Client.createChallengeHandler(realm);
azureChallengeHandler.isCustomResponse = function (response) {
...
};
azureChallengeHandler.handleChallenge = function (response) {
...
};
So i'm logging in with this function:
WL.Client.login(realm, options)
And the first time it works ok, isCustomResponse gets called, returns "true", then handleChallenge gets called.
But after logging out with this function:
WL.Client.logout(realm, options)
When I try to login again, isCustomResponse gets called and still returns "true", but handleChallenge is not firing.
How can I fix that?
After calling WL.Client.reloadApp() or reloading app itself I can login again, but it's not a suitable solution.
UPDATE:
Here is adapter code:
function onAuthRequired(headers) {
return customLoginResponse(true, false, false);
}
function customLoginResponse(authRequired, azureTokenRequired, wrongTenant) {
return {
authRequired: authRequired,
azureTokenRequired: azureTokenRequired,
realm: 'AzureAuth',
wrongTenant: wrongTenant
};
}
function onLogout(){
WL.Server.setActiveUser("AzureAuth", null);
WL.Logger.debug("Logged out");
}
function submitLogout(uuid, orgId, ssogroup){
WL.Server.invokeProcedure({
adapter: "AzureTokenSqlAdapter",
procedure: "removeRefreshToken",
parameters: [uuid, orgId, ssogroup]
});
onLogout();
}
function submitLogin(uuid, orgId, ssogroup, code) {
var tokenObject = getTokens(code);
if (tokenObject.id_token) {
var jwtParsed = parseJWT(tokenObject.id_token);
var tenantId = jwtParsed.tid;
var invocationResult = WL.Server.invokeProcedure({
adapter: "AzureTokenSqlAdapter",
procedure: "checkTenant",
parameters: [orgId, tenantId]
});
if (!invocationResult.tenantRegistered) {
return customLoginResponse(true, true, true);
}
}
return authUser(tokenObject, uuid, orgId, ssogroup);
}
And here is the client code:
function azureAuthService($q, _, $state) {
var loginPromise;
azureChallengeHandler = WL.Client.createChallengeHandler(realm);
//first response after protected call
azureChallengeHandler.isCustomResponse = function (response) {
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
return response.responseJSON.realm == realm;
};
//when isCustomResponse returns true
azureChallengeHandler.handleChallenge = function (response) {
WL.Logger.debug("challenge handler -- handleChallenge");
var authRequired = response.responseJSON.authRequired;
var azureTokenRequired = response.responseJSON.azureTokenRequired;
var wrongTenant = response.responseJSON.wrongTenant;
if (wrongTenant) {
loginPromise.reject('wrong tenant');
} else if (authRequired && azureTokenRequired) {
fullLogin();
} else if (authRequired) {
fastLogin();
} else {
loginPromise.resolve();
}
};
azureChallengeHandler.handleFailure = function (error) {
console.log('failure');
console.log(error);
};
return {
init: init,
login: login,
logout: logout
};
function init(config) {
ssogroup = config.ssogroup;
orgId = config.orgId;
}
function login() {
loginPromise = $q.defer();
WL.Client.login(realm, {
onSuccess: function(info) {
loginPromise.resolve();
},
onFailure: function(error) {
loginPromise.reject();
}
});
return loginPromise.promise;
}
function logout() {
var logoutPromise = $q.defer();
var invocationData = {
adapter : 'AzureAuth',
procedure : 'submitLogout',
parameters : [device.uuid, orgId, ssogroup]
};
WL.Client.invokeProcedure(invocationData).then(function () {
WL.Client.logout(realm, {
onSuccess: function () {
logoutPromise.resolve();
},
onFailure: function () {
logoutPromise.reject();
}
});
}, function () {
logoutPromise.reject();
});
return logoutPromise.promise;
}
}
fastLogin and fullLogin is functions that perform some work and finally call
var options = {
parameters: [device.uuid, orgId, ssogroup, transitionAuthObject.requestToken],
adapter: "AzureAuth",
procedure: "submitLogin"
};
azureChallengeHandler.submitAdapterAuthentication(options);
Can't see your fullLogin() and fastLogin() methods so it's hard to say for sure. Make sure that you're calling challengeHandler's submitSuccess() or submitFailure() methods after successful/failed authentication. The authentication framework keeps a queue of requests/responses that require authentication. After successful/failed authentication you need to invoke submitSuccess/submitFailure on challenge handler in order for authentication framework to remove your requests from queue and process it. In case you're not doing so the request remains in the queue and once you're sending a new request that triggers authentication it is put into queue but not handled since there's another request already waiting for authentication.
//Below is my callback method which returns some response code.Now the thing is that i need to navigate to different view from the callback method.This logic i am using in userlogin. Provide me some solution that i can navigate to different view below i declared some code which i used to navigate to different view which works fine outside callback method not inside callback method.
Ext.data.JsonP.request({
url:'url',
method: 'POST',
callbackkey: 'callback',
params: {
userID: user_Id,
password: password,
format: 'json'
},
callback: function (response, value, request) {
//Logic should come here
}
else
{
}
},
failure: function (response, request) {
}
});
enter code here
//Below is the cofig entry
config: {
refs: {
homepage:'HP'
}
}
//I am adding below code in success block but getting error
var noteEditor = this.getHomepage();
Ext.Viewport.animateActiveItem(noteEditor, this.slideLeftTransition);
assuming you doing something like
Ext.data.JsonP.request({
url:'url',
method: 'POST',
callbackkey: 'callback',
params: {
userID: user_Id,
password: password,
format: 'json'
},
scope: this, /// fix handler scope
callback: function (response, value, request) {
if () {
//Logic should come here
var noteEditor = this.getHomepage();
Ext.Viewport.animateActiveItem(noteEditor, this.slideLeftTransition);
}
else
{
}
},
failure: function (response, request) {
}
});
you have to add a scope: this property to the ajax call.
Cheers, Oleg