Skip to content

Commit

Permalink
feat: add addPolicy codemod
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Dec 25, 2023
1 parent 5a3d878 commit 49cea4d
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 1 deletion.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,36 @@ export const plugins: Config['plugins'] = [
]
```

### addPolicy
Register AdonisJS bouncer policy to the list of `policies` object exported from the `app/policies/main.ts` file.

> [!IMPORTANT]
> This codemod expects the `app/policies/main.ts` file to exist and must export a `policies` object from it.
```ts
const transformer = new CodeTransformer(appRoot)

try {
await transformer.addPolicy([
{
name: 'PostPolicy',
path: '#policies/post_policy'
}
])
} catch (error) {
console.error('Unable to register policy')
console.error(error)
}
```

Output

```ts
export const policies = {
UserPolicy: () => import('#policies/post_policy')
}
```

## Contributing
One of the primary goals of AdonisJS is to have a vibrant community of users and contributors who believe in the framework's principles.

Expand Down
43 changes: 42 additions & 1 deletion src/code_transformer/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from 'ts-morph'

import { RcFileTransformer } from './rc_file_transformer.js'
import type { MiddlewareNode, EnvValidationNode } from '../types.js'
import type { MiddlewareNode, EnvValidationNode, BouncerPolicyNode } from '../types.js'

/**
* This class is responsible for updating
Expand Down Expand Up @@ -139,6 +139,25 @@ export class CodeTransformer {
}
}

/**
* Add a policy to the list of pre-registered policy
*/
#addToPoliciesList(file: SourceFile, policyEntry: BouncerPolicyNode) {
const policiesObject = file
.getVariableDeclarationOrThrow('policies')
.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression)

/**
* Only define policy when one with the existing name does not
* exist.
*/
const existingProperty = policiesObject.getProperty(policyEntry.name)
if (!existingProperty) {
const policy = `${policyEntry.name}: () => import('${policyEntry.path}')`
policiesObject!.insertProperty(0, policy)
}
}

/**
* Write a leading comment
*/
Expand Down Expand Up @@ -338,4 +357,26 @@ export class CodeTransformer {
file.formatText(this.#editorSettings)
await file.save()
}

/**
* Adds a policy to the list of `policies` object configured
* inside the `app/policies/main.ts` file.
*/
async addPolicies(policies: BouncerPolicyNode[]) {
/**
* Get the `app/policies/main.ts` source file
*/
const kernelUrl = fileURLToPath(new URL('./app/policies/main.ts', this.#cwd))
const file = this.#project.getSourceFileOrThrow(kernelUrl)

/**
* Process each middleware entry
*/
for (const policy of policies) {
this.#addToPoliciesList(file, policy)
}

file.formatText(this.#editorSettings)
await file.save()
}
}
15 changes: 15 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,21 @@ export type MiddlewareNode = {
position?: 'before' | 'after'
}

/**
* Policy node to be added to the list of policies.
*/
export type BouncerPolicyNode = {
/**
* Policy name
*/
name: string

/**
* Policy import path
*/
path: string
}

/**
* Defines the structure of an environment variable validation
* definition
Expand Down
77 changes: 77 additions & 0 deletions tests/code_transformer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,3 +702,80 @@ test.group('Code transformer | addJapaPlugin', (group) => {
`)
})
})

test.group('Code transformer | addPolicies', (group) => {
group.each.setup(async ({ context }) => setupFakeAdonisproject(context.fs))

test('add policies to polices/main.ts file', async ({ assert, fs }) => {
await fs.create('app/policies/main.ts', `export const policies = {}`)

const transformer = new CodeTransformer(fs.baseUrl)

await transformer.addPolicies([
{
name: 'PostPolicy',
path: '#policies/post_policy',
},
{
name: 'UserPolicy',
path: '#policies/user_policy',
},
])

const file = await fs.contents('app/policies/main.ts')
assert.snapshot(file).matchInline(`
"export const policies = {
UserPolicy: () => import('#policies/user_policy'),
PostPolicy: () => import('#policies/post_policy')
}
"
`)
})

test('throw error when policies/main.ts file is missing', async ({ fs }) => {
const transformer = new CodeTransformer(fs.baseUrl)

await transformer.addPolicies([
{
name: 'PostPolicy',
path: '#policies/post_policy',
},
{
name: 'UserPolicy',
path: '#policies/user_policy',
},
])
}).throws(/Could not find source file in project at the provided path:/)

test('throw error when policies object is not defined', async ({ fs }) => {
await fs.create('app/policies/main.ts', `export const foo = {}`)
const transformer = new CodeTransformer(fs.baseUrl)

await transformer.addPolicies([
{
name: 'PostPolicy',
path: '#policies/post_policy',
},
{
name: 'UserPolicy',
path: '#policies/user_policy',
},
])
}).throws(`Expected to find variable declaration named 'policies'.`)

test('throw error when policies declaration is not an object', async ({ fs }) => {
await fs.create('app/policies/main.ts', `export const policies = []`)
const transformer = new CodeTransformer(fs.baseUrl)

await transformer.addPolicies([
{
name: 'PostPolicy',
path: '#policies/post_policy',
},
{
name: 'UserPolicy',
path: '#policies/user_policy',
},
])
}).throws(/Expected to find an initializer of kind \'ObjectLiteralExpression\'./)
})

0 comments on commit 49cea4d

Please sign in to comment.