Navigation Menu

Skip to content
This repository has been archived by the owner on May 28, 2021. It is now read-only.

User admin page #48

Closed
15 tasks done
icebob opened this issue May 16, 2017 · 32 comments
Closed
15 tasks done

User admin page #48

icebob opened this issue May 16, 2017 · 32 comments

Comments

@icebob
Copy link
Owner

icebob commented May 16, 2017

Branch: ice-services

Server-side tasks:

  • create users service (server/services/applogic/users). Recommend to clone the devices service. The Mongoose model is exist in server/models/user.js, just add a link file (like in persons/models/user.js)
    • permission is C.PERM_ADMIN
    • create actions (list, get, model, create, update, remove)
    • send passwordReset email to the user if new user created
    • create graphql schema & resolvers (to bottom of service file like in devices service)

Client-side tasks:

  • create new page for Users
    • create module for Users (client/app/modules/users). Recommend to clone the devices service.
    • the page is similar as Devices. Use DefaultAdminPage
    • create schema for table & form-generator with editable fields (fullName, username, email, roles, verified, apiKey, status)
    • create vuex store module (same as in devices)
    • add new page to core/router.js & core/components/sidebar

New tasks:

  • Disable to edit the "admin" user.
  • Handle errors on save (e.g. change username what cause duplicate key). No happens anything. Need at least an error toast
  • UI: apiKey is not editable. Readonly with "Generate" button. The button call "/generateAPIKey" and set the received apiKey.
  • UI: hide "Users" menu if the user has no "admin" role
@icebob icebob mentioned this issue May 16, 2017
5 tasks
@swyxio
Copy link
Contributor

swyxio commented May 17, 2017

this is where i run into some trouble as I need to understand the moleculer framework and how the Devices (and Users) page is supposed to be used. it seems that you want to have your user admin panel visible on the same front end as the normal client? does the Devices page just log all users? this will be interesting.. I won't work on this right now as I have a couple of things to clear but I will notify when I start work on this.

@icebob
Copy link
Owner Author

icebob commented May 17, 2017

It will be an admin page which only visible for admins. Currently there is no page filtering in client side, but I will add it to the router.

@swyxio
Copy link
Contributor

swyxio commented May 30, 2017

gotcha, i'm still traveling right now (been a loong trip) but should be available to start work on this this weekend. :)

@swyxio
Copy link
Contributor

swyxio commented Jun 4, 2017

starting work

@icebob
Copy link
Owner Author

icebob commented Jun 4, 2017

Thanks!

@swyxio
Copy link
Contributor

swyxio commented Jun 4, 2017

OK my main problem with adapting devices to users is the complexity of the user model.

#Problem 1
I am a little confused on what to do with the graphql schema. the devices schema is pretty flat, but users has some nested objects (socialLinks, profile, roles). I haven't been able to google any answers to how to nest types like this. this is what i have done so far:

		// SWYX: not sure about the [String] as these are technically objects
		types: `
			type User {
				fullName: String
				email: String
				username: String
				password: String
				passwordLess: Boolean
				passwordLessToken: String
				provider: String
				profile: [String]
				socialLinks: [String]
				roles: [String]
				resetPasswordToken: String
				resetPasswordExpires: Timestamp
				verified: Boolean
				verifyToken: String
				apiKey: String
				lastLogin: Timestamp
				locale: String
				status: Int
			}
		`,

		// SWYX: intentionally left out profile, socialLinks, roles, resetPasswordExpires, resetPasswordToken
		mutation: `
			userCreate(fullName: String, email: String, username: String, password: String, passwordLess: Boolean, passwordLessToken: String, provider: String, verified: Boolean, verifyToken: String, apiKey: String, lastLogin: Timestamp, local: String, status: Int): User
			userUpdate(code: String!, fullName: String, email: String, username: String, password: String, passwordLess: Boolean, passwordLessToken: String, provider: String, verified: Boolean, verifyToken: String, apiKey: String, lastLogin: Timestamp, local: String, status: Int): User
			userRemove(code: String!): User
		`,

