loopback : deleted column in model instead of actual delete - mysql

I'm considering using Loopback to build a RESTFull API, internal usage. I'm currently prototyping a small portion of the API to evaluate limitations and workload.
I have a huge constraint : I'm allowed to Create/Read/Update, but to Delete, I have update the DB entry to mark it as 'deleted' (boolean in the database). I'm not allowed to physically deleted the DB entry.
I have a PersistedModel, and some relation between object (dependencies, like one object child from another).
My question is : Is there a way to override the DELETE actions done in the background and input some custom code :
mark the object as "deleted" (like an UPDATE table SET deleted = 1 WHERE id = XXX)
manually CASCADE to dependent objects
while using the DELETE api call ?
Thanks fro your advice.

To overwrite delete method you can use the next code. Create a mixin based on it and attach for every required model
module.exports = function(Model) {
Model.removeById = Model.deleteById = Model.destroyById = function(id, options, cb) {
if (cb === undefined) {
if (typeof options === 'function') {
// destroyById(id, cb)
cb = options;
}
}
cb = cb || createPromiseCallback();
Model.update({id: id, {deleted: true}, options, cb})
return cb.promise;
};
}

Related

Symfony 3.4 : how to log the history of user actions?

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.

Is there a way to fetch data in database from a Ruby-on-Rails models

I have a rails app running alongside with a rails API, there is a constant value for DAYS_LIMIT in config/initializers/constants.rb
DAYS_LIMIT = 40
DEFAULT_PRICE = 1.29
but now in the app i added an input field so that the user decide his DAYS_LIMIT.
So i want to fetch that value from the database from inside the API models.
I have placed breakpoints and can see that inside the API controller, the data is transfered from the app but not to the models.
edited as a question requested , it's a React-on-Rails app , here is the code where the new input field is save to the database (i have removed the other fields so the question look shorter)
export const saveChannel = (files) => {
return async (dispatch, getState) => {
const { channel } = getState();
const {rss_podcast_days} = channel;
const { image } = files;
const save = id ? updateChannel : createChannel;
const sub_required = subscription_required !== undefined ? subscription_required : false;
const formData = new FormData();
formData.append('channel[rss_podcast_days]', rss_podcast_days || '');
if (Object.keys(image).length) {
formData.append('channel[image]', image);
}
const channelId = await dispatch(save(formData, id));
dispatch(fetchChannel(id));
return id;
};
};
from the app controller
podcast_list = RestClient.get("#{ENV['URL_API']}/api/#{#channel.id.as_json}/podcast/list")
#podcasts = JSON.parse(podcast_list.body)
#podcasts = #podcasts.sort.reverse.to_h
this is from the API controller witch the data is transfered from the app
def index
podcasts = #channel.podcasts.published.list(params[:page], params[:items_per_page], params[:ordered_in])
render json: Podcasts::Normalizer.normalize(podcasts, #channel.station.default_podcast_price)
end
and here from the API model that i want to fetch data instead of the constants.
scope :by_days_limit, -> {with_tags.more_recent_than(Date.today - DAYS_LIMIT.days).ordered}
it should take today date minus the value (DAYS_LIMIT) from user input, but for now i get undefined local variable or method if i try to fetch directly
Bro if your class has constant like DAYS_LIMIT you can access it using that class itself for example,
class Demo
DAYS_LIMIT = 5
end
you can access that constant by Demo.DAYS_LIMIT in controller or else wherever you need it.
good luck!
ok , so i finally got it, i don't know if i should delete this thread or just tell how i did it. If it's inapropriate just tell me and i will delete this entire thread.
So here is how i did it, in the API controller i had to add my fetch so that the arguments (list) knows what i am talking about. #channel.days_limit
def index
podcasts = #channel.podcasts.published.list(params[:page], params[:items_per_page], params[:ordered_in], #channel.days_limit)
render json: Podcasts::Normalizer.normalize(podcasts, #channel.station.default_podcast_price)
end
then in the def list of the models, i added days_limit has argument
def list(page = nil, nb_items_per_page = 40, ordered_in = 'desc', days_limit)
ordered_in = ordered_in.in?(['asc', 'desc']) ? ordered_in : 'desc'
page.blank? ? by_days_limit(days_limit) : by_page(page, nb_items_per_page, ordered_in)
end
and finally in the scope of the models, i pass in the new argument
scope :by_days_limit, -> (days_limit) {with_tags.more_recent_than(Date.today - days_limit.days).ordered}
Now the user input from the app is passing to the models via the controller.

firestore onDelete event triggers other delete events

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
});

