I've got two services in Feathers.
For the sake of example, let's call them:
TodoLists
TodoItems
TodoItems have a N:1 association to TodoLists, and I include the TodoItems model in a find hook of the TodoLists service.
Now - on my frontend I have a listener that listens for 'update' event on TodoLists.
What is the right way to get the 'update' event emitted for TodoLists when any TodoItem is updated?
Try watching on all TodoItems updates and filter by your TodoList id.
const currentTodoList = 1;
app.service('TodoItems').on('updated', (item) => {
if (item.todolist === currentTodoList){
// update ui
}
});
If you additionally want to optimize the traffic send by the backend to only send needed data, you could use some subscribe pattern (TodoListSubscription join feathers channel in create and leave in remove).
Related
I'm looking for a way to save database entities changes for some entities. I mean I need to save in a database table all changes that are done on some tables (add, modify / delete) with ability to track user which did the change.
I'm working on NextJS with a custom ExpressJS server and MYSQL database were I use Prisma as ORM. I think it's maybe possible to write an ExpressJS middleware but I have yet no idea how to do it and asking myself if any library already exist.
Usually I work on PHP Symfony and used to manage this StofDoctrineExtensionsBundle which is great and works as expected. But my current project is a Typescript project only with Express/NextJS/React/Prisma/MYSQL.
Any feedback from your knowledge will be very appreciate.
Thank's in advance.
Regards,
Gulivert
EDIT: My current API which has to be moved to Express/NextJS is still running on Symfony and the table where all changes is logged looks like this :
{
"id": 59807,
"user": "ccba6ad2-0ae8-11ec-813f-0242c0a84005",
"patient": "84c3ef66-548a-11ea-8425-0242ac140002",
"action": "update",
"logged_at": "2021-11-02 17:55:09",
"object_id": "84c3ef66-548a-11ea-8425-0242ac140002",
"object_class": "App\\Entity\\Patient",
"version": 5,
"data": "a:2:{s:10:\"birth_name\";s:2:\"--\";s:10:\"profession\";s:2:\"--\";}",
"username": "johndoe",
"object_name": "patient",
"description": null
}
Explanation about database columns:
user => relation to user table
patient => relation to patient table
action => can be "create"/"update"/delete"
logged_at => date time where the change was done
object_id => entity row ID where an entity get a change
object_class => the entity updated
version => how many time the object was change
data => all data changed during the modification
username => the username of logged user did the change
object_name => a string to identify the object modified without
using the namespace of object_class
description => a value that can be update on some specific change * during usually the action delete to keep a trace what was deleted for instance
You might find prisma middleware useful for this.
Check out the example with session data middleware which is somewhat similar to what you're doing.
For your use-case the middleware might look like something like this:
const prisma = new PrismaClient()
const contextLanguage = 'en-us' // Session state
prisma.$use(async (params, next) => {
// you can find all possible params.action values in the `PrismaAction` type in `.prisma/client/index.d.ts`.
if (params.model == '_modelWhereChangeIsTracked_' && (params.action == 'create' || params.action == "update")) {
// business logic to create an entry into the change logging table using session data of the user.
}
return next(params)
})
// this will trigger the middleware
const create = await prisma._modelWhereChangeIsTracked_.create({
data: {
foo: "bar"
},
})
However, do note that there are some performance considerations when using Prisma middleware.
You can also create express middleware for the routes where you anticipate changes that need to be logged in the change table. Personally, I would prefer this approach in most cases, especially if the number of API routes where changes need to be logged is known in advance and limited in number.
I want to store in my database all the user actions done about an entity.
For example, for 1 entity, I want to store :
Created by (= author)
Updated by
Date of creation
Date of update
I want to store the history of the actions of a user, not the last ones. I thought I could create a table with these columns :
log_id
user_id
entity_id
action (= "create" or "update" or something else)
date
And then, I could easily get the last update of my entity and display the date and the user who did it.
Is there a Symfony bundle to do this ? Should I use Monolog ?
I will do this for many entities and I'm not sure if this is the correct way to do...
Is it possible to create only one logs table to store each log about each entity ? It bothers me to create 1 logs table per entity.
Since Doctrine is event based, it's easy:
Either use an extension, like Gedmo Loggable
Or hook into Doctrine's events and log, using Monolog, everything that happens in your app.
Personally I would prefer option 2 since I'm a control maniac, it's a little more complex though. Personally I would also use Monolog so I could abstract away the way how and where the log entries are stored.
When you decide how to approach this and you will need any assistance along the way, please ask another question.
Good luck.
I don't know if that would fit what you need, but you could easily add a Listener to the symfony kernel to log every controller used.
Something like this :
class UserLogListener {
protected $authChecker;
protected $tokenStorage;
protected $entityManager;
public function __construct(TokenStorageInterface $tokenStorage, AuthorizationChecker $authChecker, EntityManager $entityManager)
{
$this->authChecker = $authChecker;
$this->tokenStorage = $tokenStorage;
$this->entityManager = $entityManager;
}
public function onKernelRequest(GetResponseEvent $event)
{
if( $this->tokenStorage->getToken() != null){
$user = $this->tokenStorage->getToken()->getUser();
$currentDate = new \Datetime();
$action = $event->getRequest()->attributes->get('_controller');
$method = $event->getRequest()->getMethod();
$userIp = $event->getRequest()->getClientIp();
$userLogRepository = $this->entityManager->getRepository(UserLog::class);
if($user instanceof User){
$userLog = new UserLog();
$userLog->setUser($user);
$userLog->setIp($userIp);
$userLog->setAction($action);
$userLog->setMethode($method);
$userLog->setDate($currentDate);
if($event->getRequest()->request && $methode=='POST'){
$userLog->setData(json_encode($event->getRequest()->request->all()));
}else{
$userLog->setData($event->getRequest()->getPathInfo());
}
$this->entityManager->persist($userLog);
$this->entityManager->flush();
}
}
}
}
What it does is add to the database (with an entity called UserLog) information about every page called. So you can know which action is made by knowing which controller is called, and you can also log the request data so you can find out what modification/creation the user did.
I am trying to use the OnDelete trigger for my Cloud Functions on Firestore. I have two collections "alerts" and "logs". The Log object has an "alertId" key. What I'm trying to do is when an Alert is deleted to delete all the corespondent logs using a cloud function.
Something like that:
exports.deleteLogs = functions.database.instance('my-app').ref('/alerts/{alertId}')
.onDelete((snap) => {
snap.ref('logs',ref => ref.where('alertId', '==', alertId)).delete();
});
You can trigger a function when a Firestore document is deleted like this:
exports.deleteUser = functions.firestore
.document('alerts/{alertID}')
.onDelete((snap, context) => {
// Get an object representing the document prior to deletion
const deletedValue = snap.data();
// From there, get the deleted alert's id and delete all logs
// with that alertId key
});
I want to expose a list of services from my DB or just return one service detail via Web API with my EF DBmodel . I used VS2012 Web API scaffolding, quite easy so far and it works and return the list of services in JSON when I hit the URL(.../api/Services). The problem is that when I want to obtain just one service URL(.../api/Services/1), I still obtain the full list of all services although when I trace it seems to return only a count of 1 object.
What happening here?
Here are the 2 controller actions.
ps: I also tried using a .Where() instead of .Find() but the result is the same in both cases.
// GET api/Services
public IEnumerable<service> Getservices()
{
var services = db.services.Include(s => s.Category).Include(s => s.Country).Include(s => s.StateProvince).Include(s => s.Territory);
return services.AsEnumerable();
}
// GET api/Services/5
public service Getservice(int id)
{
service service = db.services.Find(id);
if (service == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return service;
}
Try handling it as: var service = db.services.Single(s => s.Id == id)
First, check if your database has a single item for your query or not.
If you are querying by primary key then
db.services.SingleOrDefault(s => s.Id == id) should do.
You will need to handle the exception if you are querying on some field which may give back more than one result.
The variant of filtering (Single, SingleOrDefault, First, FirstOrDefault) that you use will depend upon the exact semantics of the code.
So the situation is that I am using Doctrine as the ORM for one of my projects.
Now I want to be able to track the changes happening on certain tables of my website without having to much extra coding for that.
For eg. I have a database which has many tables. out of that i have a table users on which I want to track the changes done
1. users has column name with value 'Raman'
2. Using update sql below i modify the row
update users set name = 'Raman Joshi' where name='Raman'
Is there any in built feature in doctrine that allows to create a log table tracking all the data level changes log that was done?
You can use a Doctrine preUpdate event listener to do this. Here's a simple example that will send changes to a logger:
use Psr\Log\LoggerInterface as Logger;
use Doctrine\ORM\Event\PreUpdateEventArgs;
class ChangeLoggerListener
{
protected $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function preUpdate(PreUpdateEventArgs $eventArgs)
{
//find out class and id of object being updated
$obj=$eventArgs->getEntity();
$class=get_class($eventArgs->getEntity());
$id=$obj->getId();
$log="$class($id) updated: ";
//find out what has changed...
$changes=$eventArgs->getEntityChangeSet();
$separator='';
foreach ($changes as $field => $values) {
$log.=$separator."$field changed from {$values[0]} to {$values[1]}";
$separator=", ";
}
//send it to logger
$this->logger->info($log);
}
}
The manual page shows how to register the listener, but if you're using Symfony, you can register the listener as a service with this in your services.yml
my.change_logger:
class: My\ExampleBundle\Listener\ChangeLoggerListener
arguments: [#logger]
tags:
- { name: doctrine.event_listener, event: preUpdate }