#Problem 2
Consequently, it is tricky to represent these complex fields in the frontend UI (client/app/modules/users/schema.js). You have specified (fullName, username, email, roles, verified, apiKey, status) of which roles is a complex field (it is a multiselectable array instead of a simple enum). How should we handle roles?

@swyxio
Copy link
Contributor

swyxio commented Jun 4, 2017

just a note to self, the new users vuex store needed to be registered in client/app/core/store.js

@swyxio
Copy link
Contributor

swyxio commented Jun 4, 2017

ok @icebob please have a look at https://github.com/sw-yx/vue-express-mongo-boilerplate/tree/ice-services. I have added all the requested items but I have not created a PR because there is something seriously wrong with the rendering when I do npm run build and then npm start. look at how it renders now:
vembscreenshot

chrome console doesnt report any errors. Any idea what is going on?

In general i think there is some serious compatibility issues going on with the dependencies in ice-services. If you just try a fresh install of ice-services, you can npm start with the existing client build, but if you try to npm run dev or npm run build you run in to all sort of nightmares. I have no idea why these dependency issues are popping up especially since you specified version number for everything.

@icebob
Copy link
Owner Author

icebob commented Jun 5, 2017

I will check these issues

@icebob
Copy link
Owner Author

icebob commented Jun 5, 2017

When you developing use npm run dev. The ˛npm start˛is a production runner command.

@icebob
Copy link
Owner Author

icebob commented Jun 5, 2017

I checkout your branch and it seems it is working properly with npm run dev:
image

@swyxio
Copy link
Contributor

swyxio commented Jun 5, 2017

ok i was not using npm run dev because it somehow crashed immediately when i ran it.

Shawns-MacBook-Air:vue-express-mongo-boilerplate swyx$ npm run dev

> vue-express-mongo-boilerplate@1.0.0 dev /Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate
> cross-env NODE_ENV=development nodemon --debug

[nodemon] 1.11.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: /Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/server/**/* config.js webpack.*.config.js
[nodemon] starting `node --debug server/index.js`
(node:29681) [DEP0062] DeprecationWarning: `node --debug` and `node --debug-brk` are invalid. Please use `node --inspect` or `node --inspect-brk` instead.
[nodemon] app crashed - waiting for file changes before starting...

i have since changed the dev script to reflect the deprecation:

"dev": "cross-env NODE_ENV=development nodemon --inspect",

and now it runs fine.

@swyxio
Copy link
Contributor

swyxio commented Jun 5, 2017

ok so now the UI works and I am testing the functionality. for Updating and Deleting users this thing works. however for creating users, because of the limited set of fields we expose, the validation fails because we dont offer a password field. how do you think about this? there are a few options

  1. users that are created from this UI should all be set to passwordLess
  2. insert a password for the created user (this is actually what my company does in our admin panel backend so that we can give the password to the user) either input by the Admin user or autogenerated and notified from the backend.

we also need to figure out how to represent roles in the UI. see #Problem 1 and #Problem 2 above and let me know how you would solve?


smaller issue but let me know what the hungarian i18ns are for these words:

    "Users": "Users",
    "FullName": "Full Name",
    "Email": "Email",
    "Username": "Username",
    "verified?": "Verified",
    "apiKey": "apiKey",
    "AddNewUser": "Add New User",

i have committed the latest working version to https://github.com/sw-yx/vue-express-mongo-boilerplate/tree/ice-services (includes your PR to fix the webpack issue)

@icebob
Copy link
Owner Author

icebob commented Jun 5, 2017

Great!

About password I think the best solution is that in admin page we can't set password. On server side (users.create) we start a "password forgot" method. so we generate a resetPasswordToken and send an email to the user that he/she set a new password.

Problem 1: you can copy the nested graphql schema from "applogic/profile/service.js".
Problem 2: for roles you can use checklist field in vue-form-generator. It is perfect to use for roles.

Please don't care with hungarian translation. I will fill it after merge.

Btw, please remove sensitive fields from "modelPropFilter" in users service.

modelPropFilter: "code fullName email username passwordLess provider profile socialLinks roles verified apiKey lastLogin locale status createdAt updatedAt"

