How to create users while registering with different roles via API?

@juniorconte not sure how this is working for you. You were right about role not being set properly during registration. This was a bug and I just fixed this on develop. With this bug fixed your script should work properly now. Also I would suggest not to use “type” in payload. This can be easily altered by any app. Instead use your API KEY to detect which app (customer or vendor) the request is coming from. DF2 already translates the API KEY to app id and stores it in the session. You can get it in ‘platform.session.app.id’. Here is the modified version of your script. I tried this with the bug fixed and it works.

var lodash = require("lodash.min.js");

var role1Id = 2;
var role2Id = 3;

var appVendorId = 66;
var appCustomerId = 65;

var originAppId = platform.session.app.id;

if (platform.session.user) {
      var userObject = platform.api.get('system/user/'+platform.session.user.id+'?related=user_to_app_to_role_by_user_id');

      lodash._.each(userObject.content.user_to_app_to_role_by_user_id, function(userApp) {
       switch (originAppId) {
          case appCustomerId:
          if (userApp.app_id === appCustomerId) {
              userApp.role_id = role1Id;
          }
          break;
          case appVendorId:
          if (userApp.app_id === appVendorId) {
              userApp.role_id = role2Id;
          }
          break;
      }
     });

    platform.api.patch('system/user/'+platform.session.user.id+'?related=user_to_app_to_role_by_user_id', userObject.content);
}

@aislam Can you please let me know when will this bug be fixed and the version be available for public?

This fix will be in the next release version 2.1.1. It will available around mid March.

I may not quite get your use case but here’s what I did in a single iOS app that has 3 roles that appears to be working correctly so far (I’m still build) and may be able to help. I’m not much of a coder so I’m really digging in to leverage the capabilities already available to me in DF, so this may be an oversimplification, but nonetheless…

My use case is:

  1. DEVICE: I need to register all devices on launch and be able to track/manage them starting from first launch. This way I am not relying on a user to register before getting any of the installed device information.
  2. DEMO: I need to provide users a fully functional, networked demo app. My app is a data-driven business app that can have multiple users in the same company (and multiple companies) all sharing data so I cannot simply just seed demo data.
  3. LIVE - of course…

One key thing to grasp is that you have a single DF User but that User can access any app / role / script / service on your DF instance.

Here’s the DF setup I’ve got working so far for my use case:

  1. I created 2 Databases, One LIVE (MySQL) and 1 DEMO (Mongo). Let’s call them LIVEDB and DEMODB. I put the DEMODB on Mongo so I can learn more about it and see how it behaves with my app without affecting any live users. So far I’ve been able to interact between both dB types in my app without modifying my code to distinguish between them. One caveat/difference is that the MySQL primary keys start at 1 and increment on create, Mongo hashes a 16-char primary key on create. To avoid any future problems with synching records and pulling common data between the two dB’s is I have my app create a unique 36 char hash with each new object and store it as ‘objectId’ (users, customers, accounts, devices, inventory, transactions, etc.- everything gets one before stored.)

  2. I created 3 Roles - LIVEROLE, DEMOROLE, DEVICEROLE

  3. I created 3 Apps - LIVEAPP, DEMOAPP, DEVICEAPP

  4. I assigned the proper role to each app as default - LIVEAPP/LIVEROLE - DEMOAPP/DEMOROLE - DEVICEAPP/DEVICEROLL.

  5. I created ‘Device’ tables in my LIVEDB and DEMODB. One thing to note, on the Mongo DB I just needed to create a table name. When my app sends data if the fields don’t exist in the table Mongo creates them. The MySQL database I have to manually create every field first or they won’t get logged.

  6. I went to the USER service and checked OpenReg and assigned the DEMOROLE. This allows not only my devices to register/update on launch without a user session, but also assigns all users to the DEMOROLE upon self-registration.

  7. in my DEVICEROLE, I assigned GET, POST and PATCH access to both dB’s /_table/Device and /_tableDevice*

  8. in my DEMOROLE, I assigned to my DEMODB GET, POST, PATCH, PUT, DELETE to /_table and /_table*. This give DEMO users access only to my DEMODB I did the same for my LIVE users in my LIVEDB, so now only the LIVEROLE users can access the LIVEDB. Down the road I’ll lock it down some more with scripts, lookup keys and what have you as I learn more about this awesome platform…

  9. I haven’t done it yet but I’m going to do the same thing I did with my db’s with the File service - create 2 storage containers for LIVE and DEMO then assign the appropriate roles.

  10. I’ve read some posts and blogs that you need to enable CORS with *. I originally did then deleted the CORS record and everything still communicates fine between my app and user dB’s…

