Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Jan 31 2019 16:43
    uniconstructor edited #4126
  • Jan 31 2019 16:41
    uniconstructor edited #4126
  • Jan 31 2019 16:41
    uniconstructor opened #4126
  • Jan 31 2019 03:44
    dzungh0ang closed #4123
  • Jan 30 2019 00:25
    techaks closed #4125
  • Jan 30 2019 00:02
    techaks edited #4125
  • Jan 30 2019 00:01
    techaks opened #4125
  • Jan 29 2019 23:33
    jkpulido opened #4124
  • Jan 29 2019 13:32
    nabdelgadir closed #4114
  • Jan 29 2019 09:17

    bajtos on add-node10

    (compare)

  • Jan 29 2019 07:13
    dzungh0ang opened #4123
  • Jan 29 2019 04:25
    dhmlau closed #3881
  • Jan 28 2019 13:14
    bajtos edited #4119
  • Jan 28 2019 12:47
    bajtos labeled #4121
  • Jan 28 2019 12:46
    bajtos labeled #4118
  • Jan 28 2019 12:46
    bajtos labeled #4118
  • Jan 28 2019 12:46
    bajtos labeled #4118
  • Jan 28 2019 12:46
    bajtos assigned #4118
  • Jan 25 2019 19:50
    dhmlau closed #3906
  • Jan 25 2019 19:13
    mschnee synchronize #4119
Rifa Achrinza
@achrinza

@lamtrinhdev_gitlab Thanks to the flexibility of LoopBack 4, It's possible to mutate the results of the repository functions (e.g. .count() and .find()) and then return that mutation as the response in the controller.

You'll also need to...

  1. Modify the expected Typescript return type for that controller function
  2. (Optional, but strongly recommended) Mutate the output of getModelSchemaRef inside the decorator (e.g. get()) to ensure the correct OpenAPI spec is generated to reflect the new response.

For the 2nd point, this is strongly recommended so as to ensure OpenAPI client generators and other OpenAPI Spec consumers are not misled to assuming the API is returning something different. Though LoopBack 4 currently only validates client requests and does not validate the server response against the OpenAPI spec.

@claudiothewall There seems to be 3 questions, do correct me if I misunderstood:

Do I need to use @authenticate on every controller method?

From my understanding, yes. Though it might be a good idea to open a new question issue on the GitHub repo to get clarification.

Do I need to manually map the matrix in @authorize myself, even for standard operations?

AFAIK, yes: you'll need to use the @authorize decorator and map the matrix yourself. Due to the flexibility of LoopBack 4, the Authorization component will not be able figure out if you're using a standard CRUD repository operation.

For example, a create() controller function may be actually calling the repository findById() function. Therefore, it's not possible to infer the actual intent of a controller function.

Do I need to store the matrix in every controller Typescript file?

LoopBack 4 gives the developers liberty on how to store and manage this matrix. As long valid parameters are passed into @authorize.

You can store this matrix in a dedicated file and then import it into the different controllers. Or you can use LoopBack's dependency injection system and store it in a binding key. So the best way to store this matrix is dependent on your requirements.

Fr4nZ82
@Fr4nZ82
Hello everyone. A question: services registered with app.service, when injected into controller are new instaces or are singleton? Another question: best pratice to implement a global heartbeat service that expose an rxjs observable to clean, for example, old temporary tokens or temporary whitelisted mail address?
meet02
@meet02

Hello every one

I have data in monogdb look like this

"value": {
"EN": "Please write to us for Queries",
"UR": "سوالات کے لئے ہم پر لکھ کریں"
},

My model like

@property({
type: "object",
required: true,
default: ""
})
value: Object;

And I am trying to get value of EN in filed

{
"where": {
"additionalProp1": {}
},
"fields": {
"_id": true,
"language_id": true,
"title": true,
"code": true,
"created_at": true,
"updated_at": true,
"status": true,
"additionalProp1": {}
},
"offset": 0,
"limit": 100,
"skip": 0,
"order": [
"string"
]
}

So how can Get value.EN in filed

Rifa Achrinza
@achrinza

@Fr4nZ82 AFAIK, app.service() uses BindingScope.SINGLETON.

I'm not too sure I follow your 2nd question. Could you clarify?

@meet02 This may be better-suited as a new question issue with a reproduction sandbox so that we can more quickly diagnose the issue.

meet02
@meet02
Hi
@achrinza hi can u help me
Diana Lau
@dhmlau
@meet02, as @achrinza mentioned, it might be the best to submit a question to loopback-next repo with a sample repo for us to try. Thanks!

@claudiothewall just wanna clarify @achrinza's comment above:

Do I need to use @authenticate on every controller method?

You can use the @authenticate on every method and also at the controller class level. If you set on the class level and want to skip particular method, can use @authentication.skip()

meet02
@meet02
Field, get perticulat key of object in field #5019
I have created new issue, please help me @dhmlau
Fr4nZ82
@Fr4nZ82

