I am very new to loopback. I am making my first app with it and I change my datasource to MySql. my person model look like this
person.json
{
"name": "Person",
"plural": "People",
"base":"User",
"properties": {
"firstName": {
"type": "string",
"required": true
},
"lastName": {
"type": "string",
"required": true
},
"username": {
"type": "string",
"required": true
},
"email": {
"type": "string",
"required": true
},
"password": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {},
"acls": [
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"accessType": "*"
}
],
"methods": []
}
and
datasource.json
{
"db": {
"name": "db",
"connector": "memory"
},
"mysql": {
"host": "localhost",
"port": 3306,
"database": "demo",
"username": "demouser",
"password": "****",
"name": "mysql",
"connector": "mysql"
}
}
and
model-config.json
{
"_meta": {
"sources": [
"../common/models",
"./models"
]
},
"AccessToken": {
"dataSource": "db",
"public": false
},
"ACL": {
"dataSource": "db",
"public": false
},
"RoleMapping": {
"dataSource": "db",
"public": false
},
"Role": {
"dataSource": "db",
"public": false
},
"Person": {
"dataSource": "mysql",
"public": true
}
}
After this when I want to create any person I cant create any person as I am unauthorized. I inserted an entry in mysql and tried to login but no luck.But when I remove User as base of person then all goes fine. But I lose all auth features inherit from User. How can I extend User using mysql as datasource properly so that I can use Person as my Users?
Thanks in Advance
Can you try to add the following ACL?
"acls": [
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"accessType": "*",
"property": "create"
}
],
You can debug the application using:
DEBUG=loopback:security:* node .
Related
I am creating loopback applications with mysql . I set out the datasource will be mysql but when i run the applications i got following error in console windows .
ReferenceError: Cannot create data source "shop": Cannot initialize connector "mysql": time is not defined
Here is my datasource.json code .
{
"db": {
"name": "db",
"connector": "memory"
},
"shop": {
"host": "localhost",
"port": 8081,
"url": "",
"database": "shoppingdatabase",
"password": "",
"name": "shop",
"user": "root",
"connector": "mysql",
"connectTimeout":9000000
}
}
Here is the code for model.json
{
"_meta": {
"sources": [
"loopback/common/models",
"loopback/server/models",
"../common/models",
"./models"
],
"mixins": [
"loopback/common/mixins",
"loopback/server/mixins",
"../common/mixins",
"./mixins"
]
},
"User": {
"dataSource": "db"
},
"AccessToken": {
"dataSource": "db",
"public": false
},
"ACL": {
"dataSource": "db",
"public": false
},
"RoleMapping": {
"dataSource": "db",
"public": false,
"options": {
"strictObjectIDCoercion": true
}
},
"Role": {
"dataSource": "db",
"public": false
},
"carts": {
"dataSource": "shop",
"public": true
},
"products": {
"dataSource": "shop",
"public": true
},
"users": {
"dataSource": "shop",
"public": true
},
"vendors": {
"dataSource": "shop",
"public": true
}
}
Here is the screen shot when i run the server .
Change your datasource.json as :
{
"db": {
"host": "localhost",
"port": 3306,
"url": "",
"database": "shoppingdatabase",
"password": "",
"name": "db",
"user": "root",
"connector": "mysql",
"connectTimeout":9000000
}
}
and model.json as
{
"_meta": {
"sources": [
"loopback/common/models",
"loopback/server/models",
"../common/models",
"./models"
],
"mixins": [
"loopback/common/mixins",
"loopback/server/mixins",
"../common/mixins",
"./mixins"
]
},
"User": {
"dataSource": "db"
},
"AccessToken": {
"dataSource": "db",
"public": false
},
"ACL": {
"dataSource": "db",
"public": false
},
"RoleMapping": {
"dataSource": "db",
"public": false,
"options": {
"strictObjectIDCoercion": true
}
},
"Role": {
"dataSource": "db",
"public": false
},
"carts": {
"dataSource": "db",
"public": true
},
"products": {
"dataSource": "db",
"public": true
},
"users": {
"dataSource": "db",
"public": true
},
"vendors": {
"dataSource": "db",
"public": true
}
}
and before that create a database in MySQL manually with the name "shoppingdatabase" as loopback will need the database pre-created.
And keep it password protected too.
I created a task that talks to a REST API to retreive the values for 2 picklists.
Filling the first dropdown box works fine, when using just the jsonpath.
Based on the first picklist I'd like to retreive values of the second list.
I've tried some variations and I'm trying something like this:
The json which I receive in the first rest call is similar to:
{
"id": "45",
"href": "https://selfservice/api/configurations/45/",
"name": "Type",
"description": "",
"version": "0.0.4",
"attributes": [
{
"value": {
"sdk-object": {
"type": "ConfigurationElement",
"id": "56"
}
},
"type": "ConfigurationElement",
"name": "win"
},
{
"value": {
"sdk-object": {
"type": "ConfigurationElement",
"id": "57"
}
},
"type": "ConfigurationElement",
"name": "lin"
}
]
}
I try to show the attributes name in the list and need the id of the attribute in the second picklist.
I created the following datasourcebindings in the task.json. Of course, the targets exist in the task.
task.json:
{
"id": "GUID",
"name": "Spinup",
"friendlyName": "Create environment",
"description": "Starts Workflow to create an environment. ___ The following placeholders are created deplending on the template and can be used in the rest of the release:**XLDEnvironment** and **testAgentHostname**",
"category": "Deploy",
"author": "***",
"version": {
"Major": 0,
"Minor": 0,
"Patch": 33
},
"minimumAgentVersion": "1.95.3",
"inputs": [
{
"label": "Endpoint",
"name": "connectedServiceName",
"required": true,
"type": "connectedService:server",
"helpMarkDown": "endpoint to connect to."
},
{
"name": "stage",
"type": "string",
"label": "Stage",
"defaultValue": "$(Release.EnvironmentName)",
"required": true,
"helpMarkDown": "Stage of the release, default value is based on the pipeline."
},
{
"name": "releaseName",
"type": "string",
"label": "Environment name",
"defaultValue": "$(Release.ReleaseName)",
"required": true,
"helpMarkDown": "Name of the environment that will be created."
},
{
"name": "owner",
"type": "string",
"label": "Owner of the environment",
"defaultValue": "***",
"required": true,
"helpMarkDown": "It is common to use the initials of the product owner in this field. There has to be a valid AD user owner of each environment. This can also be set by using a variable through the pipeline."
},
{
"name": "group",
"type": "string",
"label": "Group of the owner",
"defaultValue": "",
"required": true,
"helpMarkDown": "group that owns the environment. groups can be requested by the infrastructure department. Example: ****"
},
{
"name": "platform",
"type": "pickList",
"label": "Platform (OS)",
"defaultValue": "",
"required": true,
"helpMarkDown": "Choose the type of the target platform."
},
{
"name": "size",
"type": "pickList",
"label": "Environment Template",
"defaultValue": "",
"required": true,
"helpMarkDown": "Size of the environment to create."
}
],
"dataSourceBindings": [
{
"dataSourceName": "GetCEPlatformType",
"endpointId": "$(connectedServiceName)",
"target": "platform",
"selector": "jsonpath:$.attributes[*].name",
"keySelector": "jsonpath:$.attributes[*].value.sdk-object.id"
},
{
"dataSourceName": "GetCEPlatformSize",
"endpointId": "$(connectedServiceName)",
"target": "size",
"parameters": {
"platformTypeId": "$(platform)"
}
}
],
"instanceNameFormat": "Spin up $(size) $(platform) environment",
"execution": {
"PowerShell3": {
"target": "$(currentDirectory)\\task.ps1",
"argumentFormat": "",
"workingDirectory": "$(currentDirectory)"
}
}
}
vss-extension.json:
{
"manifestVersion": 1,
"id": "*****",
"name": "Release Tasks",
"version": "1.0.1",
"publisher": "***",
"targets": [
{
"id": "Microsoft.VisualStudio.Services"
}
],
"description": "Tools to contact ***. Includes a task to spin up a platform and a task to remove the platform.",
"categories": [
"Build and release"
],
"icons": {
"default": "images/extension-icon.png"
},
"files": [
{
"path": "RemoveEnvironment"
},
{
"path": "SpinUpEnvironment"
}
],
"contributions": [
{
"id": "******",
"type": "ms.vss-distributed-task.task",
"targets": [
"ms.vss-distributed-task.tasks"
],
"properties": {
"name": "RemoveEnvironment"
}
},
{
"id": "*********",
"type": "ms.vss-distributed-task.task",
"targets": [
"ms.vss-distributed-task.tasks"
],
"properties": {
"name": "SpinUpEnvironment"
}
},
{
"description": "Service Endpoint type for all connections",
"id": "endpoint-type",
"properties": {
"authenticationSchemes": [
{
"inputDescriptors": [
{
"description": "Username",
"id": "username",
"inputMode": "textbox",
"name": "Username",
"validation": {
"dataType": "string",
"isRequired": true
}
},
{
"description": "Password",
"id": "password",
"inputMode": "passwordbox",
"isConfidential": true,
"name": "Password",
"validation": {
"dataType": "string",
"isRequired": false
}
}
],
"type": "ms.vss-endpoint.endpoint-auth-scheme-basic"
}
],
"dataSources": [
{
"endpointUrl": "api/configurations/*****/",
"name": "GetCEPlatformType",
"resultSelector": "jsonpath:$.attributes[*].name"
},
{
"endpointUrl": "api/configurations/$(platformTypeId)/",
"name": "GetCEPlatformSize",
"resultSelector": "jsonpath:$.attributes[*].name"
}
],
"displayName": "*****",
"helpMarkDown": "Enter the url and credentials to connect to the endpoint.",
"name": "server",
"url": {
"displayName": "Server URL",
"helpText": "Url for the server to connect to."
}
},
"targets": [
"ms.vss-endpoint.endpoint-types"
],
"type": "ms.vss-endpoint.service-endpoint-type"
}
]
}
Sadly I can't get this to work. The datasources exist in the endpoint, but I'm not sure what to do with the resultSelector.
Does anyone have an idea on how to get this to work? The documentation on this isn't too good.
I'm not familiar with mustache, but in the mustache test tool this seems to work.
Thoughts are appreciated!
Try with this in the first data source:
{
"dataSourceName": "dsList1",
"endpointId": "$(connectedServiceName)",
"target": "list1",
"selector": "jsonpath:$.attributes[*].name",
"keySelector": "jsonpath:$.attributes[*].value.sdk-object.id"
}
Update:
You were trying to use "platformTypeId" in vss-extension.json file while it was defined in task.json, this cause the error message you mentioned in the comment.
By the way, I just noticed that you are using "dataSourceBindings", then you cannot use "selector" and "KeySelector" to parse the result since they are used for "sourceDefinitions".
To achieve the feature you want with "dataSourceBindings", you can definition the endpoint url in the task.json directly instead of defining datasource in endpoint contribution.
So you can move the datasources section in the vss-extension.json file first, and then in the task.json file, change to following:
"dataSourceBindings": [
{
"endpointId": "$(connectedServiceName)",
"endpointURL": "api/configurations/*****/",
"target": "platform",
"resultselector": "jsonpath:$.attributes[*]",
"resultTemplate": "{ \"Value\" : \"{{{value.id}}}\", \"DisplayValue\" : \"{{{name}}}\" }"
},
{
"endpointId": "$(connectedServiceName)",
"endpointURL": "api/configurations/$(platformTypeId)/",
"target": "size"
}
],
I have a Loopback API in which I have a container and I'm trying to add some relations to my models (using this) but when it comes to adding relations to container.js, it does not work anymore and I get this error :
/home/mowso/Documents/loopback-examples/test/node_modules/loopback-datasource-juggler/lib/datasource.js:434
modelClass[relation.type].call(modelClass, name, params);
^
TypeError: Cannot read property 'call' of undefined
at EventEmitter.<anonymous> (/home/mowso/Documents/loopback-examples/test/node_modules/loopback-datasource-juggler/lib/datasource.js:434:36)
at EventEmitter.g (events.js:260:16)
at emitOne (events.js:77:13)
at EventEmitter.emit (events.js:169:7)
at DataSource.setupDataAccess (/home/mowso/Documents/loopback-examples/test/node_modules/loopback-datasource-juggler/lib/datasource.js:540:14)
at DataSource.attach (/home/mowso/Documents/loopback-examples/test/node_modules/loopback-datasource-juggler/lib/datasource.js:752:8)
at Function.ModelClass.attachTo (/home/mowso/Documents/loopback-examples/test/node_modules/loopback-datasource-juggler/lib/model-builder.js:336:16)
at Registry.configureModel (/home/mowso/Documents/loopback-examples/test/node_modules/loopback/lib/registry.js:238:15)
at configureModel (/home/mowso/Documents/loopback-examples/test/node_modules/loopback/lib/application.js:441:16)
at EventEmitter.app.model (/home/mowso/Documents/loopback-examples/test/node_modules/loopback/lib/application.js:133:5)
I have three models like in the example I link above, they basically just have different names. Here is my container.json :
{
"name": "Container",
"base": "PersistedModel",
"properties": {
"name": {
"type": "string"
}
},
"validations": [],
"relations": {
"user": {
"type": "belongsTo",
"model": "user",
"foreignKey": "ownerId"
}
},
"acls": [],
"methods": {}
}
and my user.json :
{
"name": "user",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {
"containers": {
"type": "hasOne",
"model": "Container",
"foreignKey": "ownerId"
},
"companies": {
"type": "hasOne",
"model": "company",
"foreignKey": "ownerId"
}
},
"acls": [],
"methods": {}
}
Here is the model-config.json :
{
"_meta": {
"sources": [
"loopback/common/models",
"loopback/server/models",
"../common/models",
"./models"
],
"mixins": [
"loopback/common/mixins",
"loopback/server/mixins",
"../common/mixins",
"./mixins"
]
},
"User": {
"dataSource": "db"
},
"AccessToken": {
"dataSource": "db",
"public": false
},
"ACL": {
"dataSource": "db",
"public": false
},
"RoleMapping": {
"dataSource": "db",
"public": false
},
"Role": {
"dataSource": "db",
"public": false
},
"Container": {
"dataSource": "myfile",
"public": true
},
"user": {
"dataSource": "db",
"public": false
},
"company": {
"dataSource": "db",
"public": false
}
}
and finally my datasource.json, notice that I have a storage folder at the root of my project :
{
"db": {
"name": "db",
"connector": "mongodb"
},
"myfile": {
"name": "myfile",
"connector": "loopback-component-storage",
"provider": "filesystem",
"root": "storage"
}
}
Your foreign key is not correct, as per the documentation it should be,
Relation name: Camel case of the model name, for example, for the "supplier" model the relation is "supplier".
Foreign key: The relation name appended with ‘Id’, for example, for relation name "supplier" the default foreign key is "supplierId".
So in your case it should be
"foreignKey": "userId"
https://docs.strongloop.com/display/public/LB/HasOne+relations
And you should use the slc relations instead of editing the json files.
I believe Container should inherit Model, not PersistedModel. By default, a Container is a folder inside a filesystem, so there is no choice of persisting it or not.
{
"name": "Container",
"base": "Model",
I made a website with nodejs and strongloop. I used the mysql connector and tested the connection in Strongloop Arc. The connection is OK. I created the models with the arc tool (discover models). It was also OK.
But when I open the Strongloop API Explorer and do the test to get the data I Always get the error:
"ER_NO_DB_ERROR: No database selected".
Have you confirmed that your model was connected to dbAuction instead of db?
When using arc to add a model, the 'datasource' should be 'dbAuction'.
You can also check the model-config.json in 'server/' directory. A property named 'dataSource' belongs to the model name like
"modelname" : {
"dataSouce":"dbAuction"
...
},
...
This is the model-config.json
{
"_meta": {
"sources": [
"loopback/common/models",
"loopback/server/models",
"../common/models",
"./models"
],
"mixins": [
"loopback/common/mixins",
"loopback/server/mixins",
"../common/mixins",
"./mixins"
]
},
"User": {
"dataSource": "db"
},
"AccessToken": {
"dataSource": "db",
"public": false
},
"ACL": {
"dataSource": "db",
"public": false
},
"RoleMapping": {
"dataSource": "db",
"public": false
},
"Role": {
"dataSource": "db",
"public": false
},
"newModel": {
"dataSource": "db",
"public": true
},
"Biedingen": {
"dataSource": "dbAuction",
"public": true
},
"Kavel": {
"dataSource": "dbAuction",
"public": true
},
"Gebruikers": {
"dataSource": "dbAuction",
"public": true
},
"Message": {
"dataSource": "dbAuction",
"public": true
},
"Veilingen": {
"dataSource": "dbAuction",
"public": true
},
"Sourcemessage": {
"dataSource": "dbAuction",
"public": true
},
"Veilingenkavels": {
"dataSource": "dbAuction",
"public": true
},
"Veilingsoorten": {
"dataSource": "dbAuction",
"public": true
}
and the datasources.json
{
"db": {
"name": "db",
"connector": "mysql"
},
"dbAuction": {
"host": "localhost",
"port": 3306,
"database": "auctions",
"password": "",
"name": "dbAuction",
"connector": "mysql",
"user": "root"
}
}
I can't add a comment since my reputation is too low..
I suggest you can change the 'mysql' in
"db": {
"name": "db",
"connector": "**mysql**"
into "memory" as it was initiated because the "db" is useful for some models while you changed wrongly so that 'db' cannot work well, which may lead to this problem.
Please could anyone help me?
I've created an admin user and a simple user, the admin can do anything thats ok, but I'm trying to edit the simple user using his own authenticated token, and I got unauthorized everytime even with get, post, put or delete, here my user.json:
{
"name": "User",
"properties": {
"realm": {
"type": "string"
},
"username": {
"type": "string"
},
"realName": {
"type": "string",
"required": true
},
"timezone": {
"type": "string",
"required": false
},
"language": {
"type": "string",
"required": false
},
"password": {
"type": "string",
"required": true
},
"credentials": {
"type": "object",
"deprecated": true
},
"challenges": {
"type": "object",
"deprecated": true
},
"email": {
"type": "string",
"required": true
},
"ownerId": {
"type": "number",
"required": true
},
"emailVerified": "boolean",
"verificationToken": "string",
"status": "boolean",
"created": "date",
"lastUpdated": "date"
},
"hidden": ["password"],
"acls": [
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
},
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "create"
},
{
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW",
"property": "deleteById"
},
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "login"
},
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "logout"
},
{
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW",
"property": "findById"
},
{
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW",
"property": "upsert"
},
{
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW",
"property": "updateAttributes"
},
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "activation"
},
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "confirm"
},
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "resetPassword",
"accessType": "EXECUTE"
}
],
"relations": {
"accessTokens": {
"type": "hasMany",
"model": "AccessToken",
"foreignKey": "userId",
"options": {
"disableInclude": true
}
}
}
}
Do you intend to override the base User model? If so, you should name it to something like MyUser and set the base as "User". In server/model-config.json, set "public" to false for "User" and true for "MyUser".
Ok, let's explain the solution.
Using the security debug command line I could see how Loopback resolve permissions:
# DEBUG=loopback:security:role node .
loopback:security:role isInRole(): $everyone +0ms
loopback:security:role Custom resolver found for role $everyone +1ms
loopback:security:role isInRole(): $owner +0ms
loopback:security:role Custom resolver found for role $owner +0ms
loopback:security:role isOwner(): User 11 userId: 11 +1ms
loopback:security:role Model found: {"realm":null,"username":"...","id":11} +4ms
loopback:security:role No matching belongsTo relation found for model "11" and user: 11 +1ms
Using this data I created a field named "userId" in User table and pointed a belongsTo directive to this field inside it's own file (user.json).
"user": {
"type": "belongsTo",
"model": "User",
"foreignKey": "userId"
}
Now the user 11 can edit your own profile but can not see or change any other user register.
It's not a beautiful solution, actually loopback should have this self related directive inside ACL logic, but it works.
Regards