@swyxio
Copy link
Contributor

swyxio commented Jun 5, 2017

awesome.

separate, potential small issue:
note there is a potential naming issue with profile in the model and socialProfile in the graphql

https://github.com/icebob/vue-express-mongo-boilerplate/blob/ice-services/server/services/applogic/profile/service.js#L66

vs

i am leaving this unresolved for now until i can figure out if this matters. (i'm a complete graphql newbie so i am sticking very close to the devices example until i see how it all fits together)

@swyxio
Copy link
Contributor

swyxio commented Jun 6, 2017

ok i think I am at my last 2 problems.

#1 I am still unable to create user without password because the password validation throws an error. i have worked around this by generating a dummy password so i can save the new User but it feels very hacky. this is a small problem we dont really have to deal with it until later.

#2 I cannot render the password reset email like you do normally because i dont have access to req.render and i also don't have req.headers.host. how do you do it???? here is where i am stuck https://github.com/sw-yx/vue-express-mongo-boilerplate/blob/ice-services/server/services/applogic/users/service.js#L119

@icebob
Copy link
Owner Author

icebob commented Jun 6, 2017

#1 I think this solution is good. I'm using same method when user sign-up a passwordless account. So it's correct.

#2 the mail template is a pug file. So you can render it with pug instead of req.render.

I think the socialProfile is good name fro graphql type.

@swyxio
Copy link
Contributor

swyxio commented Jun 6, 2017

ok awesome. so the only remaining issues i have are all to do with the pug rendering and mail sending. I may not have the knowledge to finish this final step but maybe I am missing something.

#1: i18n is normally provided through req.t()
https://github.com/sw-yx/vue-express-mongo-boilerplate/blob/ice-services/server/services/applogic/users/service.js#L115
when i render the pug i need to supply i18n text but this is only available from Express' req. as far as I can tell the create user service is not contained in the Express flow so I am unable to get it

#2: backlink is normally provided through req.headers.host
https://github.com/sw-yx/vue-express-mongo-boilerplate/blob/ice-services/server/services/applogic/users/service.js#L120
when i render the password reset i need to supply the backlink but this is only available from Express' req.headers. as far as I can tell the create user service is not contained in the Express flow so I am unable to get it

#3: pug.render is not the same as req.render
https://github.com/sw-yx/vue-express-mongo-boilerplate/blob/ice-services/server/services/applogic/users/service.js#L117
pug.render does not respect filepath which is a problem. the code i have generates this error:

error in rendering mail { Error: Pug:1:1
  > 1| ../../../views/mail/passwordReset
-------^

"./" is not a valid class name.  Class names must begin with "-", "_" or a letter and can only contain "_", "-", a-z and 0-9.
    at makeError (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug-error/index.js:32:13)
    at Lexer.error (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug-lexer/index.js:58:15)
    at Lexer.className (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug-lexer/index.js:419:12)
    at Lexer.callLexerFunction (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug-lexer/index.js:1316:23)
    at Lexer.advance (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug-lexer/index.js:1352:15)
    at Lexer.callLexerFunction (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug-lexer/index.js:1316:23)
    at Lexer.getTokens (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug-lexer/index.js:1372:12)
    at lex (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug-lexer/index.js:12:42)
    at Object.lex (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug/lib/index.js:93:27)
    at Function.loadString [as string] (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug-load/index.js:44:24)
    at compileBody (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug/lib/index.js:80:18)
    at Object.exports.compile (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug/lib/index.js:237:16)
    at handleTemplateCache (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug/lib/index.js:210:25)
    at Object.exports.render (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug/lib/index.js:379:10)
    at Object.exports.render (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/pug/lib/index.js:365:21)
    at Promise.resolve.then.then.then.then.then.json (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/server/services/applogic/users/service.js:117:21)
    at tryCatcher (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/bluebird/js/release/promise.js:693:18)
    at Async._drainQueue (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/bluebird/js/release/async.js:133:16)
  code: 'PUG:INVALID_CLASS_NAME',
  msg: '"./" is not a valid class name.  Class names must begin with "-", "_" or a letter and can only contain "_", "-", a-z and 0-9.',
  line: 1,
  column: 1,
  filename: undefined,
  src: '../../../views/mail/passwordReset',
  toJSON: [Function] }

which shows it is taking the filename instead of the filepath. so doing something like the example set seems impossible. i looked thru pug's docs and there doesnt seem to be a way to resolve filepaths from within pug, except maybe with the resolve api.

#4: smaller issue - we use libs/mailer everywhere but I get the

debug: Deprecated! libs/mailer is deprecated. Use Service.get('mailer') instead!

warning everywhere. I am continuing to use libs/mailer because I dont think we are using Service.get('mailer') yet.


phew, that's a lot of errors all close together! basically we are trying to use a microservice to send mail but the mailing procedures are all set up to be sent from the main server thread and maybe that's just thinking about it in the totally wrong way. is there another way to do this?

@icebob
Copy link
Owner Author

icebob commented Jun 7, 2017

I will check it.

@icebob
Copy link
Owner Author

icebob commented Jun 8, 2017

Thanks for you hard work!

  1. currently it doesn't matter because I will change this email sending to moleculer-mail which support localizated templates out-of-box.

  2. I fixed

  3. I fixed

  4. See also (1). I will remove the deprecated message :)


