Validator not workind in Adonis Resorceful route - adonis.js

I'm following the docs, and trying many things but without success validating resource routes. My code:
routes.js:
Route.resource('/user', 'UserController').validator(
new Map([
[['user.store'],[ 'UserStore']]
])
) .apiOnly();
app/Validators/UserStore.js:
class UserStore {
get rules() {
return {
email: 'required|email|unique:users,email',
password: 'required',
name: 'required|min:30',
tiago: 'required|min:30'
};
}
get validateAll() {
return true;
}
get messages() {
return {
'email.required': 'You must provide a email address.',
'email.email': 'You must provide a valid email address.',
'password.required': 'You must provide password.',
'tiago.required': 'You must provide password.'
};
}
}
module.exports = UserStore;
The validation never happened this way. 😭
🧪 If I change to an individual route, it validates, it works:
Route.post('/user', 'UserController.store').validator('UserStore'); //it works
But I want to use the resource to write less and have a clear code.
How can I use validator with resource routes?

Frustrating way to solve it...
Just removing / on route, and it worked:
Route.resource('user', 'UserController').validator(
new Map([
[['user.store'],[ 'UserStore']]
])
) .apiOnly();

Related

How do I handle Request Body Error in Ktor

I am new to Ktor and I have a route with a request body which i am parsing with Kotlin Serialization.
I know that the request body is expected to conform to the request body data class but then, I tested by passing the wrong field in my test payload and it crashed the app.
I want to be able to handle such scenarios and respond to the client that such a field is not allowed. How do i go about that.
This is my sample data class:
#kotlinx.serialization.Serializable
data class UserLoginDetails(
var email: String = "",
var password: String = ""
)
This is the route:
post("/user/login") {
val userInfo = call.receive<UserLoginDetails>()
//my code here
}
The payload below works
{
"email": "test#email.com",
"password": "password"
}
But if use an alternative payload for instance:
{
"phone": "test#email.com",
"password": "password"
}
The app crashes with the crash message:
kotlinx.serialization.json.internal.JsonDecodingException: Unexpected
JSON token at offset 7: Encountered an unknown key 'emai'. Use
'ignoreUnknownKeys = true' in 'Json {}' builder to ignore unknown
keys.
You have two options in Ktor:
call.receive is the function which can throw an exception in this case, you can simply catch it:
try {
val userInfo = call.receive<UserLoginDetails>()
} catch (t: Throwable) {
// handle error
}
Catching exceptions globally using Status Pages:
install(StatusPages) {
exception<SerializationException> { cause ->
call.respond(/* */)
}
}
The error message says you can setup your Json with the config ignoreUnknownKeys = true where you are creating the Json object. Here's the doc link.
a tip: You might also want to check other configuration options so you can avoid setting empty string as default values.

AWS Amplify Auth Errors

I'm using the Android Amplify library. I am having trouble finding out what kind of error would be passed back from the Amplify.Auth.signIn() function. I'm not finding the documentation for this anywhere. Right now I am just kind of guessing as to what it will return. What I want is to tell the user how to recover from the error. Does the username not exist, was the password incorrect, was it of bad format, etc. Reading the source code I am given the impression that AmplifyException.recoveryMessage is what I want but that would still be problematic as it doesn't allow me to customize the message.
/**
* Sign in the user to the back-end service and set the currentUser for this application
* #param username User's username
* #param password User's password
*/
override fun initiateSignin(username : String, password : String) {
//Sign in the user to the AWS back-end
Amplify.Auth.signIn(
username,
password,
{result ->
if (result.isSignInComplete) {
Timber.tag(TAG).i("Sign in successful.")
//Load the user if the sign in was successful
loadUser()
} else {
Timber.tag(TAG).i("Sign in unsuccessful.")
//TODO: I think this will happen if the password is incorrect?
}
},
{error ->
Timber.tag(UserLogin.TAG).e(error.toString())
authenticationRecoveryMessage.value = error.recoverySuggestion
}
)
}
Authentication recovery message is LiveData that I want to update a snackbar which will tell the user what they need to do for a successful login. I feel there must be some way to get the error from this that I just haven't figured out yet. The ideal way to handle messages to the user is with XML strings for translation possibilities so I would really like to use my own strings in the snackbar but I need to know the things that can go wrong with sign-up and what is being communicated to me through the error -> {} callback.
I couldn't find them in the documentation myself, so i decided to log the possibles cases.
try {
const signInResult = await Auth.signIn({
username: emailOrPhoneNumber,
password
});
const userId = signInResult.attributes.sub;
const token = (await Auth.currentSession()).getAccessToken().getJwtToken();
console.log(userId, 'token: ', token);
resolve(new AuthSession(userId, token, false));
} catch (e) {
switch (e.message) {
case 'Username should be either an email or a phone number.':
reject(`${AuthError.usernameInvalid}: ${e.message}`);
break;
case 'Password did not conform with policy: Password not long enough':
reject(`${AuthError.passwordTooShort}: ${e.message}`);
break;
case 'User is not confirmed.':
reject(`${AuthError.userIsNotConfirmed}: ${e.message}`);
break;
case 'Incorrect username or password.':
reject(`${AuthError.incorrectUsernameOrPassword}: ${e.message}`);
break;
case 'User does not exist.':
reject(`${AuthError.userDoesNotExist}: ${e.message}`);
break;
default:
reject(`${AuthError.unknownError}: ${e.message}`);
}
}
SignIn uses Cognito's InitiateAuth under the hood, so error codes can be found here:
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html#API_InitiateAuth_Errors
They are available in the code field of the error.

