Browse Source

feat(invite): more progress on completing invite form and actually creating a real user, #2353

Torkel Ödegaard 10 năm trước cách đây
mục cha
commit
ab54971763

+ 2 - 1
pkg/api/api.go

@@ -45,6 +45,7 @@ func Register(r *macaron.Macaron) {
 
 	// invited
 	r.Get("/api/user/invite/:code", wrap(GetInviteInfoByCode))
+	r.Post("/api/user/invite/complete", bind(dtos.CompleteInviteForm{}), wrap(CompleteInvite))
 
 	// reset password
 	r.Get("/user/password/send-reset-email", Index)
@@ -97,7 +98,7 @@ func Register(r *macaron.Macaron) {
 			// invites
 			r.Get("/invites", wrap(GetPendingOrgInvites))
 			r.Post("/invites", bind(dtos.AddInviteForm{}), wrap(AddOrgInvite))
-			r.Patch("/invites/:id/revoke", wrap(RevokeInvite))
+			r.Patch("/invites/:code/revoke", wrap(RevokeInvite))
 		}, regOrgAdmin)
 
 		// create new org

+ 9 - 0
pkg/api/dtos/invite.go

@@ -14,3 +14,12 @@ type InviteInfo struct {
 	Name     string `json:"name"`
 	Username string `json:"username"`
 }
+
+type CompleteInviteForm struct {
+	InviteCode      string `json:"inviteCode"`
+	Email           string `json:"email" binding:"Required"`
+	Name            string `json:"name"`
+	Username        string `json:"username"`
+	Password        string `json:"password"`
+	ConfirmPassword string `json:"confirmPassword"`
+}

+ 49 - 3
pkg/api/org_invite.go

@@ -3,6 +3,8 @@ package api
 import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/events"