As I wrote, I will change the current email sender to the new moleculer module which will solve these problems. The microservices is the future. And with Moleculer it's easy and the developer has option how it is running (as microservices in many containers with scaling or as monolith in one process). I aim this project will be the first microservices boilerplate for Node (based on Moleculer of course :) )

P.S: I added some new tasks in the first comment. Could you do it?

@swyxio
Copy link
Contributor

swyxio commented Jun 8, 2017

thanks for the responses! will follow up on the new tasks this weekend

@swyxio
Copy link
Contributor

swyxio commented Jun 11, 2017

regarding "Disable to edit the "admin" user." - there may be more than one admin user. should one admin user be able to edit another admin user?

@swyxio
Copy link
Contributor

swyxio commented Jun 12, 2017

please look at swyxio@4b96fff (here is the link to the ice-services branch itself)

i have handled

 UI: apiKey is not editable. Readonly with "Generate" button. The button call "/generateAPIKey" and set the received apiKey.
 UI: hide "Users" menu if the user has no "admin" role

i am having a lot of trouble with

Handle errors on save (e.g. change username what cause duplicate key). No happens anything. Need at least an error toast

i am not sure where is the best place to insert the error handling. i am currently trying to insert this in server/core/api-service.js:

	/**
	 * When editing a user, check if a given field (that is supposed to be unique) is duplicate
	 *
	 * @param {any} model			Model
	 * @param {any} fieldName		name of unique field in model
	 * @returns
	 *
	 * @memberOf Service
	 */
	checkDuplicateField(ctx, fieldName) {
		return Promise.resolve(ctx)
			.then(ctx => {
				
				let query = this.collection.find({
					[fieldName]: ctx.params[fieldName]
				});
				return query.exec();
			})
			.then (docs => {
				console.log('------------------------------');
				console.log('checkDuplicateField query ' + ctx.params[fieldName],docs);
				console.log('------------------------------');
				if (docs.length < 2) {
					if (!docs.length)
						return ctx;
					//if you get here then docs.length == 1
					if (docs[0]._id == ctx.params["id"]) { // if username is unchanged for user based on the SAME id, proceed
						return ctx;
					} else {
						throw new E.RequestError(E.FORBIDDEN, C.DUPLICATE_FIELD, "app:DuplicateField");
					}
				}
				return ctx;
			});
	}

