Loopback4 with Mongodb authentification status code 500 error - mongodb

i have been struggling with Loopback4 for days...Lb3 was so much easier...
my goal is to authenticate all of my routes except #get route.
configuration went well and when i tried to get route i got a message that route is authenticated, so i have put authenticate.skip(), and then got 500 status code:
ResolutionError: The key 'dataSources.mongoDS' is not bound to any value in context
It's probably a simple solution but i cannot find it...
my code is:
i have imported components in application.ts
this.component(AuthenticationComponent)
this.component(JWTAuthenticationComponent)
this.dataSource(MongoDsDataSource, UserServiceBindings.DATASOURCE_NAME)
this.bind(UserServiceBindings.USER_SERVICE).toClass(MyUserService);
this is my datasource code:
const config = {
name: 'mongoDS',
connector: 'mongodb',
url: '',
host: 'localhost',
port: 27017,
user: '',
password: '',
database: 'ropesdb',
useNewUrlParser: true
};
#lifeCycleObserver('datasource')
export class MongoDsDataSource extends juggler.DataSource
implements LifeCycleObserver {
static dataSourceName = 'mongoDS';
static readonly defaultConfig = config;
constructor(
#inject('dataSources.config.mongoDS', {optional: true})
dsConfig: object = config,
) {
super(dsConfig);
}
}
and this is my repository code:
export class RopeRepository extends DefaultCrudRepository<
Rope,
typeof Rope.prototype.id,
RopeRelations
> {
constructor(
#inject('dataSources.mongoDS') dataSource: MongoDsDataSource,
) {
super(Rope, dataSource);
}
}

I believe LoopBack uses the datasource filename to determine the name of the datasource and place the name in the context. For example, [name-in-kebab-case].datasource.ts will be datasource.NameInCamelCase in the context. Is MongoDsDataSource placed in mongo-ds.datasource.ts?

Related

How to connect PostgreSQL Database to Cypress 10+ , no pg_hba.conf entry error

When I try to connect and query on my PostgreSQL database, I keep getting a cypress error: “no pg_hba.conf entry for host “ user “postgres”, database “postgres”, SSL off”. How do I solve the error. Is this an issue with code or with the database?
this is my cypres.config.js file:
const pg = require("pg")
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on("task", {
//create a task - take two parameters - first being a config and second is sql
READFROMDB({ dbConfig, sql }) {
//create a client using the config argument
const client = new pg.Pool(dbConfig)
//return thw result from sql
return client.query(sql)
}
})
},
DB:{
database: "<name>",
user: "<name>",
password: "<pw>",
host: "<hostname>.amazonaws.com", //not localhost
port: 5432,
dialect: "postgres",
dialectOptions: {
ssl: {
require: true, // This will help you. But you will see nwe error
rejectUnauthorized: false // This line will fix new error
}
}
}
}
})
This is my test:
cy.task("READFROMDB",{
//get config from db
dbConfig: Cypress.config('DB'),
//SQL we want to perform
sql: 'select * from user where user=\'Phoebe\''
}).then((result)=>{
console.log(result.rows)
})

naming and getting a particular connection in typeorm

Previously, typeorm seems to have allowed the use of a ConnectionManager that seems to have allowed to name a particular connection whilst creation and fetch/check the connection with the same name like this:
import { Connection, createConnection, getConnectionManager } from 'typeorm';
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions';
import * as tenantsOrmconfig from '../../tenants-orm.config';
export function getTenantConnection(tenantId: string): Promise<Connection> {
const connectionName = `tenant_${tenantId}`;
const connectionManager = getConnectionManager();
if (connectionManager.has(connectionName)) {
const connection = connectionManager.get(connectionName);
return Promise.resolve(connection.isConnected ? connection : connection.connect());
}
return createConnection({
...(tenantsOrmconfig as PostgresConnectionOptions),
name: connectionName,
schema: connectionName,
});
}
But, I'm confused now as to how to achieve this when I'm using a Datasource using a factory to create dynamic connection:
#Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => {
const host = configService.get('MY_DB_HOST');
const port = parseInt(configService.get('MY_DB_PORT'));
const user = configService.get('MY_DB_USER');
const password = configService.get('MY_DB_PASSWORD');
const dbName = configService.get('MY_DB_NAME');
return {
type: 'postgres',
host: host,
port: port,
username: user,
password: password,
database: dbName,
entities: entities,
synchronize: true,
};
}
})
],
controllers: [AppController],
providers: [AppService],
})
I'm trying to fetch an existing connection from the connection pool which typeorm is supposed to manage by default if it already exists, and that connection is supposed to belong to a tenant if it exists, else it should create a new connection for the tenant. How do I do this with a Datasource?
I can see that there is a datasourceInstance.manager.find() method, but, that seems to take an Entity which seems to query the database, and I can't find anything more useful in the docs.

