Skip to content

Releases: boostercloud/booster

Fixed small bugs on read models

04 Apr 15:08
Compare
Choose a tag to compare

What's Changed

  1. Avoid creating projection if the read model join key is not valid
  2. Avoid sending an empty object to read the model when a snapshot doesn't exist

Full Changelog: v0.26.7...v0.26.8

Add Event API to Azure Provider

14 Mar 15:22
Compare
Choose a tag to compare

Description

This version add the event API to the Azure Provider

Code and data fields on GraphQL Error messages

23 Feb 12:57
Compare
Choose a tag to compare

Description

This version add code and data fields on GraphQL Error messages

Multiple JWT roles

06 Feb 20:32
Compare
Choose a tag to compare

Description

This version improves Booster's Custom Authentication by including the possibility to manage more than one JWT role.

Details

Until now, if we defined the rolesClaim field as: firebase:groups this field could only contain a text string. Example:

{
  "firebase:groups": "User",
  "iss": "https://securetoken.google.com/demoapp",
  "aud": "demoapp",
  "auth_time": "1604676721",
  "user_id": "xJYY5Y6fTbVggNtDjaNh7cNSBd7q1",
  "sub": "xJY5Y6fTbVggNtDjaNh7cNSBd7q1",
  "iat": 1604676721,
  "exp": "1604680321",
  "phone_number": "+99999999999",
  }
}

It is now possible to also indicate a list of values, such that, if any of the strings match any of the defined roles, the validation will be successful. Example:

{
  "firebase:groups": ["Readers", "Writters"],
  "iss": "https://securetoken.google.com/demoapp",
  "aud": "demoapp",
  "auth_time": "1604676721",
  "user_id": "xJYY5Y6fTbVggNtDjaNh7cNSBd7q1",
  "sub": "xJY5Y6fTbVggNtDjaNh7cNSBd7q1",
  "iat": 1604676721,
  "exp": "1604680321",
  "phone_number": "+99999999999",
  }
}

Execution context + JWT token custom validations

27 Jan 13:58
Compare
Choose a tag to compare

This new Booster version contains a context property inside the commands Register object. In that way, the user could log, intercept or validate at the command side the content of the context object.

@Command({
  authorize: 'all',
})
export class CreatePost {
  public constructor(
    readonly postId: UUID,
    readonly title: string,
    readonly content: string,
    readonly author: string
  ) {}

  public static async handle(command: CreatePost, register: Register): Promise<void> {
    console.log('Our awesome context', register.context)
    register.events(new PostCreated(command.postId, command.title, command.content, command.author))
  }
}

Also, we support a new extraValidation function inside the TokenVerifierConfig to perform custom JWT token validations which will be executed always after the JWT standard validations, expiration, issuers checks, and so on.

This is the new signature for TokenVerifierConfig:

export type TokenVerifierConfig = {
  issuer: string
  jwksUri?: string
  publicKey?: string
  rolesClaim?: string
  extraValidation?: (jwtToken: unknown, rawToken: string) => void
}

The extraValidation function will receive the decoded token jwtToken which includes the header, payload, and signature. Also, the raw token is provided for additional checks. This extraValidation function must throw an exception if any custom validation doesn't match.

Example config:

const configWithExtraValidation = new BoosterConfig('test with extra validation')
configWithExtraValidation.tokenVerifiers = [
  {
   issuer: 'auth0',
    jwksUri: 'https://myauth0app.auth0.com/.well-known/jwks.json',
    extraValidation: (jwtToken, _rawToken) => {

     if ((jwtToken.headers as any)?.alg !== 'RS512') {
        throw 'Invalid token encoding'
      }

      if ((jwtToken.payload as any)?.['custom:role'] !== 'Admin') {
        throw 'Unauthorized'
      }
    },
  },
]

Rockets 2.0 - Multi-provider Rockets

28 Dec 11:10
Compare
Choose a tag to compare

Rockets 2.0

Rockets 2.0 is here!

This new Booster version includes the new Rockets 2.0 functionality that allows you to create multi-providers Rockets.

A Multi-providers Rocket is a Rocket that includes an implementation for different vendors in the same Rocket. For example, you can create a Rocket to handle webhooks that works for Azure or Local Provider in the same Rocket.