and this would go into the user update flow (server/services/applogic/users):

		update: {
			defaultMethod: "put",
			needModel: true,
			handler(ctx) {
				return this.Promise.resolve(ctx)
				.then(ctx => this.checkDuplicateField(ctx,"username"))
				.then(ctx => this.resolveID(ctx))
				.then(modelID => this.checkModel(modelID, "app:UserNotFound"))
				.then(modelID => this.collection.findById(modelID).exec())
				.then(doc => {

                             (....truncated)

I am not sure that this is a good way to do this as it doesnt do a "catchall" for all errors.

@icebob
Copy link
Owner Author

icebob commented Jun 12, 2017

regarding "Disable to edit the "admin" user." - there may be more than one admin user. should one admin user be able to edit another admin user?

Good question :) Please ignore this task :) I just want to block that admin can close out of page. E.g last admin remove admin role and after it there are no admins

@icebob
Copy link
Owner Author

icebob commented Jun 12, 2017

i am not sure where is the best place to insert the error handling. i am currently trying to insert this in server/core/api-service.js:

No, no, the duplication is detected in MongoDB with schema. You only need to handle this exceptions and send back the error to the UI. Tomorrow I'll search example.

@swyxio
Copy link
Contributor

swyxio commented Jun 12, 2017

yeah i had a feeling i was doing it very very wrong.

@icebob
Copy link
Owner Author

icebob commented Jun 12, 2017

No problem, just there is an easier way :) But thanks your diligent work :)
P.S. I added some comment to your commit.

@icebob
Copy link
Owner Author

icebob commented Jun 13, 2017

So the apiKey is unique in Mongoose model. You do nothing with it. But if you try to save the same key, Mongoose will throw an exception. You have to handle it here

@swyxio
Copy link
Contributor

swyxio commented Jun 15, 2017

i am facing serious trouble implementing this error handling. the problem is that the failure occurs further up the chain. for example if you try to create a user with a duplicate username, this happens on serverside:

error: [WWW-SVC] registerRESTRoutes Request error:  { WriteError({"code":11000,"index":0,"errmsg":"E11000 duplicate key error collection: boilerplate-dev.users index: username_1 dup key: { : \"test\" }","op":{"_id":26,"updatedAt":"2017-06-15T19:40:29.106Z","createdAt":"2017-06-15T19:40:29.106Z","username":"test","resetPasswordToken":"686467a020325f601c88c04f9522de454bc2c9b8748afc117e","resetPasswordExpires":"2017-06-16T19:40:29.088Z","status":1,"verified":true,"roles":["user"],"provider":"local","passwordLess":true,"password":"$2a$10$GyYuzbr.dyT7W7WdtgAE6uNI5O6YqQLFppuB5rEa7JDCKlZDe6CWi","email":"lkjsd@sdlkj.com","fullName":"sdlkj","__v":0}})
    at Function.MongoError.create (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/mongoose/node_modules/mongodb-core/lib/error.js:31:11)
    at toError (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/mongoose/node_modules/mongodb/lib/utils.js:139:22)
    at /Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/mongoose/node_modules/mongodb/lib/collection.js:669:23
    at handleCallback (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/mongoose/node_modules/mongodb/lib/utils.js:120:56)
    at /Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/mongoose/node_modules/mongodb/lib/bulk/unordered.js:465:9
    at handleCallback (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/mongoose/node_modules/mongodb/lib/utils.js:120:56)
    at resultHandler (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/mongoose/node_modules/mongodb/lib/bulk/unordered.js:413:5)
    at /Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/mongoose/node_modules/mongodb-core/lib/connection/pool.js:461:18
    at _combinedTickCallback (internal/process/next_tick.js:95:7)
    at process._tickCallback (internal/process/next_tick.js:161:9)
From previous event:
    at model.wrappedPointCut [as save] (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/mongoose/lib/services/model/applyHooks.js:117:23)
    at Promise.resolve.then (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/server/services/applogic/users/service.js:103:18)
From previous event:
    at APIService.handler (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/server/services/applogic/users/service.js:80:6)
From previous event:
    at ServiceBroker.call (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/moleculer/src/service-broker.js:699:15)
    at Context.call (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/moleculer/src/context.js:138:22)
    at Promise.resolve.then.then (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/server/services/www/service.js:174:19)
    at runCallback (timers.js:800:20)
    at tryOnImmediate (timers.js:762:5)
    at processImmediate [as _immediateCallback] (timers.js:733:5)
From previous event:
    at handler (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/server/services/www/service.js:173:8)
    at Layer.handle [as handle_request] (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/layer.js:95:5)
    at /Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:281:22
    at Function.process_params (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:335:12)
    at next (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:275:10)
    at tryAuthenticatedWithApiKey (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/server/services/www/auth/helper.js:46:3)
    at Layer.handle [as handle_request] (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:317:13)
    at /Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:335:12)
    at next (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:275:10)
    at Function.handle (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:174:3)
    at router (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:47:12)
    at Layer.handle [as handle_request] (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:317:13)
    at /Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:335:12)
    at next (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:275:10)
    at /Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:635:15
    at next (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/index.js:260:14)
    at tryAuthenticatedWithApiKey (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/server/services/www/auth/helper.js:46:3)
    at Layer.handle [as handle_request] (/Users/swyx/Desktop/webdev/vemb/vue-express-mongo-boilerplate/node_modules/express/lib/router/layer.js:95:5)
  name: 'MongoError',
  message: 'E11000 duplicate key error collection: boilerplate-dev.users index: username_1 dup key: { : "test" }',
  driver: true,
  code: 11000,
  index: 0,
  errmsg: 'E11000 duplicate key error collection: boilerplate-dev.users index: username_1 dup key: { : "test" }',
  getOperation: [Function],
  toJSON: [Function],
  toString: [Function],
  ctx: [Object] }
