Git Product home page Git Product logo

adonis-notifications's Introduction

Adonis Notifications

Send notifications with ease

npm-image license-image typescript-image

Pre-requisites

The @verful/notifications package requires @adonisjs/core >= 5.4.2

Also, it relies on @adonisjs/lucid >= 16.3.2 for database notifications and on @adonisjs/mail >= 7.2.4 for mail notifications.

Setup

Install the package from the npm registry as follows.

npm i @verful/notifications
# or
yarn add @verful/notifications

Next, configure the package by running the following ace command.

node ace configure @verful/notifications

Generating Notifications

Notifications are represented by a simple class, generally stored in the app/Notifications directory. If you dont see the directory, dont worry, it will be created when you run the make:notification ace command.

node ace make:notification TestNotification

The command will create a notification class in the app/Notifications directory. Each notification class contains a via method and any number of message builder methods, like toMail or toDatabase, that convert the notification to a message made for that channel.

Sending Notifications

Notifications may be sent using the notify or the notifyLater methods of the Notifiable mixin, or using the Notification module.

Using the Notifiable Mixin

First, apply the mixin on the model you are wanting to notify.

import { BaseModel } from '@ioc:Adonis/Lucid/Orm'
import { compose } from '@ioc:Adonis/Core/Helpers'
import { Notifiable } from '@ioc:Verful/Notification/Mixins'

// Notifiable takes the notification table name as it's only param 
export default class User extends compose(BaseModel, Notifiable('notifications')){
}

Then use the notify or the notifyLater methods to notify the model.

import TestNotification from 'App/Notifications/TestNotification'

user.notify(new TestNotification())
// Uses a in-memory queue to send the notification
user.notifyLater(new TestNotification())

Using the Notification module

You can also use the Notification module to send notifications. Sending notifications this way is useful when you need to send a notification to multiple notifiables, like a array of users.

import Notification from '@ioc:Verful/Notification'

Notification.send(users, new TestNotification())

You can also delay notifications using the sendLater method. This method uses a in-memory queue to send the notifications.

import Notification from '@ioc:Verful/Notification'

Notification.sendLater(users, new TestNotification())

Specifying Delivery Channels

Every notification class has a via method that determines which channels will be used to deliver the notification.

If you want to use other delivery channels, you can build your own.

The via method receives a notifiable instance, that is a instance of the class which the notification is being sent. You may use the notifiable to determine which channels to sent the notification to.

class TestNotification implements NotificationContract {
  public via(notifiable: User){
    return notifiable.prefersEmail ? 'mail' : 'database'
  }
}

Delaying notifications

Sending notifications can take some time, to ensure the notification doesn't block HTTP requests, you can use the notifyLater method of the Notifiable Mixin and the sendLater method of the Notification module to push notifications to a in-memory queue, ensuring that notifications will be sent after the http request ends.

Mail Notifications

If you want to send a notification via e-mail, you should define a toMail method on the notification class. This method receives the notifiable entity and should return a BaseMailer instance

If you want to use a mail driver other than default driver to send the notification, you can define it in the mailer class

class TestMailer extends BaseMailer {
  constructor(private user: User){
    super()
  }

  public prepare(message){
    message
      .subject('Test email')
      .from('[email protected]')
      .to(this.user.email)
  }
}

class TestNotification implements NotificationContract {
  public toMail(notifiable: User){
    return new TestMailer(notifiable)
  }
}

Mail notifications requires @adonisjs/mail >= 7.2.4

Database Notifications

The database channel stores the notification in a database table. This table contain the notification, and a JSON object that describes the notification

Database notifications requires @adonisjs/lucid >= 16.3.2

You can query the table to display the notifications in your UI. But, before you can do that, you need to create a table to store the notifications. You may use the notifications:table ace command to generate a migration with the correct table schema.

node ace notifications:table

node ace migration:run

Sending Database Notifications

If you want to store a notification in a database, you should define a toDatabase method on the notification class. This method receives the notifiable entity and should return a javascript object that can be transformed in JSON

class TestNotification implements NotificationContract {
  public toDatabase(notifiable: User){
    return {
      title: `Hello, ${notifiable.email}, this is a test notification`
    }
  }
}

Accessing the notifications

After notifications are stored, you can acess them from your notifiable model entities. The Notifiable mixin includes a notifications Lucid relationship that returns the notifications for that entity. You can use the notifications like any other Lucid relationship. By default, the readNotifications and unreadNotifications methods will sort the notifications using the created_at timestamp, with the most recent at the beginning.