This functionality supports the following providers:

  • Azure Provider
  • Local Provider

New improvement with a new Terraform provider

26 Nov 16:48
Compare
Choose a tag to compare

What's Changed

This release introduces a new Azure provider for Booster, which makes deploying your applications more reliable by using Terraform.

Thanks to this new provider, Azure supports will come close to the AWS provider sooner than expected πŸš€ πŸ’ͺ .

And, you can now create Terraform templates with a new Booster synth command.

Full Changelog: v0.21.7...v0.22.0

Sequenced Read Models and improved Read Model Before Hooks.

27 Aug 14:47
Compare
Choose a tag to compare

This release introduces two features:

Sequenced Read Models

This release introduces the ability to model read models as time sequenced (Or time series). This feature is useful to model a series of data objects that are indexed by the time they were logged. This might sound similar to events, but sequenced read models are also mutable, so they can change over time without losing their place in the sequence. An example use case could be a chat application, where all messages are identified by a specific channel ID and a timestamp of when they were sent, but individual messages can be edited or deleted.

Booster provides a special decorator to tag a specific property as a sequence key for a read model:

export class MessageReadModel {
  public constructor(
    readonly id: UUID, // A channel ID
    @sequencedBy readonly timestamp: string,
    readonly contents: string
  )

  @Projects(Message, 'id')
  public static projectMessage(entity: Message, currentReadModel: MessageReadModel): ProjectionResult<MessageReadModel> {
    return new MessageReadModel(entity.id, entity.timestamp, entity.contents)
  }
}

Adding a sequence key to a read model changes the behavior of its query, which now accepts the sequence key as an optional parameter:

query MessageReadModel(id: ID!, timestamp: string): [MessageReadModel]

In this query, when only the id is provided, you get an array of all the messages in the channel ordered by timestamp in ascending order (from older to newer). When you also provide a specific timestam, you still get an array, but it will only contain the specific message sent at that time.

You can read more about sequenced read models in the documentation.

Improved Read Model Before Hooks

Read Model Before Hooks allow you to set up one or more functions that are called before a read model request is processed, having the opportunity of altering or canceling the request. A common use case for read model before hooks is managing complex access restrictions policies that depend on the business logic, like allowing a user to access objects they own but not objects they should not have access to.

Starting from this release, before hooks will receive the entire request object received from GraphQL, and not only the filters, opening the door to more complex rewrites of specific queries. Before hook functions will receive and return ReadModelRequestEnvelope objects which have the following signature:

interface ReadModelRequestEnvelope<TReadModel> {
  currentUser?: UserEnvelope // The current authenticated user
  requestID: UUID // An ID assigned to this request
  key?: { // If present, contains the id and sequenceKey that identify a specific read model 
    id: UUID
    sequenceKey?: SequenceKey
  }
  className: string // The read model class name
  filters: ReadModelRequestProperties<TReadModel> // Filters set in the GraphQL query
  limit?: number // Query limit if set
  afterCursor?: unknown // For paginated requests, id to start reading from
}

Fixes in the GraphQL input types

10 Aug 13:14
Compare
Choose a tag to compare

In the latest releases, the GraphQL schema generation was improved by adding GraphQLNonNull to elements of arrays.

However, this caused arrays in input types to result in JSONObject instead of the specific type. (e9906b4)

What happened in the last release

Here's what happened

image

It also broke PR #890 which also fixes some JSONObject types, returning specific types instead.

[Booster]  Error: The type of UpsertDossierInput.parties must be Input Type but got: [DossierParty!].

With this release the input types are improved again, so they behave as expected:

image

Command handlers that return values

02 Aug 18:00
Compare
Choose a tag to compare

In this release we introduce the @Returns decorator that allows you to define the return type of a command handler.

This value will be returned to the client through GraphQL, instead of just returning true (that's left for the case of void).

As an example:

@Command({
  authorize: 'all',
})
export class CreateProduct {
  public constructor(readonly sku: string, readonly price: number) {}

  @Returns(String)
  public static async handle(command: CreateProduct, register: Register): Promise<string> {
    return "Product created!"
  }
}

Learn more in the Booster documentation