validatesLengthOf not working on password field due to hashing

I am trying to validate the password when a user is registered, but the validation is not done on the plain text but hashed value. How do i fix this?
My user model is client:
module.exports = function(client) {
client.validatesLengthOf('password', {min: 20})
};
Validations are for a model itself. I mean it affects on operation hooks not remote hooks.
You need to create a remote hook like this :
client.beforeRemote('create', function(ctx, instance, next){
if(ctx.args.data.password.length < 20){
return next(PsswordValidationError);
/* assuming you have this error object
or return any error validation you want */
}
next();
});

Unit tests on methods/publication that requires authentication in meteor

I'm writing unit test for my Meteor 1.4.2 application, where few of my methods requires authentication before processing.
How should I test these methods?
So far, I've written a test with practicalmeteor:mocha to create a new user and login with that user.
describe('login method', function () {
let logingKey;
beforeEach(function () {
Meteor.users.remove({});
const createUser = Meteor.server.method_handlers['registerUser'];
let params = {
username: 'testUsername'
}
res = createUser.apply({}, [params]);
logingKey = res.key;
});
it('can provide authentication', function () {
const loginUser = Meteor.server.method_handlers['login'];
let params = {
key: logingKey
}
console.log(params);
loginUser.apply({}, [params]);
});
I've written a custom login handler to login with the generated key which works fine with application, but in test results I'm getting following error.
Error: Cannot read property 'id' of undefined
at AccountsServer.Ap._setLoginToken (packages/accounts-base/accounts_server.js:889:35)
at packages/accounts-base/accounts_server.js:288:10
at Object.Meteor._noYieldsAllowed (packages/meteor.js:671:12)
at AccountsServer.Ap._loginUser (packages/accounts-base/accounts_server.js:287:10)
at AccountsServer.Ap._attemptLogin (packages/accounts-base/accounts_server.js:349:12)
at Object.methods.login (packages/accounts-base/accounts_server.js:533:21)
at Object.methodMap.(anonymous function) (packages/meteorhacks_kadira.js:2731:30)
at Test.<anonymous> (imports/api/methods/loginUser.tests.js:30:17)
at run (packages/practicalmeteor:mocha-core/server.js:34:29)
at Context.wrappedFunction (packages/practicalmeteor:mocha-core/server.js:63:33)
What could be wrong here? any suggestions are welcome, thanks in advance.
Original post on meteor forum
UPDATE
Ok! here is my confustion, Let say I've a write a unit test for this method, How should I verify or get the userId here.
Meteor.methods({
userStatus:function(update){
check(update, {online: String})
if (! this.userId) {
throw new Meteor.Error('error-not-authorized','User need to login', {method: "userStatus"})
}
try {
Meteor.users.update(Meteor.userId(),{$set: {'status.online': !!parseInt(update.online)}})
} catch (e) {
console.error("Error",e);
}
}
});
You are directly invoking a method handler without an appropriate context (which should be a Method Invocation object, while you provide an empty object). The login method handler attempts to get the connection id and fails to do so.
If you want to test the integration of your package with the accounts-base package (and basically you do, as you are calling some of its code), you can create a connection and call the method with that connection.
let connection = DDP.connect(Meteor.absoluteUrl());
// prepare the login data
const params = {/*...*/};
connection.call('login', params);
// test post conditions
connection.disconnect();
Edit (following question edit):
The answer remains pretty much the same. Once you have called the login method and logged in the user, the connection state on the server should include the logged-in user's id. Now you can call the methods that require the user to be logged in.
Note that you should probably use this.userId on all occasions (and not Meteor.userId()).

What is the clean way to store information get from web service in angularJS?

I've got a front-end Angular App which get information from server side on a REST API.
When I make requests, I'd like to do it only once and so, keep the data in the front app.
I found several examples but either:
the logic is in controller -> not a good practice
variables are stored in the root scope -> not a good practice in general
I was told to use services to do this.
For example, from what I understood, I should have a service which GET user profile, and another one which keep these information (a "profile" service) once the GET request has been made.
Could someone show me how to do this if my service to call GET request is something like this:
.factory('userPromise', ['$http', '$q', 'baseUrl',
function ($http, $q, baseUrl) {
return {
getUser: function(usn, uid, key) {
return $http.get(baseUrl + 'business_infos/' + parseInt(uid) + '/'
+ '?format=json&username=' + usn + '&api_key=' + key).then(function(response) {
return response.data;
});
}
};
}])
Note that I don't want to just cache information. I want to create a user profile from what I get that can contain other information.
it would be something like:
{
profile: {
firstname: "Thierry",
lastname: "Chatel"
},
hasRole: function (role) {
return ...
}
}
Thank you!
There is documentation for cacheFactory, you can cahce data and get it.
http://code.angularjs.org/1.2.14/docs/api/ng/service/$cacheFactory