const user = User.findOrFail(1)

for(const notification of await user.readNotifications()){
  console.log(notification.data)
}

If you want to retrieve only the unread notifications, you may use the unreadNotifications method.

const user = User.findOrFail(1)

for(const notification of await user.unreadNotifications()){
  console.log(notification.data)
}

The notifications are normal Lucid Models, you can use anything that applies to a Lucid Model on them

Marking notifications as read

Typically, you will want to mark a notification as read when a user views it. The notification model provides a markAsRead method, which updates the read_at column on the notification's database record:

const user = User.findOrFail(1)

for(const notification of await user.unreadNotifications()){
  await notification.markAsRead();
}

If you want to mark all notifications of a user as read, you can use the markNotificationsAsRead method of the Notifiable mixin

const user = User.findOrFail(1)

await user.markNotificationsAsRead()

There is also markAsRead and markNotificationsAsUnread methods to mark notifications as unread.

Custom Channels

You may want to deliver notifications using other channels, for that, you can use any class that implements the NotificationChannelContract

import { NotificationChannelContract } from '@ioc:Verful/Notification'

interface VoiceMessageContract {
  text: string
}

export default class VoiceChannel implements NotificationChannelContract {
  /**
   * Typing the notification argument guarantees type safety in the toChannel
   * method of the notification, in this case toVoice
   */
  public send(notification: VoiceMessageContract, notifiable: NotifiableModel){}
}

After the channel is created, you must extend the Notification module, you can use a preload or a provider to do this

// start/notification.ts
import Notification from '@ioc:Verful/Notification'
import VoiceChannel from 'App/Channels/VoiceChannel'

Notification.extend('voice', () => new VoiceChannel())

Then you must setup the config and contract for your channel

// config/notification.ts
{
  channels: {
    voice: {
      driver: 'voice'
    }
  }
}

// contracts/notification.ts
interface NotificationChannelsList {
  voice: {
    implementation: VoiceChannel
    config: {
      driver: 'voice'
    }
  }
}

adonis-notifications's People

Contributors

arthur-er avatar dependabot[bot] avatar hasanashab avatar justdare avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

adonis-notifications's Issues

Update Lucid dependency

First of all, thank you for this package, I almost started working on my own implementation.

Could you please update the @adonisjs/lucid dependency for this packake to ^17...?. Installation fails cause I have the latest version.

Handle Event

Description

how to handle notification creation event

Package version

"@verful/notifications": "^2.3.0",

Using Adonis notification with Adonis permission package

Question

How can i use the notifications package with the permissions package. Particularly how will i be able to compose the extends behaviour on the user model for both the notifications mixin and the permissions mixin

Package version

2.0.3

No Exception Handler

Thank you for this great plugin, I really want to know if their is away i can capture response from message channels and use them inside notification class. USE CASE i have an sms channel that uses external api to send message, I want to capture and use api responses inside notification class.

No such table has_database_notifications_models

Getting database error when inserting user

Description

here is my User model which have many traits, including Notifiable

import BaseModel from "App/Models/BaseModel";
import { column, hasOne, HasOne, beforeSave } from '@ioc:Adonis/Lucid/Orm'
import { attachment, AttachmentContract } from '@ioc:Adonis/Addons/AttachmentLite'
import { Exception } from '@adonisjs/core/build/standalone';
import { compose } from '@poppinss/utils/build/helpers'
import Hash from '@ioc:Adonis/Core/Hash';
import Settings from 'App/Models/Settings'
import HasFactory from 'App/Models/Traits/HasFactory'
import HasTimestamps from 'App/Models/Traits/HasTimestamps'
import HasApiTokens from 'App/Models/Traits/HasApiTokens'
import { Notifiable } from '@ioc:Verful/Notification/Mixins'
import InvalidPasswordException from 'App/Exceptions/InvalidPasswordException'
import PasswordChangeNotAllowedException from 'App/Exceptions/PasswordChangeNotAllowedException'


export default class User extends compose(BaseModel, HasFactory, HasTimestamps, HasApiTokens, Notifiable('notifications')) {
	...
}

When i insert a new user, i get this Error

no such table has_database_notifications_models

But everything works fine if my model had only Notifiable trait

Solution

One possible solution is makeing your mixin classes anynomous

From this