@achrinza I found the solution, thanks anyway: in the services/my-service.service.ts

@bind({scope: BindingScope.SINGLETON, tags: 'MYSERVICE'})
export class MyService {
  private heartbeat: Subject<number>
  constructor() {
    this.heartbeat = new Subject()
    interval(10000).pipe(map(() => Date.now())).subscribe(this.heartbeat)
  }
  get heartbeat$(){ return this.heartbeat }
}

this signleton service expose an interval Subject to which I can subscribe from other services or classes:

export class MyOtherService{
  constructor(@service(MyService) public myService: Myservice) {
    this.myService.heartbeat$.subscribe( date => this.doSomething() )
  }

For example I use it to check if a "reset password" link has expired and in that case I delete it

Diana Lau
@dhmlau
Rifa Achrinza
@achrinza

Thanks @dhmlau for the clarification!

@Fr4nZ82 Glad you were able to find a solution; Also thanks for sharing with others!

elloGuy69
@elloGuy69
has anyone used token refresh with lb4? does it even exist as of now?
Fr4nZ82
@Fr4nZ82
i implement it by myself
elloGuy69
@elloGuy69
@Fr4nZ82 could you please explain how ? :)
Fr4nZ82
@Fr4nZ82
is more a frontend task... in the backend is simple...
elloGuy69
@elloGuy69
timer? could you share a sample code?
or point me in the right direction?
Fr4nZ82
@Fr4nZ82

in the backand part i also check if the token contains right info about the user, because the admin can change roles or level and the token must be updated

@post('/users/renewjwtoken', {
    security: OPERATION_SECURITY_SPEC,
    responses: {
      '200': {
        description: 'The new token',
        content: {
          'application/json': {
            schema: {JWToken: 'string'},
          },
        },
      },
    },
  })
  @authenticate('jwt')
  async renewToken(
    @inject(SecurityBindings.USER) currentUserProfile: UserProfile,
  ): Promise<{JWToken?: string}> {

    let user = await this.userRepository.findById(currentUserProfile.id)
    let userData = await this.getFullUserData(user as UserSchema)

    /*
      user is the actual user information memorized in the token, userData are the actual user information in the database;
      if they do not match a new token is needed
    */
    let JWToken: string
    if (
      user.level != userData.level ||
      !_.isEqual(_.sortBy(user.roles), _.sortBy(userData.roles))
    ) {
      let updatedUser = _.pick(userData, ['id', 'email', 'roles', 'level']) as User
      JWToken = await this.jwtService.generateToken(this.userService.convertToUserProfile(updatedUser))
    } else JWToken = await this.jwtService.generateToken(this.userService.convertToUserProfile(user))

    return {JWToken}
  }

to only get a new token only generate a new one and send to the frontend

elloGuy69
@elloGuy69
@Fr4nZ82 awesome thanks but whats the front-end logic?
for asking for refreshing token before its expired or after its expired
Fr4nZ82
@Fr4nZ82

i use angular 8 as frontend... this is on the authentication service constructor

setRenewalInterval(exp:number,iat:number){
        interval(10000).pipe(takeUntil(this.stopInterval), tap(n=>{
            let jwtokenDuration = exp - iat
            let datenowsec = Date.now() / 1000
            let remaining = exp - datenowsec
            let remainingP = remaining / jwtokenDuration
            this.ls.log(['authentication'],'JWToken renewal interval; tokenDuration: '+jwtokenDuration+' remaining: '+remaining+' remainingP: '+remainingP)
            //this.renewJWToken method is launched from the interceptor!
            if( remainingP < 0.333 ){
                this.stopInterval.next()
                this.getNewJWToken()
                .subscribe(res => {
                    this.ls.log(['authentication'],'JW TOKEN RENEWED, res:',JSON.stringify(res))
                    if(res.JWToken && res.JWToken.length > 0) this.renewToken(res.JWToken)
                })
            }
        })).subscribe()
    }

and the interceptor:

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
    isLogin=false
    constructor(
        private authenticationService: AuthenticationService,
        private ls:LoggingService
    ) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if(request.url.startsWith('http')) this.ls.log(['interceptors'],'request intercepted by JWT interceptor: ',JSON.stringify(request))
        if(request.url.endsWith('/users/login')) this.isLogin = true
        else this.isLogin = false
        // add authorization header with JWToken if available
        let currentUserJWToken = this.authenticationService.currentUserJWToken;
        if (currentUserJWToken) {
            request = request.clone({
                setHeaders: {
                    Authorization: `Bearer ${currentUserJWToken}`
                }
            });
        }
        //intercept response and renew JWToken if present
        return next.handle(request).pipe(
            map( event => {
                if (!this.isLogin && event instanceof HttpResponse && event.body && event.body.JWToken && event.body.JWToken.length > 0){
                    this.authenticationService.renewToken(event.body.JWToken)
                }
                this.ls.log(['interceptors'],'JWT interceptor handle event:',JSON.stringify(event))
                return event
            })
        )
    }
}
not sure if this work atm... :D i'm at work at another part
elloGuy69
@elloGuy69
nice thank you so much. essentially you check at regular interval if token is about to expire and then ask for new token. Am I correct?
Fr4nZ82
@Fr4nZ82
yes
att 33%
elloGuy69
@elloGuy69
Gotcha. Genius :)
Thanks again for the code
A11st0r
@A11st0r
Hi, can I ask a question about loopback 3 here?
A11st0r
@A11st0r
I want to create a file object type and use it in a model. it should have a fileURL and a fileDescription property. then I want to use it in on the model properties.
images: {
type: '[File]'
}
is this possible?
MwSpace LLC
@MwSpaceLLC