+	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
@@ -85,8 +87,7 @@ func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response
 
 func RevokeInvite(c *middleware.Context) Response {
 	cmd := m.UpdateTempUserStatusCommand{
-		Id:     c.ParamsInt64(":id"),
-		OrgId:  c.OrgId,
+		Code:   c.Params(":code"),
 		Status: m.TmpUserRevoked,
 	}
 
@@ -98,7 +99,7 @@ func RevokeInvite(c *middleware.Context) Response {
 }
 
 func GetInviteInfoByCode(c *middleware.Context) Response {
-	query := m.GetTempUsersByCodeQuery{Code: c.Params(":code")}
+	query := m.GetTempUserByCodeQuery{Code: c.Params(":code")}
 
 	if err := bus.Dispatch(&query); err != nil {
 		if err == m.ErrTempUserNotFound {
@@ -115,3 +116,48 @@ func GetInviteInfoByCode(c *middleware.Context) Response {
 
 	return Json(200, &info)
 }
+
+func CompleteInvite(c *middleware.Context, completeInvite dtos.CompleteInviteForm) Response {
+	query := m.GetTempUserByCodeQuery{Code: completeInvite.InviteCode}
+
+	if err := bus.Dispatch(&query); err != nil {
+		if err == m.ErrTempUserNotFound {
+			return ApiError(404, "Invite not found", nil)
+		}
+		return ApiError(500, "Failed to get invite", err)
+	}
+
+	invite := query.Result
+
+	cmd := m.CreateUserCommand{
+		Email:    completeInvite.Email,
+		Name:     completeInvite.Name,
+		Login:    completeInvite.Username,
+		Password: completeInvite.Password,
+	}
+
+	if err := bus.Dispatch(&cmd); err != nil {
+		return ApiError(500, "failed to create user", err)
+	}
+
+	user := cmd.Result
+
+	bus.Publish(&events.UserSignedUp{
+		Id:    user.Id,
+		Name:  user.Name,
+		Email: user.Email,
+		Login: user.Login,
+	})
+
+	// update temp user status
+	updateTmpUserCmd := m.UpdateTempUserStatusCommand{Code: invite.Code, Status: m.TmpUserCompleted}
+	if err := bus.Dispatch(&updateTmpUserCmd); err != nil {
+		return ApiError(500, "Failed to update invite status", err)
+	}
+
+	loginUserWithUser(&user, c)
+
+	metrics.M_Api_User_SignUp.Inc(1)
+
+	return ApiSuccess("User created and logged in")
+}

+ 2 - 3
pkg/models/temp_user.go

@@ -56,8 +56,7 @@ type CreateTempUserCommand struct {
 }
 
 type UpdateTempUserStatusCommand struct {
-	Id     int64
-	OrgId  int64
+	Code   string
 	Status TempUserStatus
 }
 
@@ -68,7 +67,7 @@ type GetTempUsersForOrgQuery struct {
 	Result []*TempUserDTO
 }
 
-type GetTempUsersByCodeQuery struct {
+type GetTempUserByCodeQuery struct {
 	Code string
 
 	Result *TempUser

+ 4 - 4
pkg/services/sqlstore/temp_user.go

@@ -12,13 +12,13 @@ func init() {
 	bus.AddHandler("sql", CreateTempUser)
 	bus.AddHandler("sql", GetTempUsersForOrg)
 	bus.AddHandler("sql", UpdateTempUserStatus)
-	bus.AddHandler("sql", GetTempUsersByCode)
+	bus.AddHandler("sql", GetTempUserByCode)
 }
 
 func UpdateTempUserStatus(cmd *m.UpdateTempUserStatusCommand) error {
 	return inTransaction(func(sess *xorm.Session) error {
-		var rawSql = "UPDATE temp_user SET status=? WHERE id=? and org_id=?"
-		_, err := sess.Exec(rawSql, string(cmd.Status), cmd.Id, cmd.OrgId)
+		var rawSql = "UPDATE temp_user SET status=? WHERE code=?"
+		_, err := sess.Exec(rawSql, string(cmd.Status), cmd.Code)
 		return err
 	})
 }
@@ -70,7 +70,7 @@ func GetTempUsersForOrg(query *m.GetTempUsersForOrgQuery) error {
 	return err
 }
 
-func GetTempUsersByCode(query *m.GetTempUsersByCodeQuery) error {
+func GetTempUserByCode(query *m.GetTempUserByCodeQuery) error {
 	var user m.TempUser
 	has, err := x.Table("temp_user").Where("code=?", query.Code).Get(&user)
 

+ 2 - 1
pkg/services/sqlstore/temp_user_test.go

@@ -17,6 +17,7 @@ func TestTempUserCommandsAndQueries(t *testing.T) {
 			cmd := m.CreateTempUserCommand{
 				OrgId:  2256,
 				Name:   "hello",
+				Code:   "asd",
 				Email:  "e@as.co",
 				Status: m.TmpUserInvitePending,
 			}
@@ -32,7 +33,7 @@ func TestTempUserCommandsAndQueries(t *testing.T) {
 			})
 
 			Convey("Should be able update status", func() {
-				cmd2 := m.UpdateTempUserStatusCommand{OrgId: 2256, Status: m.TmpUserRevoked, Id: cmd.Result.Id}
+				cmd2 := m.UpdateTempUserStatusCommand{Code: "asd", Status: m.TmpUserRevoked}
 				err := UpdateTempUserStatus(&cmd2)
 				So(err, ShouldBeNil)
 			})

+ 14 - 6
public/app/controllers/invitedCtrl.js

@@ -1,7 +1,8 @@
 define([
   'angular',
+  'config',
 ],
-function (angular) {
+function (angular, config) {
   'use strict';
 
   var module = angular.module('grafana.controllers');
@@ -10,20 +11,27 @@ function (angular) {
 
     contextSrv.sidemenu = false;
 
-    $scope.user = {};
+    $scope.formModel = {};
 
     $scope.init = function() {
       backendSrv.get('/api/user/invite/' + $routeParams.code).then(function(invite) {
-        $scope.user.name = invite.name;
-        $scope.user.email = invite.email;
-        $scope.user.username = invite.email;
-        $scope.user.inviteId =  invite.id;
+        $scope.formModel.name = invite.name;
+        $scope.formModel.email = invite.email;
+        $scope.formModel.username = invite.email;
+        $scope.formModel.inviteCode =  $routeParams.code;
 
         $scope.greeting = invite.name || invite.email;
       });
     };
 
     $scope.submit = function() {
+      if (!$scope.inviteForm.$valid) {
+        return;
+      }
+
+      backendSrv.post('/api/user/invite/complete', $scope.formModel).then(function() {
+        window.location.href = config.appSubUrl + '/';
+      });
     };
 
     $scope.init();

+ 1 - 1
public/app/features/org/orgUsersCtrl.js

@@ -40,7 +40,7 @@ function (angular) {
 
     $scope.revokeInvite = function(invite, evt) {
       evt.stopPropagation();
-      backendSrv.patch('/api/org/invites/' + invite.id + '/revoke').then($scope.get);
+      backendSrv.patch('/api/org/invites/' + invite.code + '/revoke').then($scope.get);
     };
 
     $scope.copyInviteToClipboard = function(evt) {

+ 6 - 6
public/app/partials/signup_invited.html

@@ -27,7 +27,7 @@
 								Email
 							</li>
 							<li>
-								<input type="email" name="email" class="tight-form-input last" required ng-model='user.email' placeholder="Email" style="width: 253px">
+								<input type="email" name="email" class="tight-form-input last" required ng-model='formModel.email' placeholder="Email" style="width: 253px">
 							</li>
 						</ul>
 						<div class="clearfix"></div>
@@ -38,7 +38,7 @@
 								Name
 							</li>
 							<li>
-								<input type="text" name="name" class="tight-form-input last" ng-model='user.name' placeholder="Name (optional)" style="width: 253px">
+								<input type="text" name="name" class="tight-form-input last" ng-model='formModel.name' placeholder="Name (optional)" style="width: 253px">
 							</li>
 						</ul>
 						<div class="clearfix"></div>
@@ -49,7 +49,7 @@
 								Username
 							</li>
 							<li>
-								<input type="text" name="username" class="tight-form-input last" required ng-model='user.username' placeholder="Username" style="width: 253px">
+								<input type="text" name="username" class="tight-form-input last" required ng-model='formModel.username' placeholder="Username" style="width: 253px">
 							</li>
 						</ul>
 						<div class="clearfix"></div>
@@ -61,7 +61,7 @@
 								Password
 							</li>
 							<li>
-								<input type="password" name="password" class="tight-form-input last" required ng-model="user.password" id="inputPassword" style="width: 253px" placeholder="password">
+								<input type="password" name="password" class="tight-form-input last" required ng-model="formModel.password" id="inputPassword" style="width: 253px" placeholder="password">
 							</li>
 						</ul>
 						<div class="clearfix"></div>
@@ -72,7 +72,7 @@
 								Confirm Password
 							</li>
 							<li>
-								<input type="password" name="confirmPassword" class="tight-form-input last" required ng-model="user.confirmPassword" id="confirmPassword" style="width: 253px" placeholder="confirm password">
+								<input type="password" name="confirmPassword" class="tight-form-input last" required ng-model="formModel.confirmPassword" id="confirmPassword" style="width: 253px" placeholder="confirm password">
 							</li>
 						</ul>
 						<div class="clearfix"></div>
@@ -80,7 +80,7 @@
 				</div>
 
 				<div style="margin-left: 147px; width: 254px;">
-					<password-strength password="user.password"></password-strength>
+					<password-strength password="formModel.password"></password-strength>
 				</div>
 
 				<div class="login-submit-button-row">