function HasDatabaseNotifications(notificationsTable: string): HasDatabaseNotificationsMixin {
  const DatabaseNotification = createNotificationModel(notificationsTable)

  return (superclass) => {
    class HasDatabaseNotificationsModel
      extends superclass
      implements HasDatabaseNotificationsModelContract
    {
      @column({ isPrimary: true })
      public id: any

      @hasMany(() => DatabaseNotification, {
        localKey: 'id',
        foreignKey: 'notifiableId',
      })
      public notifications: HasMany<DatabaseNotificationModel>

      public async readNotifications(this: HasDatabaseNotificationsModel) {
        return this.related('notifications')
          .query()
          .whereNotNull('readAt')
          .orderBy('createdAt', 'desc')
      }

      public async unreadNotifications(this: HasDatabaseNotificationsModel) {
        return this.related('notifications')
          .query()
          .whereNull('readAt')
          .orderBy('createdAt', 'desc')
      }

      public async markNotificationsAsRead(this: HasDatabaseNotificationsModel) {
        await this.related('notifications').query().update({ readAt: DateTime.now().toSQL() })
      }

      public async markNotificationsAsUnread(this: HasDatabaseNotificationsModel) {
        await this.related('notifications').query().update({ readAt: null })
      }
    }
    return HasDatabaseNotificationsModel
  }
}

To this

function HasDatabaseNotifications(notificationsTable: string): HasDatabaseNotificationsMixin {
  const DatabaseNotification = createNotificationModel(notificationsTable)

  return (superclass) => {
    return class extends superclass
      implements HasDatabaseNotificationsModelContract
    {
      @column({ isPrimary: true })
      public id: any

      @hasMany(() => DatabaseNotification, {
        localKey: 'id',
        foreignKey: 'notifiableId',
      })
      public notifications: HasMany<DatabaseNotificationModel>

      public async readNotifications(this: HasDatabaseNotificationsModel) {
        return this.related('notifications')
          .query()
          .whereNotNull('readAt')
          .orderBy('createdAt', 'desc')
      }

      public async unreadNotifications(this: HasDatabaseNotificationsModel) {
        return this.related('notifications')
          .query()
          .whereNull('readAt')
          .orderBy('createdAt', 'desc')
      }

      public async markNotificationsAsRead(this: HasDatabaseNotificationsModel) {
        await this.related('notifications').query().update({ readAt: DateTime.now().toSQL() })
      }

      public async markNotificationsAsUnread(this: HasDatabaseNotificationsModel) {
        await this.related('notifications').query().update({ readAt: null })
      }
    }
  }
}

Package version

^2.1.0

Error Message & Stack Trace

Error: insert into has_database_notifications_models (created_at, email, password, role, updated_at, username, verified) values ('2024-01-15T06:05:42.700+06:00', '[email protected]', '$2a$04$gUHZhCWcCK4e6Y4p8dAk6.x72CHagHMboMLQd5mls6mOysaPLurky', 'novice', '2024-01-15T06:05:42.703+06:00', 'test14', false) - SQLITE_ERROR: no such table: has_database_notifications_models
"errno": 1,
"code": "SQLITE_ERROR",
"status": 500
}

Feature Request: Add testing capability

How i can test notification sending?

Is there any existing approach for that? (docs says nothing about that).

If not, then please add testing helpers like Laravel

Notification.fake();
 
        // Assert that no notifications were sent...
        Notification.assertNothingSent();
 
        // Assert a notification was sent to the given users...
        Notification.assertSentTo(user, OrderShipped);
 
        // Assert a notification was not sent...
        Notification.assertNotSentTo(user, AnotherNotification);
 
        // Assert that a given number of notifications were sent...
        Notification.assertCount(3);

Adonisjs 5 @verful/notifications Error

Package version

@verful/notifications": "^2.0.3"

Error Message & Stack Trace

  1. yarn add @verful/notifications
  2. node ace configure @verful/notifications
/**
 *
 * Feel free to let us know via PR, if you find something broken in this config
 * file.
 */

import { NotificationConfig } from '@ioc:Verful/Notification'

/*
|--------------------------------------------------------------------------
| Notification Mapping
|--------------------------------------------------------------------------
|
| List of available notification channels. You must first define them
| inside the `contracts/notification.ts` file before mentioning them here.
|
*/
const NotificationConfig: NotificationConfig = {
  channel: 'database',
  channels: {
    /*
    |--------------------------------------------------------------------------
    | Database channel
    |--------------------------------------------------------------------------
    |
    | Use this channel to store notifications in the database.
    |
    */
    database: {
      driver: 'database',
    },
  },
  notificationsTable: 'notifications' // ERROR IS FOUND ON THIS LINE
}

export default NotificationConfig