That’s it for the DF setup. Now I’ll explain how it works out in my app so far…

  1. The trick is to create service definitions to distinguish the APIs between apps (for me device, live and demo). Once you do this, the logic can be built into your app to call the correct Role through the API, keep your JSON structures as is and switch easily between APIs (ROLES) without a bunch of coding. It’s probably easier for me to show you my code than explain.

I used the DF iOS starter app as a basis and pattern for my app to follow. In the API management class (RESTEngine.h / .m in the starter app), I set up my global variables like this:

//BASE URL
#define kBaseUrl @"http://<yourUrl>/api/v2"

//dB Services
    #define kDemoService @"/demomdb"
    #define kLiveService @"/ipaysql"
    #define kDbServiceName @"/_table"

//Header API Key Parameters
#define kDemoApiKey @"<the DEMOAPP Api Key>"
#define kLiveApiKey @"<the LIVEAPP Api Key>"
#define kDeviceApiKey @"the DEVICEAPP Api Key"

//Header Key and Content Params
#define kHeaderApiKeyParam @"X-DreamFactory-Api-Key"
#define kContentType @"application/json"

//user Session services
#define kSessionTokenKey @"SessionToken"
#define kUserEmail @"UserEmail"
#define kPassword @"UserPassword"

In my implementation file (RESTEngine.m) I created routing/path methods using these global variables. Here’s an example:

+ (NSString *)userWithResourceName: (NSString *)resourceName {
    return [NSString stringWithFormat:@"%@/user/%@", kBaseUrl, resourceName];
}

+ (NSString *)customUserWithResourceName: (NSString *)resourceName {
    return [NSString stringWithFormat:@"%@/system/user/%@",kBaseUrl, resourceName];
}

Now here is where I set up conditional-based routing/paths that I will call in my app. For me, it will be a simple LIVE/DEMO switch. If the user is in LIVE mode and switched to DEMO, I have a global BOOL value that sets the kApi parameter to start using the kDemoApiKey DEMOAPP API in it’s serviceWithTableName calls. This will ‘assume’ the DEMOROLE and start using the DEMO database. If the User switches back to LIVE then the kLiveApiKey is set in the serviceWithTableName to the LIVEAPP and the user assumes the LIVEROLE and interacts with the LIVEDB (and scripts, and services and whatever else the LIVEROLE is assigned to do). Look closely at the returns in this call referencing my global definitions above, it should explain it all…

This is my code to set the header params to set on my BOOL determining what ‘mode’ the user requests. The myControl method is my own private class to store vars.

- (NSDictionary *)headerParams {
    
    if ([[[myControl controller]getTransactionValue:@"deviceSetup"]boolValue] == TRUE) {
        if (_headerParams == nil) {
            _headerParams = @{@"X-DreamFactory-Api-Key": kDeviceApiKey};
            LOGI(@"USING DEVICE API");
        }
    }
    
    if ([[[myControl controller]getSessionValue:@"liveMode"]boolValue] == TRUE) {
        if (_headerParams == nil) {
            _headerParams = @{@"X-DreamFactory-Api-Key": kLiveApiKey};
            LOGI(@"USING LIVE API");

        }
    } else {
        if (_headerParams == nil) {
            _headerParams = @{@"X-DreamFactory-Api-Key": kDemoApiKey};
            LOGI(@"USING DEMO API");
        }
    }
    return _headerParams;
}

And here is how I set up my path routing services based on the live/demo BOOL value.

+ (NSString *)serviceWithTableName: (NSString *)tableName {
    
    if ([[[myControl controller]getSessionValue:@"liveMode"]boolValue] == TRUE) {
        
        return [NSString stringWithFormat:@"%@%@%@%@", kBaseUrl, kLiveService, kDbServiceName, tableName];
        
    } else {
        
        return [NSString stringWithFormat:@"%@%@%@%@", kBaseUrl, kDemoService, kDbServiceName, tableName];
    }
}

