Error: Cyclic dependency: "User" with loopback passport example - node.js

I'm trying to work with Strongloop loopback and loopback-component-passport but can't quite figure out how to get the relationships between the built in User model, and the loopback-component-passport provided models, to work
It looks like the relationships are provided in the loopback-component-passport/lib/models/*.json files but I get the following error:
Error: Cyclic dependency: "User"
at visit (/work/node/loopback/myapp/node_modules/loopback-boot/node_modules/toposort/index.js:29:13)
at visit (/work/node/loopback/myapp/node_modules/loopback-boot/node_modules/toposort/index.js:43:9)
at toposort (/work/node/loopback/myapp/node_modules/loopback-boot/node_modules/toposort/index.js:22:22)
at module.exports.exports (/work/node/loopback/myapp/node_modules/loopback-boot/node_modules/toposort/index.js:10:10)
at sortByInheritance (/work/node/loopback/myapp/node_modules/loopback-boot/lib/compiler.js:248:21)
at buildAllModelInstructions (/work/node/loopback/myapp/node_modules/loopback-boot/lib/compiler.js:205:10)
at compile (/work/node/loopback/myapp/node_modules/loopback-boot/lib/compiler.js:79:27)
at bootLoopBackApp (/work/node/loopback/myapp/node_modules/loopback-boot/index.js:128:22)
at Object.<anonymous> (/work/node/loopback/myapp/server/server.js:44:1)
at Module._compile (module.js:456:26)
[stu:/work/node/loopback/myapp (master)]$
This is my model-config.json
{
"_meta": {
"sources": [
"loopback/common/models",
"loopback/server/models",
"../common/models",
"./models",
"../node_modules/loopback-component-passport/lib/models"
]
},
"User": {
"dataSource": "db",
"public": true
},
"UserCredential": {
"dataSource": "db",
"public": true
},
"UserIdentity": {
"dataSource": "db",
"public": true
},
"AccessToken": {
"dataSource": "db",
"public": false
},
"ACL": {
"dataSource": "db",
"public": false
},
"RoleMapping": {
"dataSource": "db",
"public": false
},
"Role": {
"dataSource": "db",
"public": false
},
"KnowBit": {
"dataSource": "localpg",
"public": true
}
}
By enabling debug with loopback:boot:compiler
$ DEBUG=loopback:boot:compiler slc run
I'm able to get these logs:
[stu:/work/node/loopback/myapp (master)]$ DEBUG=loopback:boot:compiler slc run
INFO strong-agent API key not found, StrongOps dashboard reporting disabled.
Generate configuration with:
npm install -g strongloop
slc strongops
See http://docs.strongloop.com/strong-agent for more information.
supervisor running without clustering (unsupervised)
loopback:boot:compiler Found model "AccessToken" - ../node_modules/loopback/common/models/access-token.json ../node_modules/loopback/common/models/access-token.js +0ms
loopback:boot:compiler Found model "ACL" - ../node_modules/loopback/common/models/acl.json ../node_modules/loopback/common/models/acl.js +1ms
loopback:boot:compiler Found model "Application" - ../node_modules/loopback/common/models/application.json ../node_modules/loopback/common/models/application.js +0ms
loopback:boot:compiler Found model "Change" - ../node_modules/loopback/common/models/change.json ../node_modules/loopback/common/models/change.js +1ms
loopback:boot:compiler Found model "Checkpoint" - ../node_modules/loopback/common/models/checkpoint.json ../node_modules/loopback/common/models/checkpoint.js +0ms
loopback:boot:compiler Found model "Email" - ../node_modules/loopback/common/models/email.json ../node_modules/loopback/common/models/email.js +0ms
loopback:boot:compiler Found model "RoleMapping" - ../node_modules/loopback/common/models/role-mapping.json ../node_modules/loopback/common/models/role-mapping.js +0ms
loopback:boot:compiler Found model "Role" - ../node_modules/loopback/common/models/role.json ../node_modules/loopback/common/models/role.js +1ms
loopback:boot:compiler Found model "Scope" - ../node_modules/loopback/common/models/scope.json ../node_modules/loopback/common/models/scope.js +0ms
loopback:boot:compiler Found model "User" - ../node_modules/loopback/common/models/user.json ../node_modules/loopback/common/models/user.js +0ms
loopback:boot:compiler Skipping unknown module source dir "loopback/server/models" +0ms
loopback:boot:compiler Found model "KnowBit" - ../common/models/know-bit.json ../common/models/know-bit.js +1ms
loopback:boot:compiler Model source code not found: undefined - TypeError: Arguments to path.join must be strings +0ms
loopback:boot:compiler Found model "User" - ../common/models/user.json (no source file) +0ms
loopback:boot:compiler Skipping unknown module source dir "./models" +0ms
loopback:boot:compiler Found model "ApplicationCredential" - ../node_modules/loopback-component-passport/lib/models/application-credential.json ../node_modules/loopback-component-passport/lib/models/application-credential.js +1ms
loopback:boot:compiler Found model "UserCredential" - ../node_modules/loopback-component-passport/lib/models/user-credential.json ../node_modules/loopback-component-passport/lib/models/user-credential.js +0ms
loopback:boot:compiler Found model "UserIdentity" - ../node_modules/loopback-component-passport/lib/models/user-identity.json ../node_modules/loopback-component-passport/lib/models/user-identity.js +0ms
loopback:boot:compiler Using model "User"
Configuration: {"dataSource":"db","public":true}
Definition {"name":"User","plural":"users","base":"User","relations":{"accessTokens":{"type":"hasMany","model":"AccessToken","foreignKey":"userId"},"identities":{"type":"hasMany","model":"UserIdentity","foreignKey":"userId"},"credentials":{"type":"hasMany","model":"UserCredential","foreignKey":"userId"}},"validations":[],"acls":[],"methods":[]} +0ms
loopback:boot:compiler Using model "UserCredential"
Configuration: {"dataSource":"db","public":true}
Definition {"name":"UserCredential","base":"PersistedModel","properties":{"provider":{"type":"String","comments":"facebook, google, twitter, linkedin"},"authScheme":{"type":"String","comments":"oAuth, oAuth 2.0, OpenID, OpenID Connect"},"externalId":{"type":"String","comments":"The provider specific id"},"profile":{"type":"Object"},"credentials":{"type":"Object"},"created":"Date","modified":"Date","id":{"id":1,"generated":true}},"acls":[{"principalType":"ROLE","principalId":"$everyone","permission":"DENY"},{"principalType":"ROLE","principalId":"$owner","permission":"ALLOW"}],"relations":{"user":{"type":"belongsTo","model":"User","foreignKey":"userId"}}} +1ms
loopback:boot:compiler Using model "UserIdentity"
Configuration: {"dataSource":"db","public":true}
Definition {"name":"UserIdentity","plural":"UserIdentities","base":"PersistedModel","properties":{"provider":{"type":"String","comments":"facebook, google, twitter, linkedin"},"authScheme":{"type":"String","comments":"oAuth, oAuth 2.0, OpenID, OpenID Connect"},"externalId":{"type":"String","comments":"The provider specific id"},"profile":{"type":"Object"},"credentials":{"type":"Object"},"created":"Date","modified":"Date","id":{"id":1,"generated":true}},"acls":[{"principalType":"ROLE","principalId":"$everyone","permission":"DENY"},{"principalType":"ROLE","principalId":"$owner","permission":"ALLOW"}],"relations":{"user":{"type":"belongsTo","model":"User","foreignKey":"userId"}}} +0ms
loopback:boot:compiler Using model "AccessToken"
Configuration: {"dataSource":"db","public":false}
Definition {"name":"AccessToken","properties":{"id":{"type":"string","id":true},"ttl":{"type":"number","ttl":true,"default":1209600,"description":"time to live in seconds (2 weeks by default)"},"created":{"type":"Date"}},"relations":{"user":{"type":"belongsTo","model":"User","foreignKey":"userId"}},"acls":[{"principalType":"ROLE","principalId":"$everyone","permission":"DENY"},{"principalType":"ROLE","principalId":"$everyone","property":"create","permission":"ALLOW"}]} +0ms
loopback:boot:compiler Using model "ACL"
Configuration: {"dataSource":"db","public":false}
Definition {"name":"ACL","properties":{"model":{"type":"string","description":"The name of the model"},"property":{"type":"string","description":"The name of the property, method, scope, or relation"},"accessType":"string","permission":"string","principalType":"string","principalId":"string","id":{"id":1,"generated":true}}} +0ms
loopback:boot:compiler Using model "RoleMapping"
Configuration: {"dataSource":"db","public":false}
Definition {"name":"RoleMapping","description":"Map principals to roles","properties":{"id":{"type":"string","id":true,"generated":true},"principalType":{"type":"string","description":"The principal type, such as user, application, or role"},"principalId":"string"},"relations":{"role":{"type":"belongsTo","model":"Role","foreignKey":"roleId"}}} +0ms
loopback:boot:compiler Using model "Role"
Configuration: {"dataSource":"db","public":false}
Definition {"name":"Role","properties":{"id":{"type":"string","id":true,"generated":true},"name":{"type":"string","required":true},"description":"string","created":"date","modified":"date"},"relations":{"principals":{"type":"hasMany","model":"RoleMapping","foreignKey":"roleId"}}} +0ms
loopback:boot:compiler Using model "KnowBit"
Configuration: {"dataSource":"localpg","public":true}
Definition {"name":"KnowBit","base":"PersistedModel","idInjection":true,"properties":{"label":{"type":"string","required":true},"link":{"type":"string","required":true}},"validations":[],"relations":{},"acls":[],"methods":[]} +0ms

TL;DR
I had specified the same string "User" for the name and base attributes, to fix the problem I changed the name value to lowercase "user"
Full story
ok so when I copied the
loopback-example-passport/common/models/user.json
file I mistyped and set the name='User' instead of 'user'
This did not fix the ultimate problem, but I think it explains the Cyclic dependency. The 'base' is the 'base' class of this model and I had that set to 'User' as well
This allows me to get past the model compilation stage but still leaves me without a configured relationship from 'user' to 'UserIdentity' which the passport code requires.
I think I'm on the right track though, the insight being that the loopback models in the strongloop/loopback-example-passport example are Overriding the built in classes with new model classes where the first letter of the class name is lower cased for example userCredential is the override for the built in model class UserCredential and so on, this is a bit confusing at first
{
"name": "userCredential",
"plural": "userCredentials",
"base": "UserCredential",
...
But I think if I go through and check that I'm using the lower case versions things might work out
This was indeed the problem

Related

Issue with Keycloak and nestjs

I have been trying to include Keycloak authentication with my NestJS app and this is driving me crazy. I keep getting an error
"WARN [Keycloak] Cannot validate access token: Error: Grant validation failed. Reason: failed to load public key to verify token. Reason: connect ECONNREFUSED ::1:8080"
My Keycloak.json file is:
{
"realm": "my-realm",
"auth-server-url": "http://localhost:8080/",
"ssl-required": "external",
"resource": "test",
"verify-token-audience": false,
"credentials": {
"secret": "my-secret"
},
"policy-enforcer": {}
}
This is being imported in Apps.module.ts as:
KeycloakConnectModule.register('./dist/keycloak.json', {
policyEnforcement: PolicyEnforcementMode.PERMISSIVE,
tokenValidation: TokenValidation.ONLINE,
}),
I am using Keycloak version 19.0.1 and nest-key cloak-connect v 1.9.0.
When I tried debugging. Grant-manager.js's public key is undefined. I checked with the well-known config and jwks-uri was defined as:
http://localhost:8080/realms/my-realm/protocol/openid-connect/certs
Any ideas on what might be wrong?

Why Botium CLI fails extracting utterances data from DialogFlow agent?

After setting up Botium and connecting it to my DialogFlow agent (with a DialogFlow Admin Service Account on Google Cloud Platform), I tried to extract the data (intents and utterances) from DialogFlow automatically with the command botium-cli nlpextract. This is the error I got:
user#ubuntu20:~/workspace$ export GOOGLE_APPLICATION_CREDENTIALS="/path/to/google_key.json"
user#ubuntu20:~/workspace$ botium-cli nlpextract --config botium.json --convos dataset_botium --verbose
botium-cli Using Botium configuration file botium.json +0ms
botium-cli-nlp command options: { _: [ 'nlpextract' ],
botium-cli-nlp config: 'botium.json',
botium-cli-nlp c: 'botium.json',
botium-cli-nlp convos: [ 'dataset_botium' ],
botium-cli-nlp C: [ 'dataset_botium' ],
botium-cli-nlp verbose: true,
botium-cli-nlp v: true,
botium-cli-nlp '$0': '/usr/local/bin/botium-cli' } +0ms
(...)
botium-connector-PluginConnectorContainer-helper Botium plugin loaded from /usr/local/lib/node_modules/botium-cli/node_modules/botium-connector-dialogflow/index.js +0ms
botium-connector-dialogflow Validate called +0ms
botium-connector-BaseContainer _RunCustomHook onBuild finished +0ms
botium-connector-dialogflow Build called +1ms
botium-connector-dialogflow-nlp Dialogflow agent: {
botium-connector-dialogflow-nlp "parent": ####,
botium-connector-dialogflow-nlp "displayName": ####,
botium-connector-dialogflow-nlp "defaultLanguageCode": "it",
botium-connector-dialogflow-nlp "timeZone": "Europe/Kaliningrad",
botium-connector-dialogflow-nlp "enableLogging": true,
botium-connector-dialogflow-nlp "matchMode": "MATCH_MODE_HYBRID",
botium-connector-dialogflow-nlp "classificationThreshold": 0.30000001192092896,
botium-connector-dialogflow-nlp "apiVersion": "API_VERSION_V2",
botium-connector-dialogflow-nlp "tier": "TIER_STANDARD"
botium-connector-dialogflow-nlp } +0ms
botium-connector-dialogflow-nlp Utterances files not found for Default Fallback Intent, checking for utterances in intents/Default Fallback Intent_usersays_en-us.json and intents/Default Fallback Intent_usersays_it.json. Ignoring intent. +20ms
botium-connector-dialogflow Clean called +2s
botium-connector-BaseContainer _RunCustomHook onClean finished +2s
botium-connector-BaseContainer Cleanup rimrafing temp dir /home/user/workspace/botiumwork/agent-name 20200722 124913 wTImF +0ms
Failed to extract utterances: Cannot read property 'intents' of undefined
I can't find anything online about this. If I download the agent in a zip file, all the 21 intents which I inserted are available, each one with 85 training utterances.
(I am using Ubuntu20 on an Entroware Apollo laptop)
There was a bug in the Botium Dialogflow Connector. It is fixed now and will be included in the next release.
In the meantime, you can replace the latest release of the connector with the latest changes from the Github repository:
npm install codeforequity-at/botium-connector-dialogflow#master

TypeORM RepositoryNotFoundError when searching for entities using the class in Jest

I am having an issue here difficult to debug. I upgraded all my project dependencies and suddenly all my tests (Jest 25.5.4 or 26.x) started to fail with the "RepositoryNotFoundError".
The strange behavior is that all the entities are loaded in the metadata storage:
import { Connection, getMetadataArgsStorage } from 'typeorm';
let connection = await createConnection(); //<- The connection is creating according to my config
console.log(getMetadataArgsStorage()); //<- All the entities are here
console.log(getRepository('User')); //<- This works
console.log(getRepository(User)); //<- But this will raise the error
After some time debugging, I noticed the error is at https://github.com/typeorm/typeorm/blob/0.2.24/src/connection/Connection.ts#L482 and I created a repository for you to replicate the issue.
The comparison (metadata.target === target) always returns false. The targets are from the same class, but they are somewhat different. Using toString() return different versions of the class, one with comments stripped, another without comments (if I am using removeComments: true in my tsc config):
const targetMetadata = connection.entityMetadatas[7].target; // 7 is the index in my debug, it can be anything else in the array
console.log(targetMetadata === User); // prints false
I still did not figure out what caused the issue after the upgrade. Unfortunately I cannot share the code of the project, but I can give more information if you need. Could you help me to figure out what is the problem?
My jest config (in package.json):
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
I still haven't tried tinkering with the example repository you provided, but I see that the paths in your TypeORM configuration files start with dist/. If you're using ts-jest and also have set src as your rootDir I think that could be the cause of your trouble.

Mongoose validation failed, without giving the field-name with the issue

When I try to save my "Rating" schema, it gives the error
error: ValidationError: Rating validation failed
without giving the field name with the problem, neither the kind of issue (if it's about a casting, a null or missing value, or something else..)
In my schema I don't have any field "required", neither "index:true"... I just specify the types in this way:
{
ex_string_field : {type:String},
ex_number_field : {type:Number},
ex_array_field : [
{
sub_field_string : {type:String}
}
],
ex_object_field : {
sub_field_string : {type:String}
}
}
How can I find which is the field rising the error? I have many fields. Actually, without any required field, it should be pretty easy to validate an object. I guessed that if a field is not compliant, it's just ignored....
This is my system:
"mongoose": "^4.11.12",
"node" : "v8.1.3",
"npm" : "5.3.0"
And this is the full error stack track:
2017-12-5 - error: ValidationError: Rating validation failed
at ValidationError (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/mongoose/lib/error/validation.js:28:11)
at model.Document.invalidate (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/mongoose/lib/document.js:1658:32)
at model.Document.$set (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/mongoose/lib/document.js:760:10)
at model._handleIndex (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/mongoose/lib/document.js:590:14)
at model.Document.$set (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/mongoose/lib/document.js:550:24)
at model._handleIndex (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/mongoose/lib/document.js:574:12)
at model.Document.$set (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/mongoose/lib/document.js:550:24)
at model.Document (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/mongoose/lib/document.js:77:12)
at model.Model (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/mongoose/lib/model.js:55:12)
at new model (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/mongoose/lib/model.js:3885:13)
at /Volumes/HD Daniele/Sites/cashinvoice/server/server/routes/admin/admin-utils.js:370:13
at _fulfilled (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/q/q.js:854:54)
at self.promiseDispatch.done (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/q/q.js:883:30)
at Promise.promise.promiseDispatch (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/q/q.js:816:13)
at /Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/q/q.js:624:44
at runSingle (/Volumes/HD Daniele/Sites/cashinvoice/server/node_modules/q/q.js:137:13)
================== UPDATE ====================
After some testing, I ended up that the error is the complex field 'main_info'. In the specific, 2 types were swapped (a number as String, and a String as Number). They were defined as follow:
this.schema = new Schema({
main_info :
{
rea : {type:String},
name : {type:Number},
}
});
This is an example of passed data that rise the Validation Error:
main_info :
{
"rea": 649087,
"name": "NUTIS S.R.L."
}
Anyway I think that it's crazy manual parse the whole data to find which is the problem. Does Mongoose really not provide any further info about the validation fail?

Nodemailer with Gmail on Loopback error - Object #<Object> has no method 'getToken'

I am learning Loopback and I decided to make some email sending. I want to use gmail account.
I created remote method and configured datasources. Here is how it looks:
"myEmailDataSource": {
"name": "myEmailDataSource",
"connector": "mail",
"transports": [
{
"type": "smtp",
"host": "smtp.gmail.com",
"auth": {
"xoauth2": {
"user": "myMail#gmail.com",
"clientId": "myClientId.apps.googleusercontent.com",
"clientSecret": "mySecret",
"refreshToken": "myToken"
}
}
}
]
}
But when I want to send an email, it throws this error:
TypeError: Object #<Object> has no method 'getToken'
at SMTPConnection._handleXOauth2Token (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/src/smtp-connection.js:961:67)
at SMTPConnection.login (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/src/smtp-connection.js:233:18)
at SMTPTransport.<anonymous> (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/src/smtp-transport.js:96:24)
at SMTPConnection.g (events.js:180:16)
at SMTPConnection.EventEmitter.emit (events.js:92:17)
at SMTPConnection._actionEHLO (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/src/smtp-connection.js:692:10)
at SMTPConnection._processResponse (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/src/smtp-connection.js:511:16)
at SMTPConnection._onData (/home/arth95/Projects/firstCMS/node_modules/loopback/node_modules/nodemailer/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/src/smtp-connection.js:357:10)
at CleartextStream.EventEmitter.emit (events.js:95:17)
at CleartextStream.<anonymous> (_stream_readable.js:746:14)
Why is that?
I had exact same problem. Did you find any solution for this?
As a workaround I've done following.
create a boot script in server\boot
in the script wrote following code
var email = app.models.Email;
var auth = email.dataSource.connector.transports[0].transporter.options.auth;
auth.xoauth2 = require('xoauth2').createXOAuth2Generator(auth.xoauth2);
This converts the xoauth2 object that you defined in data source to XOAuth2Generator object that is needed by nodemailer.
You need to have xoauth2 module installed.
There should be a better way to handle this. But so far I've not found it, so using this workaround.

Resources