May release - 2022
This release improves Lucid model factories and the ability to switch Drive S3 and GCS buckets at runtime. In addition, the make:suite
command to create new test suites and many bug fixes and minor improvements.
You must update all the packages under @adonisjs
scope to their latest version. You can run the npm update command or use the following command to upgrade packages manually.
npx npm-check-updates -i
Once done, make sure to update the ace commands index by running following ace command.
node ace generate:manifest
Infer TypeScript types from config
If you open contracts/drive.ts
, contracts/mail.ts
, or contracts/hash.ts
, you will notice we have manually defined the mappings of the drivers we want to use within our application.
Back story
Before moving forward, let's understand why we have to define these mappings on an interface, and we will use Drive as an example for the same.
In the following import statement, the Drive
object is a singleton created using the config defined inside the config/drive.ts
file and you switch between the disks using the Drive.use
method.
import Drive from '@ioc:Adonis/Core/Drive'
Drive.use('<use-any-mapping-from-config-file>')
All this works as expected at runtime. However, TypeScript has no way to make a relation between the Drive import and its config file. Therefore the TypeScript static compiler cannot provide any IntelliSense or type safety.
To combat that, we use an interface called DisksList
and explicitly inform the TypeScript compiler about the mappings/disks we want to use inside our application.
declare module '@ioc:Adonis/Core/Drive' {
interface DisksList {
local: {
config: LocalDriverConfig
implementation: LocalDriverContract
}
s3: {
config: S3DriverConfig
implementation: S3DriverContract
}
}
}
In a nutshell, we have disk mappings in two places.
- First is the contracts file for the TypeScript compiler.
- Another is the config file for runtime JavaScript.
We can do better here and reduce the mental fatigue of defining mappings at multiple places. Technically it is possible by inferring the TypeScript types directly from the config.
In this and the upcoming releases, we will incrementally remove the manually defined mappings from the interface and use config inference. This release focuses on Drive, Mailer, and the Hash module.
Updating Drive config and contract
Open the config/drive.ts
file and wrap the config object within the driveConfig
method below.
import { DriveConfig } from '@ioc:Adonis/Core/Drive'
import { driveConfig } from '@adonisjs/core/build/config'
const driveConfig: DriveConfig = {
// ...configObject
export default driveConfig({
})
export default driveConfig
The next step is to replace the entire contents of the contracts/drive.ts
file with the following code block.
import { InferDisksFromConfig } from '@adonisjs/core/build/config'
import driveConfig from '../config/drive'
declare module '@ioc:Adonis/Core/Drive' {
interface DisksList extends InferDisksFromConfig<typeof driveConfig> {}
}
Updating Mail config and contract
Open the config/mail.ts
file and wrap the config object within the mailConfig
method below.
import { MailConfig } from '@ioc:Adonis/Addons/Mail'
import { mailConfig } from '@adonisjs/mail/build/config'
const mailConfig: MailConfig = {
// ...configObject
export default mailConfig({
})
export default mailConfig
The next step is to replace the entire contents of the contracts/mail.ts
file with the following code block.
import { InferMailersFromConfig } from '@adonisjs/mail/build/config'
import mailConfig from '../config/mail'
declare module '@ioc:Adonis/Addons/Mail' {
interface MailersList extends InferMailersFromConfig<typeof mailConfig> {}
}
Updating Hash config and contract
Open the config/hash.ts
file and wrap the config object within the hashConfig
method below.
import { HashConfig } from '@ioc:Adonis/Core/Hash'
import { hashConfig } from '@adonisjs/core/build/config'
const hashConfig: HashConfig = {
// ...configObject
export default hashConfig({
})
export default hashConfig
The next step is to replace the entire contents of the contracts/hash.ts
file with the following code block.
import { InferListFromConfig } from '@adonisjs/core/build/config'
import hashConfig from '../config/hash'
declare module '@ioc:Adonis/Core/Hash' {
interface HashersList extends InferListFromConfig<typeof hashConfig> {}
}
That is all. We will also introduce config type inference for other packages in future releases.
Lucid model factory improvements and new commands
The following new methods/properties have been added to Lucid model factories.
tap
The tap
method allows you to access the underlying model instance before it is persisted using the factory. You can use this method to define additional attributes on the model.
UserFactory
.tap((user) => user.isAdmin => true)
.create()
parent
You can access the parent
of a relationship inside the with
callback. This is usually helpful when you want to infer some attributes from the parent model.
We infer the tenantId
property from the User model instance in the following example.
await TenantFactory
.with('users', 1, (user) => {
user
.with('posts', 2, (post) => {
post.merge({ tenantId: post.parent.tenantId })
})
})
.create()
pivotAttributes
You can define pivot attributes for a many-to-many relationship using the pivotAttributes
method.
await Team.with('users', 2, (user) => {
user.pivotAttributes({ role: 'admin' })
})
mergeRecursive
You often want to merge attributes with all the children's relationships.
Let's take the previous example of defining the tenant id on the Post model. Instead of accessing the Post factory builder and then calling the merge
method, we can recursively pass the tenantId
to all the children relationships from the User factory.
await TenantFactory
.with('users', 1, (user) => {
user
.mergeRecursive({ tenantId: user.parent.id })
.with('posts', 2)
.with('posts', 2, (post) => {
post.merge({ tenantId: post.parent.tenantId })
})
})
.create()
make:factory
command
You can now make a new factory by running the node ace make:factory ModelName
command. Also, you can pass the -f
flag to the make:model
command to create a factory alongside a model.
node ace make:factory User
# create a factory with model
node ace make:model User -f
# create factory + migration with model
node ace make:model User -cf
Upgrading to Knex 2.0
We have upgraded the Knex version to 2.0, resulting in a small breaking change for SQLite users. Earlier, knex used the @vscode/sqlite3
package for the SQLite connection. However, now it relies on the sqlite3
package.
If you are using SQLite, uninstall the old dependency and favor the new one.
npm uninstall @vscode/sqlite3
npm install sqlite3
Lucid now also ships with the following query builder methods. They were introduced in Knex 1.0.
whereLike
andwhereILike
whereJson
,whereJsonSuperset
,whereJsonSubset
, andwhereJsonPath
.
Drive improvements
You can now switch buckets at runtime before performing a Drive operation. The drive-s3
and the drive-gcs
ship with a new bucket
method.
await Drive
.use('s3')
.bucket('bucket-name')
.put(path, contents)
Prevention for path traversal
In earlier versions of Drive using local driver, the absolute paths were working with drive methods without prefixing them with disk root. To prevent Path Traversal
it has been changed to always resolve paths relative to the root of the defined disk and not from the entire filesystem. If you are using absolute paths with Drive like in the example below, please change it to relative locations. This applies to all Drive methods not only put
.
const filename = 'somefile.txt'
const contents = '...'
await Drive.put(
// DO NOT resolve absolute path for filename
Application.tmpPath('uploads', filename),
contents
)
// instead just let Drive to prefix it with
// defined root for given disk inside config
await Drive.put(filename, contents)
list
method
Experimental The local
driver of Drive now ships with an expiremental list method
to list all files inside a given directory. We will implement the same for the S3 and the GCS drivers in the coming days.
Since implementing the list
method is experimental, we might change the final APIs.
Testing improvements
- A new
make:suite
command has been added to create a new test suite and register it inside the.adonisrc.json
file. - Add support to filter tests by the test title. For example, you can now pass the
--tests="the title of the test"
to run tests matching the mentioned title. - The migrations and database seeders now runs in compact mode during tests.
Commits
- feat: filter tests by title - 09d2b81
- fix: array filters passed to TestProcess were not processed properly - f7a3069
- feat: add
make:suite
command 018da9d - feat: add support to find routes by identifier 7920caf
- feat: add route lookup methods on the router class 9211cfd
- fix: do not use a magic method to compute file name when the file type is not supported 0740a35
- fix: retain default value assigned to arguments on the class instance cf51363
- fix: use the mutated value for validating minLength and maxLength 80081bb
- fix: define serialize property for the attachment column b847dae
- fix: do not persist pre-computed URL to be database ecc6397
- feat: add config helper and type from infer disklist from config 92d1448
- feat: add support for listing files inside a directory e98cb43
- feat: add hashConfig method to define the hash config 87f8e4f
- fix: add stayAlive to ListRoutes command (#3703) 36406f5
- fix: check for the main command name inside command aliases as well 1f8da36
- feat: add support to switch bucket at runtime 8d75ec4
- feat: add support to switch bucket at runtime 208e5bb
- feat: add mailConfig method to infer types from user-defined config 7cd6313
- feat: add redisConfig method to infer types from config abc1ce3
- fix: use response cookies to read the session id 38ccee9
- refactor: regenerate session id as soon as regenerate method is called cb21abf
- feat: add sessionConfig helper method 70cbca0
- feat: add dumpSession method 7289496
- refactor: use anonymous classes for migrations (#829) 3d12a45
- chore: remove
@vscode/sqlite3
in favor ofsqlite3
2332fbf - refactor: pass factory builder instance to all factory operations 72f233c
- refactor: remove inline callbacks in favor of tap method 13588af
- feat: add support to define pivot attributes via factory builder 49a1d04
- feat: allow passing model assignment options via relationship persistence methods 5b2f846
- feat: allow model properties to report if they are dirty or not e40297c
- feat: add
--compact-output
flag to run/rollback commands (#836) f8e0c8c - feat:
make:factory
command +--factory
flag to make(#837) bd22c96 - feat: add
withMaterialized
andwithNotMaterialized
to query builder 04c5c25 - feat: add
whereLike
andwhereILike
methods 1b6001d - feat: expose knex query builder instance in schema classes dff3f84
- feat: add
--compact-output
on DbSeed command 3880c8d - feat: add where"JSON" methods introduced by Knex 1.0 f875828
- fix: prefix table name when making relationship join queries 9385a9b
- feat: add support for recursively merging attributes from factories 8f708b3