To wrap it up, on launch I set the kDeviceApiKey and the and check local storage for the LIVE/DEMO value and set a flag for it ‘isLive = TRUE’. Once the app gathers all the required device information I check to see if the local storage/keychain has a unique UUID (my 36-char hash deviceId). If deviceId = null, then it is flagged a ‘firstRun = TRUE’. I create a new hash and run a POST (all new installs and registrations start in DEMO mode). Remember way back when we started that the DEVICEAPP has the DEVICEROLE default - and the DEVICEROLE is configured to ONLY GET, POST, PATCH to DEMODB /_tables/Device - can’t do anything else anywhere else (I hope).

If deviceId exists in localstore/keychain on launch then I check the local/store/keychain value for a BOOL = ‘isLive’ which stored the last user setting for LIVE/DEMO. If TRUE, I set the kApiLive in the header and route paths (see code above). If FALSE then I set the kApiDemo header and router paths. Then I do a GET for the device to check a necessary flag on database record = isActive. If = FALSE then I deactivate the app and clear the ApiPaths. If TRUE then I run a PATCH to update the record in the (live/demo) database with the current device/location details. Once complete I set myGlobal var for device mode to FALSE.

THEN is when the Login screen becomes available. If firstRun, header/route has already been set to kApiDemo. If firstRun = FALSE, then header/path is LIVE or DEMO based on whatever the user had last session stored in the localStore/Keychain. Finally, the Login screen can also have a switch available for the user to set the kApiLive or kApiDemo API and routes prior to logging on…

Hope this helps…

Hi @aislam

Detect the type from the API KEY is really very good. Thanks for the tip.
I recorded my screen DF playing all of the scenario I had described as functional. Apparently everything is working as it should. Then I’ll test with the suggestion that you gave me.

@juniorconte, I see how it works for you. You had the default role set not just for open registration but also for the APPs to public. In my case I had it set for open registration only. I had no default role set for APPs. Regardless, there was a bug in the code that didn’t set the role for the newly registered user to what’s set on the open registration. Which is fixed now but at least this way you can get this to work until the fix goes out. Thanks for the screen recording and pointing this out.

1 Like

@juniorconte 's way is also not working for me. All the users are getting defaulted to public role. The bug I have observed is platform.session.user is not available during the post process. Everything else is fine. Just because the platform.session.user is not available the if statement fails and we can’t do a patch event.

@aislam Can you please let me know what I need to, which file I need to modify to make this available so that I can get the user id and make a patch event working. Mid march is something which is very far for us.

@ramnew2006 I believe has missed define the role of the user registration as the role standard App.

@juniorconte I have added the public role to both user registration as well as the Apps default role.

@ramnew2006 which dreamfactory version you are using on?

2.1

1 Like

@ramnew2006, just to make sure are you setting login parameter to true when registering the user?
api/v2/user/register?login=true

@aislam yes I am. Adding to that I’m making an AJAX call from Google Chrome console. Here is the request I am making. I used one of the app’s API key.

$.ajax({
                    dataType: 'json',
                    contentType: 'application/json; charset=utf-8',
                    url: "http://api.doorstepsolutions.co.in" + '/api/v2/user/register?login=true',
                    data: JSON.stringify({
                        "email": "storygagooooof@gmail.com",
                        "new_password": "password"
                    }),
                    cache:false,
                    method:'POST',
headers:{
"X-DreamFactory-API-Key":"API_KEY"
},
                    success:function (response) {
                        console.log(response);
                    },
                    error:function (response) {
                        console.log(response);
                        return false;
                    }
                });

@ramnew2006, You need to use a valid API_KEY for the X-DreamFactory-API-Key header. This would be the API key of your ‘vendor’ or ‘customer’ app. Without the API Key initial role won’t be set for your user.

@aislam I just removed that API Key while I placed this code here :stuck_out_tongue: I used customer app’s api key in my original call!

@aislam To be more precise, please look at the below screenshot, I tried to write the platform.session into response object. You can see there is nothing related to user under the session object, that’s why the if loop is failing.

thanks for u r command mr.aislam i got (platform.session.user) is null how to solve this

Hi,
I am getting error on this line var userObject = platform.api.get(‘system/user/’+platform.session.user.id+’?related=user_to_app_to_role_by_user_id’);

api error says command return with error code 1, any idea what that means ?

I am having this issue on Process Event Scripts system system.user [POST] system.user system.user.post.post_process
but on service its working fine.

this is the response:

“error”: {
“context”: null,
“message”: “This instance cannot run server-side javascript scripts. The ‘v8js’ is not available.”,
“code”: 503,
“trace”: [