Relevant Information

The ERROR MESSAGE:
Type '{ channel: "database"; channels: { database: { driver: "database"; }; }; notificationsTable: string; }' is not assignable to type 'NotificationConfig'.
Object literal may only specify known properties, and 'notificationsTable' does not exist in type 'NotificationConfig'.

Type error in notification config

const NotificationConfig: NotificationConfig = {
  channel: 'database',
  channels: {
    database: {
      driver: 'database',
    },
    mail: {
      driver: 'mail',
      mailer: 'smtp' // Error: Type '{ driver: "mail"; mailer: string; }' is not assignable to type 'MailChannelConfig'.
    },
  },
  notificationsTable: 'notifications' // Error notificationsTable: string; }' is not assignable to type 'NotificationConfig'.
}

V6 support

When the pkg would be migrated to V6?

If you need help I can contribute.. :)

Is this a dead repo?

Just putting this issue here in an effort to save some future dev the effort and energy of trying to work through this disaster of a package. If this issue is still up, this package is still broken.

Docs are either woefully out of date or incomplete. Type errors and SQL errors all over the place.

The automated release is failing 🚨

🚨 The automated release from the main branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you can benefit from your bug fixes and new features again.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can fix this πŸ’ͺ.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the main branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here are some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


No npm token specified.

An npm token must be created and set in the NPM_TOKEN environment variable on your CI environment.

Please make sure to create an npm token and to set it in the NPM_TOKEN environment variable on your CI environment. The token must allow to publish to the registry https://registry.npmjs.org.


Good luck with your project ✨

Your semantic-release bot πŸ“¦πŸš€

Unexpected token o in JSON at position 1

Prerequisites

We do our best to reply to all the issues on time. If you will follow the given guidelines, the turn around time will be faster.

  • Ensure the issue isn't already reported.
  • Ensure you are reporting the bug in the correct repo.

Delete the above section and the instructions in the sections below before submitting

Description

If this is a feature request, explain why it should be added. Specific use-cases are best.

For bug reports, please provide as much relevant info as possible.

Package version

2.0.2

Error Message & Stack Trace

err: {
2022-08-02T09:57:23.591840955Z "type": "SyntaxError",
2022-08-02T09:57:23.591846064Z "message": "Unexpected token o in JSON at position 1",
2022-08-02T09:57:23.591851234Z "stack":
2022-08-02T09:57:23.591855993Z SyntaxError: Unexpected token o in JSON at position 1
2022-08-02T09:57:23.591860772Z at JSON.parse ()
2022-08-02T09:57:23.591866212Z at Object.consume (/opt/microservices/node_modules/@verful/notifications/build/src/Models/DatabaseNotification.js:41:38)
2022-08-02T09:57:23.591871723Z at /opt/microservices/node_modules/@adonisjs/lucid/build/src/Orm/BaseModel/index.js:1196:37
2022-08-02T09:57:23.591876812Z at Array.forEach ()
2022-08-02T09:57:23.591881732Z at Proxy.$consumeAdapterResult (/opt/microservices/node_modules/@adonisjs/lucid/build/src/Orm/BaseModel/index.js:1183:40)
2022-08-02T09:57:23.591886851Z at Function.$createFromAdapterResult (/opt/microservices/node_modules/@adonisjs/lucid/build/src/Orm/BaseModel/index.js:274:18)
2022-08-02T09:57:23.591891921Z at /opt/microservices/node_modules/@adonisjs/lucid/build/src/Orm/QueryBuilder/index.js:197:50
2022-08-02T09:57:23.591896890Z at Array.reduce ()
2022-08-02T09:57:23.591920064Z at HasManyQueryBuilder.execQuery (/opt/microservices/node_modules/@adonisjs/lucid/build/src/Orm/QueryBuilder/index.js:195:37)
2022-08-02T09:57:23.591925344Z at processTicksAndRejections (node:internal/process/task_queues:96:5)
2022-08-02T09:57:23.591930183Z "status": 500
2022-08-02T09:57:23.591935082Z }

Relevant Information

This is happening every time I want to access users' database notifications. I followed the instructions (pretty straightforward to be honest) but didn't come to a working solution. FYI, the database is Postgres.

I identified that this part of the code is responsible for that:

@column({ prepare: (value) => JSON.stringify(value), consume: (value) => JSON.parse(value), }) public data: Record<string, any>
from src/Models/DatabaseNotification.ts

why is stringifying and parsing necessary? Adonis handles jsons automatically, doesn't it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.