TypeORM doesn't support the database setting on the entity decorator

I'm trying to seperate my TypeORM project over multiple databases as it is growing in size, and its components are very discrete(yet interlinked, so i need to be able to have relations cross-database).
I am trying to do that using the database setting on the #Entity decorator, as described here: https://typeorm.io/#multiple-connections/using-multiple-databases-in-a-single-connection
I made a minimal reproducable example for this, with two entities that should in theory be put in different databases:
#Entity({ database: 'test' })
export default class Entity1 {
#PrimaryGeneratedColumn()
id?: number
#Column()
name?: string
#Column()
address?: string
}
and
#Entity({ database: 'database2' })
export default class Entity2 {
#PrimaryGeneratedColumn()
id?: number
#Column()
name?: string
#Column()
address?: string
}
Connection code:
import {createConnections} from "typeorm";
async function doDbExample() {
const connections = await createConnections([{
name: "db1Connection",
type: "postgres",
host: "db",
port: 5432,
username: "test",
password: "testPassword",
database: "test",
entities: [__dirname + "/entity/*{.js,.ts}"],
synchronize: true
}]);
console.log("Created connections")
}
doDbExample()
However, what happens is that both entities' table is put in the database of the connection. Am i doing something wrong, or is this a bug in TypeORM? It looks to me like it is not respecting the database setting any more.
I am running the code using ts-node-dev
I made a full minimal reproducable example, complete with dockerized setup of the database environment, on github: https://github.com/petterroea/TypeOrmBug-MRE
This is a setup issue. I solved it like this:
Modify the array entities so each connection/database has its own folder with entity files and name the entity you use the most as default:
// src/index.ts
await createConnections([
{
name: 'default',
host: 'SERVER1',
username: 'bob',
password: 'kiwi,
type: 'mssql',
database: 'db1',
...
"synchronize": true,
"entities": ["src/db1/entity/**/*.ts"],
},
{
name: 'connection2,
host: 'SERVER2',
username: 'Mike',
password: 'carrot',
type: 'mssql',
database: 'db2,
...
"synchronize": true,
"entities": ["src/db2/entity/**/*.ts"],
])
Create entity files for each database in its respective folder:
src/db1/entity/Fruit.ts > table in db1
src/db2/entity/Vegetables.ts > table in db2
With "synchronize": true each table will be created automatically in the correct database
Accessing data in the tables:
For the default connection::
import { Fruit} from 'src/db1/entity/Fruit.ts'
fruits() {
return Fruit.find()
}
For the non default connection:
import { getRepository } from 'typeorm'
import { Vegetable} from 'src/db2/entity/Vegetable.ts'
vegetables() {
return async () => await getRepository(Vegetable).find()
}
or
async vegetables() {
return await getRepository(vegetables, 'connection2').find()
}
I hope this helps someone else struggling with the same issues as you and me.

Angular 2 Passing object between routes

Trying to find the answer on how to pass a common object between routes.
I have a service which store socket.io
I need to have access to that socket service across my whole site so I can listen for emits.
I have a route.ts file and I am not sure how to initialise a socket service in the root then pass it to the route when needed.
I have read the docs and I am trying to use data like below in my route.ts file:
const routes: RouterConfig = [
{ path: '', component: AppComponent },
{ path: 'function1', component: Function1Component, data: { socket: socket }},
];
However I dont know where to declare the socket service.
Thanks
In your case you are trying to pass some function reference(which is going to return object/promise/observable) from data option of route, so that would not work by passing it in data option because it does stringify the data when you ask for data by doing this.routerData.get('socket').
I strongly recommend to use resolve option of route here. resolve method would return promise/observable
Code
#Injectable()
class SocketResolver implements Resolve {
constructor(private socketService: socketService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<any> {
return this.socketService.connection();
}
}
const routes: RouterConfig = [
{ path: '', component: AppComponent },
{ path: 'function1', component: Function1Component,
resolve: { socket: SocketResolver }
}
];
Doc Link
you should not involve routing in this.
just create a service: https://angular.io/docs/ts/latest/tutorial/toh-pt4.html
SocketService.ts
import { Injectable } from '#angular/core';
#Injectable()
export class SocketService {
...
}
and inject that service in your component or other services via the constructor:
constructor(private socketService:SocketService) { }
plus you make sure your service is intialized in the bootstrap process:
bootstrap(AppComponent, [
SocketService,
... ]);
on bootstrap, angular will create a single instance of your service which will be injected into every component which has it in its constructor, therefore you have access to the same socket service from everywhere you want.

Custom authenticator with Ember simple auth + Ember CLI

I'm trying to write a custom authenticator, similar to the one from this example in the docs. The goal is to be able to retrieve the currently logged in user via session.user.
I'm using Ember CLI, so in initializers/authentication.js I have
import Ember from 'ember';
var customAuthenticator = Ember.SimpleAuth.Authenticators.Devise.extend({
authenticate: function(credentials) {
debugger;
}
});
export default {
name: 'authentication',
initialize: function(container, application) {
Ember.SimpleAuth.Session.reopen({
user: function() {
var userId = this.get('user_id');
if (!Ember.isEmpty(userId)) {
return container.lookup('store:main').find('user', userId);
}
}.property('userId')
});
// register the custom authenticator so the session can find it
container.register('authenticator:custom', customAuthenticator);
Ember.SimpleAuth.setup(container, application, {
routeAfterAuthentication: 'landing-pages',
authorizerFactory: 'ember-simple-auth-authorizer:devise'
});
}
};
When I try to authenticate, I get the following error:
TypeError: Cannot read property 'authenticate' of undefined
at __exports__.default.Ember.ObjectProxy.extend.authenticate
Any idea why?
As of Simple Auth 0.6.4, you can now do something like:
index.html:
window.ENV['simple-auth'] = {
authorizer: 'simple-auth-authorizer:devise',
session: 'session:withCurrentUser'
};
initializers/customize-session.js:
import Ember from 'ember';
import Session from 'simple-auth/session';
var SessionWithCurrentUser = Session.extend({
currentUser: function() {
var userId = this.get('user_id');
if (!Ember.isEmpty(userId)) {
return this.container.lookup('store:main').find('user', userId);
}
}.property('user_id')
});
export default {
name: 'customize-session',
initialize: function(container) {
container.register('session:withCurrentUser', SessionWithCurrentUser);
}
};
You would need to do something like this:
Em.SimpleAuth.Authenticators.OAuth2.reopen
serverTokenEndpoint: "http://myapp.com/token"
authenticate: (credentials) ->
new Em.RSVP.Promise (resolve, reject) =>
data =
grant_type: "password"
username: credentials.identification
password: credentials.password
#makeRequest(data).then (response) =>
# success call
, (xhr, status, error) ->
# fail call
What I think might be happening is that you are registering the authenticator with the application and not the authenticator itself?
The problem is that the AMD build does not currently automatically register the extension libraries' components (see https://github.com/simplabs/ember-simple-auth/issues/198). I'll change that in the next release and will probably also adopt the documentation to be more focussed on the AMD build instead of the browserified version. For the moment you'd have to run this in your initializer
container.register(
'ember-simple-auth-authorizer:devise',
Ember.SimpleAuth.Authorizers.Devise
);
container.register(
'ember-simple-auth-authenticator:devise',
Ember.SimpleAuth.Authenticators.Devise
);