Save Model Values Without Null

Everytime i try attempt to update a row i receive an error which says "something is required". In codeigniter you can update rows without the need to set everything to null in the mysql tabel settings.
I just want to update one value not the entire row.
Is this possible?
if ($users->save() == false) {
echo "Umh, We can't update the user right now: \n";
foreach ($users->getMessages() as $message) {
echo $message, "<br>";
}
$this->flash->error("Error in updating information.");
$this->response->redirect('user/profile');
} else {
echo "Great, a new robot was saved successfully!";
$this->flash->success("Member has been updaed successfully.");
//$this->response->redirect('user/profile');
}
Your isseue happens because you have already filled table and not yet properly defined model. Phalcon is validating all fo model data BEFORE trying to save it. If you define your model with all defaults, skips etc. properly, updates will be fired on single columns as you wish.
If you have definitions, that does not allow nulls, but you need an empty or default value there anyway, you may want to use 'beforeCreate' actions in model implementations. Also if there are things with defaults to set on first insert, you may wanto to use skipAttributes method.
More information is in documentation: Working with Models. So far best bit over internet I've found.
Also, below is an example for nullable email column and NOT NULL DEFAULT '0' 'skipped' column from my working code:
public function initialize() {
$this->skipAttributesOnCreate(['skipped']);
}
public function validation()
{
if($this->email !== null) {
$this->validate(
new Email(
array(
'field' => 'email',
'required' => true,
)
)
);
if ($this->validationHasFailed() == true) {
return false;
}
}
}
You do want errors of "something is required". All you're missing are just proper implementations of defaults over models. Once you get used to those mechanics, you should find them easy to handle and with more pros than cons.
What you are doing is called an insert. To set a column to a different value in a pre-existing row is called an update.
The latter is flexible, the former in not.
I highly recommend not treating a database like this is what i feel like
Put all the data in. Null is your enemy

LINQ to SQL SubmitChanges() Inserts two Database Rows and one Child Row

I have this going me crazy,
I'm attaching a List with 1 Customer and 1 Address child record row.
Everything seems OK while debugging. 1 customer Row and 1 Address Row should inserted.
But instead I get 2 Customer Records and 1 Address Row.
I don't know why. When Attaching and looping inside the List only 1 record seen.
Any points?
[EDITED]
Code Attached:
public bool InsertUpdateCustomers(List<Customer> customerList, List<Customer> originalCustomers)
{
using (DbContext db = new DbContext(DbContext.ConnectionString))
{
db.Log = Console.Out;
List<Customer> customerCloned = new List<Customer>();
customerList.ForEach(p => customerCloned.Add(p.CloneObjectGraph()));
customerCloned.ForEach(p => p.Address =
customerList.Where(pe => pe.Id == p.Id).Single().Address.CloneObjectGraph());
customerCloned.ForEach(p =>
{
if (p.Id > 0)
{
db.Customer.Attach(p,
originalCustomers.Single(
x => x.Id == p.Id));
db.Address.Attach(p.Address,
originalCustomers.Single(
x => p.AddressId== x.AddressId).
Address);
}
});
customerCloned.ForEach(p =>
{
if (p.Id == 0)
db.Customer.InsertOnSubmit(p);
});
try
{
db.SubmitChanges(ConflictMode.ContinueOnConflict);
return true;
}
catch (Exception ex)
{
return false;
}
}
}
I have checked the Log in the output and I see indeed 2 Inserts in the table.
I don't see nothing about the Address, but inserts correctly.
It could be the foreign key problem i don't get it.
I guess you've solved this for now but I ran into a similar issue and wanted to report back my understanding of this issue for future users.
The issue, I believe, is that you are using an existing list of Customer objects retrieved from the DB using a particular DataContext. You are then creating a new DataContext in your method and with this new DataContext, you are attaching an Address object.
This Address object (assuming has a foreign key relation with Customer) creates a new Customer object in the DB since the DataContext for which SubmitChanges is called, the originalCustomer is also treated as a new record.
In other words, to avoid these problems, you must re-use the existing DataContext using which the originalCustomer List was fetched so that inserting the child record of Address doesn't trigger an entry into the parent table.
Hope this helps.