Repository: Rocketseat/adonis-bull
Branch: master
Commit: 2331e07a3869
Files: 28
Total size: 42.7 KB
Directory structure:
gitextract_8sjho86i/
├── .editorconfig
├── .eslintrc.js
├── .github/
│ ├── CODEOWNERS
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ ├── documentation-report.md
│ │ └── enhancement-request.md
│ ├── PULL_REQUEST_TEMPLATE
│ └── workflows/
│ ├── commit.yml
│ ├── coveralls.yml
│ ├── lint.yml
│ └── nodejs.yml
├── .gitignore
├── .prettierrc
├── LICENSE.md
├── README.md
├── japaFile.js
├── package.json
├── providers/
│ ├── Bull.js
│ └── Command.js
├── src/
│ ├── Commands/
│ │ └── Listen.js
│ └── Queue.js
└── test/
├── functional/
│ ├── app/
│ │ └── SomeJob.js
│ ├── providers.spec.js
│ └── start/
│ └── jobs.js
└── unit/
└── queue.spec.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
================================================
FILE: .eslintrc.js
================================================
module.exports = {
env: {
es6: true,
node: true,
},
plugins: ['prettier'],
extends: ['standard', 'prettier'],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
},
parserOptions: {
ecmaVersion: 2018,
},
rules: {
'prettier/prettier': 'error',
},
}
================================================
FILE: .github/CODEOWNERS
================================================
# Learn how to add code owners here:
# https://help.github.com/en/articles/about-code-owners
* @jpedroschmitz
*.js @HigoRibeiro
================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Code of Conduct
This Code of Conduct outlines our expectations for participants within the Rocketseat community as well as steps to reporting unacceptable behavior. Our goal is to make explicit what we expect from participants in this community as well as its leaders. We are committed to providing a welcoming and inspiring community for all and expect our code of conduct to be honored. Anyone who violates this code of conduct may be banned from the community.
Our community strives to:
- **Be welcoming, friendly and patient**.
- **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that you might not be communicating in someone else’s primary language.
- **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one.
- **Be understandable**: Disagreements, both social and technical, happen all the time and Rocketseat is no exception. It is important that we resolve disagreements and differing views constructively. Remember that we're different. The strength of Rocketseat comes from its varied community, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn't mean that they're wrong. Don't forget that it is human to err and blaming each other doesn't get us anywhere, rather offer to help resolving issues and to help learn from mistakes.
This code serves to distill our common understanding of a collaborative, shared environment, and goals. We expect it to be followed in spirit as much as in the letter.
### Diversity Statement
We encourage everyone to participate and are committed to building a community for all. Although we may not be able to satisfy everyone, we all agree that everyone is equal. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong.
Although this list cannot be exhaustive, we explicitly honor diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected characteristics above, including participants with disabilities.
### Reporting Issues
If you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us via [opensource@rocketseat.com.br](mailto:opensource@rocketseat.com.br). All reports will be handled with discretion. In your report please include:
- Your contact information.
- Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record, please include a link.
- Any additional information that may be helpful.
After filing a report, a representative will contact you personally. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. A representative will then review the incident, follow up with any additional questions, and make a decision as to how to respond. We will respect confidentiality requests for the purpose of protecting victims of abuse.
Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable behavior, the representative may take any action they deem appropriate, up to and including a permanent ban from our community without warning.
================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contribution guidelines
## Table of Contents
- [Getting started](#getting-started)
- [Language](#language)
- [For native English speakers](#for-native-english-speakers)
- [Code of Conduct](#code-of-conduct)
- [How can I help?](#how-can-i-help)
- [Documentation](#documentation)
- [Issues](#issues)
- [Submitting an issue](#submitting-an-issue)
- [Feedback](#feedback)
- [Code](#code)
- [Commiting](#commiting)
- [Why all these rules?](#why-all-these-rules)
- [Submitting a pull request](#submitting-a-pull-request)
## Getting started
First off, we would like to thank you for taking the time to contribute and make this a better project!
Here we have a set of instructions and guidelines to reduce misunderstandings and make the process of contributing to `adonis-bull` as smooth as possible. We hope this guide makes the contribution process clear and answers any questions you may have.
### Language
Please, while contributing or interacting in any way in this project, **refrain from using any language other than English**.
#### For native English speakers
Try to use simple words and sentences. Don't make fun of non-native English speakers if you find something wrong about the way they express themselves.
Try to encourage newcomers to express their opinions, and make them comfortable enough to do so.
### Code of Conduct
We expect that project participants to adhere to our Code of Conduct. You can check the [full text](CODE_OF_CONDUCT.md) so that you may understand the kind of conduct we are expecting and what actions will and will not be tolerated.
By participating in this project, you agree to abide by its terms.
## How can I help?
Here are some ways you can help along with some guidelines.
### Documentation
As a user of `adonis-bull`, you're the perfect candidate to help us improve our documentation!
Typos, errors, lack of examples and/or explanation and so on, are just some examples of things that could be fixed and/or improved. You could even make improvements to this guide :)
While documenting, try to keep things simple and clear.
### Issues
Some issues are created with missing information, without a template, not reproducible, or plain
invalid. You can make them easier to understand and resolve.
#### Submitting an issue
- Please search for similar issues before opening a new one;
- Use one of the corresponding issue templates;
- Use a clear and descriptive title;
- Include as much information as possible by filling out the provided issue template;
- Most of the time, the best way to report an issue is a failing test proving it.
### Feedback
The more feedback the better! We're always looking for more suggestions and opinions on discussions. That's a good opportunity to influence the future direction of this tool.
This includes submitting an enhancement suggestion, including completely new features and minor improvements to existing functionality.
The [`question`](https://github.com/Rocketseat/adonis-bull/labels/type%3A%20question%20or%20discussion)and [`rfc`](https://github.com/Rocketseat/adonis-bull/labels/type%3A%20rfc) labels are a good place to find ongoing discussions.
### Code
You can use issue labels to discover issues you could help out with:
- [`bug` issues](https://github.com/Rocketseat/adonis-bull/labels/kind%3A%20bug) are known bugs we'd like to fix;
- [`enhancement` issues](https://github.com/Rocketseat/adonis-bull/labels/type%3A%20feature%20request) are features we're open to include.
The [`help wanted`](https://github.com/Rocketseat/adonis-bull/labels/help%20wanted) and [`good first issue`](https://github.com/Rocketseat/adonis-bull/labels/good%20first%20issue) labels are especially useful.
When you see an issue that is already assigned, please check to see if there isn't someone working on it already (maybe try asking in the issue). This is to prevent unnecessary work for everyone involved.
## Commiting
A commit message can consists of a **header**, **body** and **footer**. The header is the only mandatory part and consists of a type and a subject. The body is used to fully describe the change. The footer is the place to reference any issues or pull requests related to the commit. That said, we end with a template like this:
```
<type>: <subject>
[optional body]
[optional footer]
```
To ensure that a commit is valid, easy to read, and changelog-ready, we have a hook that lints the commit message before allowing a commit to pass. This linter verifies the following:
- The header (first line) is the only mandatory part of the commit message;
- The body and footer are both optional but its use is highly encouraged;
- The header should contains:
- A type:
- Must be lowercase;
- Must be one of:
- **chore**: A change that neither fix a bug nor adds a feature;
- **ci**: A CI change;
- **docs**: A documentation change or fix;
- **feat**: A new feature;
- **fix**: A bug fix;
- **test**: A test-related change.
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- A subject:
- Must be capitalized;
- Must be limited to 50 characters or less;
- Must omit any trailing punctuation.
- The body:
- Must have a leading blank line;
- Each line must be limited to 72 characters or less.
- The footer:
- Must have a leading blank line;
- Each line must be limited to 72 characters or less;
- If your commit is about documentation or meta files, please add the tag **[skip ci]** to skip the building process.
- If needed, reference to issues and pull requests must be made here in the last line.
You also should follow these general guidelines when committing:
- Use the present tense ("Add feature" not "Added feature");
- Use the imperative mood ("Move cursor to..." not "Moves cursor to...");
- Try to answer the following questions:
- Why is this change necessary?
- How does it address the issue?
- What side effects (if any) does this change may have?
Example of a commit message:
```
type: Commit message style guide for Git
The first line of a commit message serves as a summary. When displayed
on the web, it's often styled as a heading, and in emails, it's
typically used as the subject. As such, you should specify a "type" and
a "subject". The type must be lowercase and one of: chore, ci, docs,
feat, fix, test. For the subject you'll need capitalize it and
omit any trailing punctuation. Aim for about 50 characters, give or
take, otherwise it may be painfully truncated in some contexts. Write
it, along with the rest of your message, in the present tense and
imperative mood: "Fix bug" and not "Fixed bug" or "Fixes bug".
Consistent wording makes it easier to mentally process a list of
commits.
Oftentimes a subject by itself is sufficient. When it's not, add a
blank line (this is important) followed by one or more paragraphs hard
wrapped to 72 characters. Git is strongly opinionated that the author
is responsible for line breaks; if you omit them, command line tooling
will show it as one extremely long unwrapped line. Fortunately, most
text editors are capable of automating this.
Issues and pull request can be referenced on the footer: #3 #12
```
### Why all these rules?
We try to enforce these rules for the following reasons:
- Automatically generating changelog;
- Communicating in a better way the nature of changes;
- Triggering build and publish processes;
- Automatically determining a semantic version bump (based on the types of commits);
- Making it easier for people to contribute, by allowing them to explore a more structured commit history.
## Submitting a pull request
Before submitting a pull request, please make sure the following is done:
- [Fork](https://help.github.com/en/articles/fork-a-repo) the repository and create your branch from `master`.
- Example: `feat/my-awesome-feature` or `fix/annoying-bug`;
- Run `yarn` in the repository root;
- If you’ve fixed a bug or added code that should be tested, **add tests**;
- Ensure the test suite passes;
- Ensure your commit is validated;
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
---
<!-- Verify first that your issue is not already reported -->
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks! -->
<!-- If possible complete *all* sections as described. Don't remove any section. -->
**Description of bug**
<!-- A clear and concise description of what the bug is. -->
**To Reproduce**
<!--
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
-->
**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->
**Exception or Error**
<pre><code>
<!-- If the issue is accompanied by an exception or an error, please share it below: -->
<!-- ✍️-->
</code></pre>
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
**Environment:**
<!--
Add information about your environment
- OS: [e.g. Ubuntu, Windows, macOS]
- Node [e.g 12.0.0]
- Adonis [e.g 4.1.0]
- Etc
-->
**Additional context**
<!-- Add any other context about the problem here. -->
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
================================================
FILE: .github/ISSUE_TEMPLATE/documentation-report.md
================================================
---
name: Documentation report
about: Use this template for documentation related issues
---
<!-- Verify first that your issue is not already reported -->
<!-- Please only use this template for documentation related issues -->
<!-- If possible complete *all* sections as described. Don't remove any section. -->
**Description of issue**
<!-- A clear description what needs changing, why should it be changed? How is it useful? -->
**Usage example**
<!-- Is there a usage example? -->
================================================
FILE: .github/ISSUE_TEMPLATE/enhancement-request.md
================================================
---
name: Enhancement request
about: Suggest an idea for this project
---
<!-- Verify first that your issue is not already reported -->
<!-- Please only use this template for submitting enhancement requests -->
<!-- If possible complete *all* sections as described. Don't remove any section. -->
**What would you like to be added**:
<!-- A clear and concise description of what would you like to be added. -->
**Why is this needed**:
<!-- A clear and concise description of why is it needed. -->
**Is your enhancement request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Additional context**
<!-- Add any other context or screenshots about the enhancement request here. -->
================================================
FILE: .github/PULL_REQUEST_TEMPLATE
================================================
<!-- If this is your first time, please read our contribution guidelines: (https://github.com/Rocketseat/adonis-bull/blob/master/.github/CONTRIBUTING.md) -->
<!-- Verify first that your pull request is not already proposed -->
<!-- Refrain from using any language other than English -->
<!-- Ensure you have added or ran the appropriate tests for your PR -->
**Changes proposed**
<!--- What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) -->
<!--- Does this PR introduce a breaking change? -->
<!--- What is the current behavior? (You can also link to an open issue here) -->
<!--- What is the new behavior (if this is a feature change)?** -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->
================================================
FILE: .github/workflows/commit.yml
================================================
name: Lint Commit Messages
on: [pull_request]
jobs:
commit:
name: Lint commit messages
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Install dependencies
run: yarn
- name: Check commit message
uses: wagoid/commitlint-github-action@v1
env:
NODE_PATH: ${{ github.workspace }}/node_modules
================================================
FILE: .github/workflows/coveralls.yml
================================================
name: Coveralls
on: [pull_request]
env:
CI: true
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
NODE_ENV: test
jobs:
run:
name: Coverage [Node.js ${{ matrix.node-version }} on ${{ matrix.os }}]
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node-version: [12.x]
steps:
- name: Clone repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Setup Redis
uses: zhulik/redis-action@1.1.0
with:
redis version: '5'
number of databases: 1
- name: Install dependencies
run: yarn
- name: Run tests
run: yarn coverage
- name: Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint
on: [pull_request]
jobs:
code:
name: Lint code
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Node
uses: actions/setup-node@v1
with:
node-version: 12
- name: Install dependencies
run: yarn
- name: Run ESLint
run: yarn lint
================================================
FILE: .github/workflows/nodejs.yml
================================================
name: Node.js
on: [push, pull_request]
env:
CI: true
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
jobs:
run:
name: Node.js ${{ matrix.node-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node-version: [10.x, 12.x]
steps:
- name: Clone repository
uses: actions/checkout@v1
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Setup Redis
uses: zhulik/redis-action@1.1.0
with:
redis version: '5'
number of databases: 1
- name: Install dependencies
run: yarn
- name: Run tests
run: yarn test
================================================
FILE: .gitignore
================================================
# Created by https://www.gitignore.io/api/node
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Coveralls configuration file
.coveralls.yml
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# tmp dirs
test/tmp
test/database
# End of https://www.gitignore.io/api/node
================================================
FILE: .prettierrc
================================================
{
"singleQuote": true,
"semi": false
}
================================================
FILE: LICENSE.md
================================================
MIT License
Copyright (c) 2021 Rocketseat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<br />
<img width="275" alt="Adonis Bull" src="https://user-images.githubusercontent.com/16545335/71373449-f2146880-2595-11ea-8a8c-9f51384a3f22.png">
<p align="center">
<h2>A <a href="https://github.com/taskforcesh/bullmq">Bull</a> provider for <a href="https://adonisjs.com/">AdonisJS</a> </br>
Adonis Bull provides an easy way to start using Bull.
</h2>
</p>
<div>
[![build-image]][build-url] [![coveralls-image]][coveralls-url] [![license-image]][license-url] [![npm-image]][npm-url]
</div>
<br />
> **This documentation refers to the stable version of Adonis Bull, for Adonis v4.x** <br /> > **If you are using Adonis v5, [click here](https://github.com/Rocketseat/adonis-bull/tree/alpha).**
## Why
Using Bull with Adonis shouldn't be hard. It shouldn't require dozens of steps to configure it. That's why adonis-bull exists. It provides an easy way to use queues when developing applications with AdonisJS.
## Install
```sh
adonis install @rocketseat/adonis-bull
```
## Usage
Register the Bull commands at `start/app.js`
```js
const aceProviders = ['@rocketseat/adonis-bull/providers/Command']
```
Register the Bull provider at `start/app.js`
```js
const providers = [
//...
'@rocketseat/adonis-bull/providers/Bull',
]
```
Create a file with the `jobs` that will be processed at `start/jobs.js`:
```js
module.exports = ['App/Jobs/UserRegisterEmail']
```
Add the config file at `config/bull.js`:
```js
'use strict'
const Env = use('Env')
module.exports = {
// redis connection
connection: Env.get('BULL_CONNECTION', 'bull'),
bull: {
redis: {
host: '127.0.0.1',
port: 6379,
password: null,
db: 0,
keyPrefix: '',
},
},
remote: 'redis://redis.example.com?password=correcthorsebatterystaple',
}
```
In the above file you can define redis connections, there you can pass all `Bull` queue configurations described [here](https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#queue).
Create a file to initiate `Bull` at `preloads/bull.js`:
```js
const Bull = use('Rocketseat/Bull')
Bull.process()
// Optionally you can start BullBoard:
.ui(9999, 'localhost') // http://localhost:9999
// You don't need to specify either port or hostname, the default port number is 9999 and the default hostname is localhost
```
Add .preLoad in server.js to initialize the bull preload
```js
new Ignitor(require('@adonisjs/fold'))
.appRoot(__dirname)
.preLoad('preloads/bull') // Add This Line
.fireHttpServer()
.catch(console.error)
```
## Creating your job
Create a class that mandatorily has the methods `key` and `handle`.
The `key` method is the unique identification of each job. It has to be a `static get` method.
The `handle` is the method that contains the functionality of your `job`.
```js
const Mail = use('Mail')
class UserRegisterEmail {
static get key() {
return 'UserRegisterEmail-key'
}
async handle(job) {
const { data } = job // the 'data' variable has user data
await Mail.send('emails.welcome', data, (message) => {
message
.to(data.email)
.from('<from-email>')
.subject('Welcome to yardstick')
})
return data
}
}
module.exports = UserRegisterEmail
```
You can use the `connection` static get method to specify which connection your `job` will work.
```js
class UserRegisterEmail {
// ...
static get connection() {
return 'remote'
}
}
```
### Events
The package has support for all events triggered in the bull, just add "on" and complete with the name of the event
Ex: `onCompleted()`, `onActive()`, `onWaiting()` and etc.
```js
class UserRegisterEmail {
...
onCompleted(job, result) {}
onActive(job) {}
...
}
module.exports = UserRegisterEmail;
```
## Processing the jobs
### Simple job
You can share the `job` of any `controller`, `hook` or any other place you might like:
```js
const User = use('App/Models/User')
const Bull = use('Rocketseat/Bull')
const Job = use('App/Jobs/UserRegisterEmail')
class UserController {
store ({ request, response }) {
const data = request.only(['email', 'name', 'password'])
const user = await User.create(data)
Bull.add(Job.key, user)
}
}
module.exports = UserController
```
### Scheduled job
Sometimes it is necessary to schedule a job instead of shooting it imediately. You should use `schedule` for that:
```js
const User = use('App/Models/User')
const Bull = use('Rocketseat/Bull')
const Job = use('App/Jobs/HolidayOnSaleEmail')
class HolidayOnSaleController {
store ({ request, response }) {
const data = request.only(['date', 'product_list']) // 2019-11-15 12:00:00
const products = await ProductOnSale.create(data)
Bull.schedule(Job.key, products, data.date)
}
}
module.exports = HolidayOnSaleController
```
This `job` will be sent only on the specific date, wich for example here is on November 15th at noon.
When finishing a date, never use past dates because it will cause an error.
other ways of using `schedule`:
```js
Bull.schedule(key, data, new Date('2019-11-15 12:00:00'))
Bull.schedule(key, data, '2 hours') // 2 hours from now
Bull.schedule(key, data, 60 * 1000) // 1 minute from now.
```
### Advanced jobs
You can use the own `Bull` configs to improve your job:
```js
Bull.add(key, data, {
repeat: {
cron: '0 30 12 * * WED,FRI',
},
})
```
This `job` will be run at 12:30 PM, only on wednesdays and fridays.
### Exceptions
To have a bigger control over errors that might occur on the line, the events that fail can be manipulated at the file `App/Exceptions/QueueHandler.js`:
```js
const Sentry = use('Sentry')
class QueueHandler {
async report(error, job) {
Sentry.configureScope((scope) => {
scope.setExtra(job)
})
Sentry.captureException(error)
}
}
module.exports = QueueHandler
```
## Contributing
Thank you for being interested in making this package better. We encourage everyone to help improve this project with new features, bug fixes, or performance improvements. Please take a little bit of your time to read our guide to make this process faster and easier.
### Contribution Guidelines
To understand how to submit an issue, commit and create pull requests, check our [Contribution Guidelines](/.github/CONTRIBUTING.md).
### Code of Conduct
We expect you to follow our [Code of Conduct](/.github/CODE_OF_CONDUCT.md). You can read it to understand what kind of behavior will and will not be tolerated.
## License
MIT License © [Rocketseat](https://github.com/Rocketseat)
[npm-image]: https://img.shields.io/npm/v/@rocketseat/adonis-bull?color=8257E5&style=for-the-badge
[npm-url]: https://www.npmjs.com/package/@rocketseat/adonis-bull 'npm'
[license-url]: LICENSE.md
[license-image]: https://img.shields.io/github/license/adonisjs/adonis-framework?color=8257E5&style=for-the-badge
[build-url]: https://github.com/Rocketseat/adonis-bull/actions
[build-image]: https://img.shields.io/github/workflow/status/Rocketseat/adonis-bull/Node.js/master?color=8257E5&style=for-the-badge
[coveralls-image]: https://img.shields.io/coveralls/github/Rocketseat/adonis-bull/master?color=8257E5&style=for-the-badge
[coveralls-url]: https://coveralls.io/github/Rocketseat/adonis-bull?branch=master
================================================
FILE: japaFile.js
================================================
const { configure } = require('japa')
configure({
files: ['test/**/*.spec.js'],
})
================================================
FILE: package.json
================================================
{
"name": "@rocketseat/adonis-bull",
"version": "0.3.0",
"main": "src/Queue.js",
"license": "MIT",
"scripts": {
"test": "node japaFile.js",
"coverage": "nyc --reporter=lcov --reporter=text-summary npm run test",
"lint": "eslint --ignore-path .gitignore",
"format": "prettier \"**/*.js\" --write --ignore-path .gitignore"
},
"nyc": {
"exclude": [
"**/*.spec.js",
"bin"
]
},
"directories": {
"test": "test"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"lint-staged": {
"*.js": [
"yarn lint --fix",
"yarn format"
]
},
"commitlint": {
"extends": [
"@rocketseat/commitlint-config"
]
},
"keywords": [
"bull",
"queue",
"adonis",
"adonisjs",
"adonis-js",
"adonis-bull",
"adonis-queue"
],
"devDependencies": {
"@adonisjs/ace": "^5.0.8",
"@adonisjs/fold": "^4.0.9",
"@adonisjs/sink": "^1.0.17",
"@commitlint/cli": "^11.0.0",
"@rocketseat/commitlint-config": "^0.0.2",
"delay": "^4.3.0",
"eslint": "^7.18.0",
"eslint-config-prettier": "^7.2.0",
"eslint-config-standard": "^16.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"husky": "^4.3.8",
"japa": "^3.0.1",
"lint-staged": "^10.5.3",
"nyc": "^14.1.1",
"prettier": "^2.2.1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/rocketseat/adonis-bull.git"
},
"dependencies": {
"bull": "^3.22.1",
"bull-board": "^1.5.1",
"date-fns": "^2.7.0",
"human-interval": "^0.1.6"
},
"resolutions": {
"**/**/minimist": "^1.2.3",
"**/**/set-value": "^3.0.1"
}
}
================================================
FILE: providers/Bull.js
================================================
const { ServiceProvider, resolver } = require('@adonisjs/fold')
const path = require('path')
class BullProvider extends ServiceProvider {
register() {
this.app.singleton('Rocketseat/Bull', (app) => {
const Queue = require('../src/Queue')
const Helpers = app.use('Adonis/Src/Helpers')
const Logger = app.use('Adonis/Src/Logger')
const Config = app.use('Adonis/Src/Config')
const jobs = require(path.join(Helpers.appRoot(), 'start/jobs.js')) || []
return new Queue(Logger, Config, jobs, app, resolver)
})
this.app.alias('Rocketseat/Bull', 'Bull')
}
}
module.exports = BullProvider
================================================
FILE: providers/Command.js
================================================
'use strict'
const { ServiceProvider } = require('@adonisjs/fold')
class MigrationsProvider extends ServiceProvider {
register() {
this.app.bind('Rocketseat/Commands/Bull:Listen', () =>
require('../src/Commands/Listen')
)
}
boot() {
const ace = require('@adonisjs/ace')
ace.addCommand('Rocketseat/Commands/Bull:Listen')
}
}
module.exports = MigrationsProvider
================================================
FILE: src/Commands/Listen.js
================================================
'use strict'
const { Command } = require('@adonisjs/ace')
class Listen extends Command {
static get inject() {
return ['Rocketseat/Bull']
}
constructor(Bull) {
super()
this.Bull = Bull
}
static get signature() {
return `
bull:listen
{ --board?=false : Run bull's dashboard }
{ --board-hostname?=@value : Dashboard hostname }
{ --board-port?=@value : Dashboard port }
`
}
static get description() {
return 'Start the Bull listener'
}
async handle(args, { board = false, boardHostname = 'localhost', boardPort = 9999 }) {
this.Bull.process()
if (board) {
this.Bull.ui(boardPort, boardHostname)
}
}
}
module.exports = Listen
================================================
FILE: src/Queue.js
================================================
'use strict'
const Bull = require('bull')
const BullBoard = require('bull-board')
const humanInterval = require('human-interval')
const differenceInMilliseconds = require('date-fns/differenceInMilliseconds')
const parseISO = require('date-fns/parseISO')
const fs = require('fs')
class Queue {
constructor(Logger, Config, jobs, app, resolver) {
this.Logger = Logger
this.jobs = jobs
this.app = app
this.resolver = resolver
this._queues = null
const { connection, ...connections } = Config.get('bull')
this.config = connections[connection]
this.connections = connections
}
_getJobListeners(Job) {
const jobListeners = Object.getOwnPropertyNames(Job.prototype)
.filter((method) => method.startsWith('on'))
.map((method) => {
const eventName = method
.replace(/^on(\w)/, (match, group) => group.toLowerCase())
.replace(/([A-Z]+)/, (match, group) => ` ${group.toLowerCase()}`)
return { eventName, method }
})
return jobListeners
}
get queues() {
if (!this._queues) {
this._queues = this.jobs.reduce((queues, path) => {
const Job = this.app.use(path)
let config = this.config
if (Job.connection) {
config = this.connections[Job.connection]
}
queues[Job.key] = {
bull: new Bull(Job.key, config),
Job,
name: Job.key,
handle: Job.handle,
concurrency: Job.concurrency || 1,
options: Job.options,
}
return queues
}, {})
}
return this._queues
}
get(name) {
return this.queues[name]
}
add(name, data, options) {
const queue = this.get(name)
const job = queue.bull.add(data, { ...queue.options, ...options })
return job
}
removeRepeatable(name, repeat) {
const queue = this.get(name)
return queue.bull.removeRepeatable('__default__', repeat)
}
getRepeatableJobs(name) {
const queue = this.get(name)
const jobs = queue.bull.getRepeatableJobs()
return jobs
}
schedule(name, data, date, options) {
let delay
if (typeof date === 'number' || date instanceof Number) {
delay = date
} else {
if (typeof date === 'string' || date instanceof String) {
const byHuman = humanInterval(date)
if (!isNaN(byHuman)) {
delay = byHuman
} else {
delay = differenceInMilliseconds(parseISO(date), new Date())
}
} else {
delay = differenceInMilliseconds(date, new Date())
}
}
if (delay > 0) {
return this.add(name, data, { ...options, delay })
} else {
throw new Error('Invalid schedule time')
}
}
ui(port = 9999, hostname = 'localhost') {
BullBoard.setQueues(
Object.values(this.queues).map(
(queue) => new BullBoard.BullAdapter(queue.bull)
)
)
const server = BullBoard.router.listen(port, hostname, () => {
this.Logger.info(`bull board on http://${hostname}:${port}`)
})
const shutdown = () => {
server.close(() => {
this.Logger.info('Stopping bull board server')
process.exit(0)
})
}
process.on('SIGTERM', shutdown)
process.on('SIGINT', shutdown)
}
async remove(name, jobId) {
const job = await this.queues[name].bull.getJob(jobId)
job.remove()
}
/* eslint handle-callback-err: "error" */
handleException(error, job) {
try {
const exceptionHandlerFile = this.resolver
.forDir('exceptions')
.getPath('QueueHandler.js')
fs.accessSync(exceptionHandlerFile, fs.constants.R_OK)
const namespace = this.resolver
.forDir('exceptions')
.translate('QueueHandler')
const handler = this.app.make(this.app.use(namespace))
handler.report(error, job)
} catch (err) {
this.Logger.error(`name=${job.queue.name} id=${job.id}`)
}
}
process() {
this.Logger.info('Queue processing started')
Object.values(this.queues).forEach((queue) => {
const Job = new queue.Job()
const jobListeners = this._getJobListeners(queue.Job)
jobListeners.forEach(function (item) {
queue.bull.on(item.eventName, Job[item.method].bind(Job))
})
queue.bull.process(queue.concurrency, (job, done) => {
Job.handle(job)
.then((result) => {
done(null, result)
})
.catch((error) => {
this.handleException(error, job)
done(error)
})
})
})
const shutdown = () => {
const promises = Object.values(this.queues).map((queue) => {
return queue.bull.close()
})
return Promise.all(promises).then(process.exit(0))
}
process.on('SIGTERM', shutdown)
process.on('SIGINT', shutdown)
return this
}
}
module.exports = Queue
================================================
FILE: test/functional/app/SomeJob.js
================================================
class SomeJob {
static get key() {
return 'SomeJob-key'
}
async handle() {
return 'good luck'
}
}
module.exports = SomeJob
================================================
FILE: test/functional/providers.spec.js
================================================
const path = require('path')
const test = require('japa')
const { ioc, registrar, resolver } = require('@adonisjs/fold')
const { Helpers, Config } = require('@adonisjs/sink')
test.group('Provider', (group) => {
group.before(async () => {
resolver.appNamespace('App')
registrar
.providers([path.join(__dirname, '../../providers/Bull')])
.register()
ioc.bind('Adonis/Src/Logger', () => {
return console
})
ioc.alias('Adonis/Src/Logger', 'Logger')
ioc.bind('Adonis/Src/Helpers', () => {
return new Helpers(__dirname)
})
ioc.alias('Adonis/Src/Helpers', 'Helpers')
ioc.bind('Adonis/Src/Config', () => {
const config = new Config()
config.set('redis', {
connection: 'local',
local: {
host: '127.0.0.1',
port: 6379,
db: 0,
keyPrefix: '',
},
bull: {
host: '127.0.0.1',
port: 6379,
db: 0,
keyPrefix: 'q',
},
})
config.set('bull', {
connection: 'bull',
})
return config
})
ioc.alias('Adonis/Src/Config', 'Config')
await registrar.boot()
})
group.beforeEach(() => {
ioc.restore()
})
test('BullProvider', async (assert) => {
assert.isDefined(ioc.use('Rocketseat/Bull'))
assert.isTrue(ioc._bindings['Rocketseat/Bull'].singleton)
})
})
================================================
FILE: test/functional/start/jobs.js
================================================
module.exports = ['App/SomeJob']
================================================
FILE: test/unit/queue.spec.js
================================================
const test = require('japa')
const delay = require('delay')
const { ioc, registrar, resolver } = require('@adonisjs/fold')
const { setupResolver, Helpers, Config } = require('@adonisjs/sink')
const path = require('path')
const Queue = require('../../src/Queue')
test.group('Bull', (group) => {
group.before(async () => {
// registrar.before(['@adonisjs/redis/providers/RedisProvider']).register()
ioc.bind('Adonis/Src/Helpers', () => {
return new Helpers(path.join(__dirname, '..'))
})
ioc.alias('Adonis/Src/Helpers', 'Helpers')
ioc.bind('Adonis/Src/Config', () => {
const config = new Config()
config.set('redis', {
connection: 'local',
local: {
host: '127.0.0.1',
port: 6379,
db: 0,
keyPrefix: '',
},
bull: {
host: '127.0.0.1',
port: 6379,
db: 0,
keyPrefix: 'q',
},
})
config.set('bull', {
connection: 'bull',
})
return config
})
ioc.alias('Adonis/Src/Config', 'Config')
await registrar.boot()
setupResolver()
})
group.beforeEach(() => {
ioc.restore()
})
test('should add a new job', async (assert) => {
ioc.bind('Test/Bull', () => {
return class {
static get key() {
return 'TestBull-name'
}
static get concurrency() {
return 2
}
async handle() {}
}
})
const bull = new Queue(
console,
ioc.use('Config'),
['Test/Bull'],
ioc,
resolver
)
const Job = ioc.use('Test/Bull')
const data = { test: 'data' }
const queue = bull.get(Job.key)
const job = await bull.add(Job.key, data)
assert.equal(Job.key, job.queue.name)
assert.deepEqual(data, job.data)
assert.equal(queue.concurrency, 2)
})
test('should add a new job with events inside Job class', async (assert) => {
assert.plan(1)
ioc.bind('Test/Bull', () => {
return class {
static get key() {
return 'TestBull-name'
}
onCompleted() {
assert.isOk()
}
async handle() {}
}
})
const bull = new Queue(
console,
ioc.use('Config'),
['Test/Bull'],
ioc,
resolver
)
const Job = ioc.use('Test/Bull')
const data = { test: 'data' }
bull.add(Job.key, data)
bull.process()
await delay(1050)
})
test('should schedule a new job', async (assert) => {
ioc.bind('Test/Bull', () => {
return class {
static get key() {
return 'TestBull-name'
}
async handle() {}
}
})
const bull = new Queue(
console,
ioc.use('Config'),
['Test/Bull'],
ioc,
resolver
)
const Job = ioc.use('Test/Bull')
const data = { test: 'data' }
const job = await bull.schedule(Job.key, data, '1 second')
assert.equal(Job.key, job.queue.name)
assert.equal(job.delay, 1000)
assert.deepEqual(data, job.data)
})
test("shouldn't schedule when time is invalid", async (assert) => {
assert.plan(1)
ioc.bind('Test/Bull', () => {
return class {
static get key() {
return 'TestBull-name'
}
async handle() {}
}
})
const bull = new Queue(
console,
ioc.use('Config'),
['Test/Bull'],
ioc,
resolver
)
const Job = ioc.use('Test/Bull')
const data = { test: 'data' }
try {
await bull.schedule(Job.key, data, 'invalid time')
} catch (err) {
assert.equal('Invalid schedule time', err.message)
}
})
test('should get all repeatable jobs', async (assert) => {
ioc.bind('Test/Bull', () => {
return class {
static get key() {
return 'TestBull-name'
}
async handle() {}
}
})
const bull = new Queue(
console,
ioc.use('Config'),
['Test/Bull'],
ioc,
resolver
)
const Job = ioc.use('Test/Bull')
const data = { test: 'data' }
const repeat = { cron: '* * * * *' }
await bull.add(Job.key, data, { repeat })
const jobs = await bull.getRepeatableJobs(Job.key)
assert.equal(jobs.length, 1)
assert.deepEqual(repeat.cron, jobs[0].cron)
})
test('should remove a repeatable job', async (assert) => {
ioc.bind('Test/Bull', () => {
return class {
static get key() {
return 'TestBull-name'
}
async handle() {}
}
})
const bull = new Queue(
console,
ioc.use('Config'),
['Test/Bull'],
ioc,
resolver
)
const Job = ioc.use('Test/Bull')
const data = { test: 'data' }
const repeat = { cron: '* * * * *' }
await bull.add(Job.key, data, { repeat })
let jobs = await bull.getRepeatableJobs(Job.key)
assert.equal(jobs.length, 1)
assert.deepEqual(repeat.cron, jobs[0].cron)
await bull.removeRepeatable(Job.key, repeat)
jobs = await bull.getRepeatableJobs(Job.key)
assert.equal(jobs.length, 0)
})
})
gitextract_8sjho86i/
├── .editorconfig
├── .eslintrc.js
├── .github/
│ ├── CODEOWNERS
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ ├── documentation-report.md
│ │ └── enhancement-request.md
│ ├── PULL_REQUEST_TEMPLATE
│ └── workflows/
│ ├── commit.yml
│ ├── coveralls.yml
│ ├── lint.yml
│ └── nodejs.yml
├── .gitignore
├── .prettierrc
├── LICENSE.md
├── README.md
├── japaFile.js
├── package.json
├── providers/
│ ├── Bull.js
│ └── Command.js
├── src/
│ ├── Commands/
│ │ └── Listen.js
│ └── Queue.js
└── test/
├── functional/
│ ├── app/
│ │ └── SomeJob.js
│ ├── providers.spec.js
│ └── start/
│ └── jobs.js
└── unit/
└── queue.spec.js
SYMBOL INDEX (41 symbols across 6 files)
FILE: providers/Bull.js
class BullProvider (line 4) | class BullProvider extends ServiceProvider {
method register (line 5) | register() {
FILE: providers/Command.js
class MigrationsProvider (line 5) | class MigrationsProvider extends ServiceProvider {
method register (line 6) | register() {
method boot (line 12) | boot() {
FILE: src/Commands/Listen.js
class Listen (line 5) | class Listen extends Command {
method inject (line 6) | static get inject() {
method constructor (line 10) | constructor(Bull) {
method signature (line 15) | static get signature() {
method description (line 24) | static get description() {
method handle (line 28) | async handle(args, { board = false, boardHostname = 'localhost', board...
FILE: src/Queue.js
class Queue (line 11) | class Queue {
method constructor (line 12) | constructor(Logger, Config, jobs, app, resolver) {
method _getJobListeners (line 27) | _getJobListeners(Job) {
method queues (line 40) | get queues() {
method get (line 66) | get(name) {
method add (line 70) | add(name, data, options) {
method removeRepeatable (line 78) | removeRepeatable(name, repeat) {
method getRepeatableJobs (line 84) | getRepeatableJobs(name) {
method schedule (line 91) | schedule(name, data, date, options) {
method ui (line 116) | ui(port = 9999, hostname = 'localhost') {
method remove (line 138) | async remove(name, jobId) {
method handleException (line 145) | handleException(error, job) {
method process (line 162) | process() {
FILE: test/functional/app/SomeJob.js
class SomeJob (line 1) | class SomeJob {
method key (line 2) | static get key() {
method handle (line 6) | async handle() {
FILE: test/unit/queue.spec.js
method key (line 54) | static get key() {
method concurrency (line 58) | static get concurrency() {
method handle (line 62) | async handle() {}
method key (line 89) | static get key() {
method onCompleted (line 93) | onCompleted() {
method handle (line 97) | async handle() {}
method key (line 120) | static get key() {
method handle (line 124) | async handle() {}
method key (line 150) | static get key() {
method handle (line 154) | async handle() {}
method key (line 178) | static get key() {
method handle (line 182) | async handle() {}
method key (line 208) | static get key() {
method handle (line 212) | async handle() {}
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (47K chars).
[
{
"path": ".editorconfig",
"chars": 229,
"preview": "# EditorConfig is awesome: https://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n[*]\nindent_style = space\n"
},
{
"path": ".eslintrc.js",
"chars": 303,
"preview": "module.exports = {\n env: {\n es6: true,\n node: true,\n },\n plugins: ['prettier'],\n extends: ['standard', 'pretti"
},
{
"path": ".github/CODEOWNERS",
"chars": 150,
"preview": "# Learn how to add code owners here:\n# https://help.github.com/en/articles/about-code-owners\n\n* @jpedroschmi"
},
{
"path": ".github/CODE_OF_CONDUCT.md",
"chars": 4180,
"preview": "# Code of Conduct\n\nThis Code of Conduct outlines our expectations for participants within the Rocketseat community as we"
},
{
"path": ".github/CONTRIBUTING.md",
"chars": 8143,
"preview": "# Contribution guidelines\n\n## Table of Contents\n\n- [Getting started](#getting-started)\n - [Language](#language)\n - ["
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 1185,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\n---\n\n<!-- Verify first that your issue is not already rep"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 28,
"preview": "blank_issues_enabled: false\n"
},
{
"path": ".github/ISSUE_TEMPLATE/documentation-report.md",
"chars": 491,
"preview": "---\nname: Documentation report\nabout: Use this template for documentation related issues\n---\n\n<!-- Verify first that you"
},
{
"path": ".github/ISSUE_TEMPLATE/enhancement-request.md",
"chars": 785,
"preview": "---\nname: Enhancement request\nabout: Suggest an idea for this project\n---\n\n<!-- Verify first that your issue is not alre"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE",
"chars": 784,
"preview": "<!-- If this is your first time, please read our contribution guidelines: (https://github.com/Rocketseat/adonis-bull/blo"
},
{
"path": ".github/workflows/commit.yml",
"chars": 508,
"preview": "name: Lint Commit Messages\n\non: [pull_request]\n\njobs:\n commit:\n name: Lint commit messages\n runs-on: ubuntu-lates"
},
{
"path": ".github/workflows/coveralls.yml",
"chars": 996,
"preview": "name: Coveralls\n\non: [pull_request]\nenv:\n CI: true\n COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}\n NODE_E"
},
{
"path": ".github/workflows/lint.yml",
"chars": 419,
"preview": "name: Lint\n\non: [pull_request]\n\njobs:\n code:\n name: Lint code\n runs-on: ubuntu-latest\n\n steps:\n - name: C"
},
{
"path": ".github/workflows/nodejs.yml",
"chars": 796,
"preview": "name: Node.js\n\non: [push, pull_request]\nenv:\n CI: true\n COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}\n\njob"
},
{
"path": ".gitignore",
"chars": 1070,
"preview": "# Created by https://www.gitignore.io/api/node\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error"
},
{
"path": ".prettierrc",
"chars": 43,
"preview": "{\n \"singleQuote\": true,\n \"semi\": false\n}\n"
},
{
"path": "LICENSE.md",
"chars": 1067,
"preview": "MIT License\n\nCopyright (c) 2021 Rocketseat\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
},
{
"path": "README.md",
"chars": 7249,
"preview": "<br />\n\n<img width=\"275\" alt=\"Adonis Bull\" src=\"https://user-images.githubusercontent.com/16545335/71373449-f2146880-259"
},
{
"path": "japaFile.js",
"chars": 85,
"preview": "const { configure } = require('japa')\nconfigure({\n files: ['test/**/*.spec.js'],\n})\n"
},
{
"path": "package.json",
"chars": 1877,
"preview": "{\n \"name\": \"@rocketseat/adonis-bull\",\n \"version\": \"0.3.0\",\n \"main\": \"src/Queue.js\",\n \"license\": \"MIT\",\n \"scripts\": "
},
{
"path": "providers/Bull.js",
"chars": 636,
"preview": "const { ServiceProvider, resolver } = require('@adonisjs/fold')\nconst path = require('path')\n\nclass BullProvider extends"
},
{
"path": "providers/Command.js",
"chars": 394,
"preview": "'use strict'\n\nconst { ServiceProvider } = require('@adonisjs/fold')\n\nclass MigrationsProvider extends ServiceProvider {\n"
},
{
"path": "src/Commands/Listen.js",
"chars": 715,
"preview": "'use strict'\n\nconst { Command } = require('@adonisjs/ace')\n\nclass Listen extends Command {\n static get inject() {\n r"
},
{
"path": "src/Queue.js",
"chars": 4880,
"preview": "'use strict'\n\nconst Bull = require('bull')\nconst BullBoard = require('bull-board')\nconst humanInterval = require('human-"
},
{
"path": "test/functional/app/SomeJob.js",
"chars": 141,
"preview": "class SomeJob {\n static get key() {\n return 'SomeJob-key'\n }\n\n async handle() {\n return 'good luck'\n }\n}\n\nmodu"
},
{
"path": "test/functional/providers.spec.js",
"chars": 1392,
"preview": "const path = require('path')\nconst test = require('japa')\nconst { ioc, registrar, resolver } = require('@adonisjs/fold')"
},
{
"path": "test/functional/start/jobs.js",
"chars": 33,
"preview": "module.exports = ['App/SomeJob']\n"
},
{
"path": "test/unit/queue.spec.js",
"chars": 5108,
"preview": "const test = require('japa')\nconst delay = require('delay')\nconst { ioc, registrar, resolver } = require('@adonisjs/fold"
}
]
About this extraction
This page contains the full source code of the Rocketseat/adonis-bull GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (42.7 KB), approximately 11.5k tokens, and a symbol index with 41 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.