guys if you want to share a starter we left a package with basic email template and sms authentication 🤣🤣🤣🤣

https://github.com/MwSpaceLLC/lb4-starter

Fr4nZ82
@Fr4nZ82
there is a reference for the mongodb connector? Where things like this are documented?
const w = new WhereBuilder<TokenLink>().and({expires: {gt: 1}}, {expires: {lte: now}}).build()
    return this.tkr.deleteAll(w)
Rifa Achrinza
@achrinza

@Fr4nZ82 Filter builders are currently not documented in the traditional sense; I believe it's currently being tracked by strongloop/loopback-next#1973

But there are API docs:

https://loopback.io/doc/en/lb4/apidocs.repository.filterbuilder.html
https://loopback.io/doc/en/lb4/apidocs.repository.wherebuilder.html

@MwSpaceLLC Thanks for sharing!

sai-honeysys
@lakshmansai1980
how can in include nested object property in filter fields
I don't find any way to include nested object property in Filter in loopback 4, can somebody share lights
Rifa Achrinza
@achrinza

@lakshmansai1980 Nested properties can be targeted using the dot-notation (e.g. “user.email”)

The “Working with Data” docs are currently being migrated from LoopBack 3 to 4 (see: strongloop/loopback-next#4970)

In the mean time, we can reference the LoopBack 3 docs:
https://loopback.io/doc/en/lb3/Querying-data.html#filtering-nested-properties

sai-honeysys
@lakshmansai1980
Thanks @achrinza for quick response. It does't looks to be working. When I provide {fields: {user.email : true}} it is giving issue.
Rifa Achrinza
@achrinza
@lakshmansai1980 It should be a string:
{
  fields: {
    “user.email”: true
  }
}
sai-honeysys
@lakshmansai1980
I tried all combinations .Nothing works out .. Here is my sample model

@property({
type: 'string',
id: true,
generated: true,
})
id?: string;

@property({
type: 'string',
})
name?: string;

@property({
type: MediaType,
})
webMedia?: MediaType;

MediaType :

@property({
type: 'string',
})
type?: string;

@property({
type: 'string',
})
media?: string;

{fields: {"id": true, "name": true, "webMedia.media" : true}}
sai-honeysys
@lakshmansai1980
this is giving trouble
Rifa Achrinza
@achrinza

@lakshmansai1980 Thanks for providing a sample model;

Unfortunately this would be difficult to debug via chat. Could you create a new "question" issue and provide a link to a GitHub repo with a minimum-reproduction sandbox? This would allow the other maintainers to provide assistance on the issue.

Providing a reproduction sandbox in the issue would help speed up debugging the issue.

meet02
@meet02
Hello how can i create custom primary_key in loopback 4
Rifa Achrinza
@achrinza

@meet02, what kind of custom primary key would you like to create?

LoopBack 4 allows you to define the name, type and type of a primary key, and also a define composite key.

sai-honeysys
@lakshmansai1980
I was trying with latest loopback 4 (2.0.2) to include relation (hasMany, belongs to), that does't looks to be working .. https://loopback.io/doc/en/lb4/HasMany-relation.html#configuring-a-hasmany-relation. Can somebody share light .. Do I need to add strictObjectIDCoercion as true
sai-honeysys
@lakshmansai1980
If I include strictObjectIDCoercion to true in model settings, it is able to resolve relation.. but findById is not getting any results .. If I set strictObjectIDCoercion to false, findbyId is working, but relations are not working .. This is blocking and unable to proceed further .. Can somebody help ..
Rifa Achrinza
@achrinza

@lakshmansai1980 It is strongly recommended to use the lb4 relation command to generate the relations. Unfortunately, without a minimum reproduction sandbox, we can't easily debug the issue as there's too many variables involved.

With the MongoDB connector, strictObjectIDCoercion should be consistently used or not used to ensure that LoopBack can resolve ObjectID

If there's any issues with using lb4 relation, please create a new "question" issue with a link to a GitHub repo containing a minimum reproduction sandbox. This will allow the other maintainers (who may be more versed in MongoDB than me) to look into it and check if this may be a bug.

More information on strictObjectIDCoercion can be found on the docs: https://loopback.io/doc/en/lb4/MongoDB-connector.html#strictobjectidcoercion-flag