debug: [BROKER] Event emitted: metrics.trace.span.finish
debug: [METRICS-SVC] ┌───────────────────────────────────────────────────────────────────────┐
debug: [METRICS-SVC] │ request.rest         × 308.65ms [■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■] │
debug: [METRICS-SVC] │   users.create       × 302.81ms [■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■] │
debug: [METRICS-SVC] └───────────────────────────────────────────────────────────────────────┘
debug: POST /api/users 500 317.168 ms - 151
...

I dont understand why the error code returned is 500 instead of 11000. but that is what is happening and so the clientside is not handling this correctly:

Create new model...
Save model...
POST http://localhost:3000/api/users 500 (Internal Server Error)
(anonymous) @ VM4500:1
dispatchXhrRequest @ xhr.js:177
xhrAdapter @ xhr.js:12
dispatchRequest @ dispatchRequest.js:52
actions.js:51 response Error: Request failed with status code 500
    at createError (createError.js:15)
    at settle (settle.js:18)
    at XMLHttpRequest.handleLoad (xhr.js:77)

at the User.save function that you pointed to above. At this point i'm quite lost in the microservices as I cannot pinpoint where the error code is coming from. I think its coming because error.status is not a field being returned by the Mongoose error and so you automatically replace it with 500. can you confirm?

@icebob
Copy link
Owner Author

icebob commented Jun 20, 2017

I think, you need to handle the error in user.create and return back with an other Error. I created a PR in your fork: swyxio#5

return user.save().catch(err => {
	if (err && err.code === 11000) {
		let field = err.message.split(".$")[1];
		field = field.split(" dup key")[0];
		field = field.substring(0, field.lastIndexOf("_"));						
		let newErr = new Error("Unable to save user!");
		newErr.params = {
			field: field
		};
		newErr.status = 400;
		throw newErr;
	}
	throw err;
});

Now it is catch the user.save error and create a better Error with the conflicted field. And afterwards you could handle it in the server side and show a message or toast.

image

You can improve it if you add locale error messages and set msgCode to newError.
E.g. if the field is email, then show a message as "Unable to save the user because this email is in use!"
The www service will transform this code to the locale message (by logged in user's language)

@swyxio swyxio mentioned this issue Jun 23, 2017
swyxio added a commit to swyxio/vue-express-mongo-boilerplate that referenced this issue Jun 28, 2017
@icebob
Copy link
Owner Author

icebob commented Jul 8, 2017

@sw-yx Thank you for your work! I merged.
I found some bugs but I fixed :)

@icebob icebob closed this as completed Jul 8, 2017
@swyxio
Copy link
Contributor

swyxio commented Jul 8, 2017

this was my first-ever contribution to open source so thanks very much for patiently working with me on this :) I am going to work on a couple other projects and improve my skills for now but i will definitely keep an eye out for moleculer and when you roll out ice-services to the master branch!!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants