I created a one-to one relationship between two tables in strapi.
As an example, suppose that Bob currently has a job, say messenger, if we assign Bob’s Job to secretary, Strapi simply reassigns the new Job, without warning that Bob was already in a job
If a person is not in a current job, it’s job would be ‘none’
I’d like to forbid the reassignment of the job, if Bob was already in a job (the user would have to assign the Bob's job to ‘none’ before assigning a new job)
In strapi, what would be the right way to forbid it (checking if the current job is not ‘none’, and, if it’s the case, stopping the assignment), using a service, a controller or a lifecycle hook?
One way to handle this in Strapi would be to use a lifecycle hook. Lifecycle hooks allow you to perform specific actions at certain stages of the CRUD operations (create, update, delete) on a model. In this case, you can use the beforeUpdate hook to check if the current job is not none before allowing the assignment of a new job:
// api/person/models/Person.js
module.exports = {
lifecycles: {
// This hook will be called before updating a person
async beforeUpdate(params, data) {
// Check if the current job is not 'none'
if (params.current.job !== 'none') {
// If the current job is not 'none', throw an error
throw new Error('Cannot reassign a job to a person who already has a job');
}
}
}
};
You can also use a service or a controller to handle this logic, but using a lifecycle hook allows you to centralize this logic and keep it separate from your business logic.
Related
I started a complex project management application and I have the challenge of building resource permissions management for different types of user profiles.
My challenge is:
User story
John is an user with a common user profile.
John creates a project in the application.
John creates several tasks and adds them to the project.
John adds an user responsible for each task.
Added users must have access to the project and the tasks to which they have been added.
John creates a specific task and adds it as a subtask to one of the project's tasks.
In this subtask John adds an user as responsible, automatically that user must have access to the subtask, the task and the project.
And at any time, John can restrict access to a project resource, such as defining that a specific user can only view tasks
The way I started.
I created a specification pattern for each use case, where I inform the variables and it returns a true or false answer.
However, I have to request for each resource and, in my opinion, this is not performative.
What I mentioned is one of the simplest cases, there are others that are more complex.
canEditTaskOnProject(): boolean {
if (!this.project) {
console.error(
`Project not provided on ${TaskPermission.name}.${this.canEditTaskOnProject.name}`
);
return false;
}
return new ProjectLeader(this.project, this.userId)
.or(new Creator(this.task, this.userId))
.or(new FullAccessTaskPermission(this.project, this.userId))
.or(new TaskResponsible(this.task, this.userId))
.or(
new RestrictTaskPermission(this.project, this.userId).and(
new Creator(this.task, this.userId).or(
new TaskResponsible(this.task, this.userId)
)
)
)
.or(
new ReadAndWriteTaskPermission(this.project, this.userId).and(
new TaskResponsible(this.task, this.userId)
)
)
.isSatisfiedBy(this.userId);
}
I would very much like suggestions from experienced people who have already done something similar. I am a beginner in the area and in the company where I work, there are no seniors.
Thank you in advance!
I found the best way using casl:
import { defineAbility } from '#casl/ability';
export default defineAbility((can, cannot) => {
can('read', 'Article');
cannot('read', 'Article', { published: false }); // inverted rule
});
This question is similar to this one I previously asked, in that I want the task to perform a Target Worker Expression check on a list of WorkerSids that I've added as one of the task's attributes. But I think this problem is different enough to warrant its own question.
My goal is to associate a "do not contact" list of WorkerSids with a Task; these are workers who should not be assigned the task (maybe the customer previously had a bad interaction with them).
I have the following workflow configuration:
{
"task_routing":{
"filters":[
{
"filter_friendly_name":"don't call self",
"expression":"1==1",
"targets":[
{
"queue":queueSid,
"expression":"(task.caller!=worker.contact_uri) and (worker.sid not in task.do_not_contact)",
"skip_if": "workers.available == 0"
},
{
"queue":automaticQueueSid
}
]
}
],
"default_filter":{
"queue":queueSid
}
}
}
When I create a task, checking the Twilio Console, I can see that the task has the following attributes:
{"from_country":"US","do_not_contact":["WORKER_SID1_HERE","WORKER_SID_2_HERE"],
... bunch of other attributes...
}
So I know that the task has successfully been assigned the array of WorkerSids as one of its attributes.
There is only one worker who is Idle and whose attributes match the queueSid TaskQueue. That worker's SID is WORKER_SID1_HERE, so the only available worker is ineligible to receive the task reservation. So what should happen is that the first target expression worker.sid not in task.do_not_contact returns false, and the task falls through to the automaticQueueSid TaskQueue.
Instead, the task remains in queueSid unassigned. The following sequence of Taskrouter events are logged:
task-queue.entered
Task TASK_SID entered TaskQueue QUEUESID_QUEUENAME
task.created
Task TASK_SID created
workflow.target-matched
Task TASK_SID matched a workflow target
workflow.entered
Task TASK_SID entered Workflow WORKFLOW_NAME
What do I need to change to get the desired workflow behavior?
Changing the skip_if to
"skip_if": "1==1"
solved the problem.
Per Twilio developer support, the worker.sid not in task.do_not_contact returns true for workers who are unavailable but are also not in do_not_contact, so the target expression still returns a set of workers, and then the "skip_if": "workers.available==0" returns false because technically there is one "available" worker--the one who is ineligible due to the do_not_contact list.
What's needed is for the skip_if to always return true, so when the first target processes the task without assigning it, the skip_if then passes it to the next target, as discussed in Taskrouter Workflow documentation:
"TaskRouter will only skip a routing step in a Workflow if:
No Reservations are immediately created when a Task enters the routing step
The Skip Timeout expression evaluates to true"
I have a Jenkins multi-branch pipeline for building artifacts and there are branches for master, *-dev etc.
I want to enable project based security on a per branch basis, ie only allow devs to run the *-dev branch jobs of the build not any other ones because doing so would have undesirable effects.
I know there is project based security, but I didn't see any per branch. Does this exist? We are behind in updating Jenkins and are currently running Jenkins 2.46.1.
Otherwise I am thinking I might have to have a separate upstream job to call the correct branch of the downstream one and make the downstream artifact job unable to be run by devs with the privilege to do so. (This sounds like overkill).
Or is there any way to accomplish this in the branch's Jenkinsfile?
Here's some Jenkinsfile groovy that will get you close to what you want:
// return the user id that caused this build; else empty string
#NonCPS
def user_id_cause() {
def CAUSE = currentBuild.rawBuild.getCause(
hudson.model.Cause.UserIdCause.class
);
return CAUSE ? CAUSE.getUserId() : "";
}
// return all groups to which the given user id belongs
#NonCPS
def groups(USER_ID) {
return Jenkins.instance.securityRealm.loadUserByUsername(USER_ID).authorities.collect{ it.toString() };
}
...
env.USER_ID_CAUSE = user_id_cause();
if (!env.BRANCH_NAME.endsWith('-dev')) {
if (env.USER_ID_CAUSE) {
if ('jenkins_admins' in groups(env.USER_ID_CAUSE)) {
echo("INFO: user id `${env.USER_ID_CAUSE}` is in the group `jenkins_admins`.");
} else {
currentBuild.result = 'ABORTED';
error("user id `${env.USER_ID_CAUSE}` is not in the group `jenkins_admins`.");
}
}
}
Caveats:
These tricks rely heavily on API functions that require "In-process Script Approval" by a Jenkins administrator.
The above example assumes the existence of the jenkins_admins group to which privileged users belong --- your user/groups situation may be very different.
In general, playing with objects returned from Jenkins API functions should be done within #NonCPS-annotated functions --- you risk java.io.NotSerializableException otherwise.
References:
https://github.com/jenkinsci/workflow-cps-plugin/blob/master/README.md
http://javadoc.jenkins-ci.org/hudson/model/Cause.UserCause.html
http://javadoc.jenkins-ci.org/hudson/model/Run.html#getCause-java.lang.Class-
http://javadoc.jenkins.io/hudson/security/SecurityRealm.html#loadUserByUsername-java.lang.String-
I am using cqrs and ddd to build my application.
I have an account entity, a transaction entity and a transactionLine entity. A transaction contains multiple transactionLines. Each transactionLine has an amount and points to an account.
If a user adds a transactionLine in a transaction that already has a transactionLine that points to the same account as the one the new transactionLine, I want to simply add the new transactionLine amount to the existing one, preventing a transaction from having two transactionLines that point to the same account.
Ex :
Before command :
transaction
transactionLine1(amount=100, account=2)
transactionLine2(amount=50, account=1)
Command :
addNewTransaction(amount=25, account=1)
Desired result :
transaction
transactionLine1(amount=100, account=2)
transactionLine2(amount=75, account=1) // Add amount (50+25) instead of two different transactionLines
instead of
transaction
transactionLine1(amount=100, account=2)
transactionLine2(amount=50, account=1)
transactionLine3(amount=25, account=1) // Error, two different transactionLines point to the same account
But I wonder if it is best to handle this in the command or the event handler.
If this case is handled by the command handler
Before command :
transaction
transactionLine1(amount=100, account=2)
transactionLine2(amount=50, account=1)
Command :
addNewTransaction(amount=25, account=1) // Detects the case
Dispatches event
transactionLineAmountChanged(transactionLine=2, amount=75)
AddTransactionLine command is received
Check if a transactionLine exists in the new transactionLine's transaction with the same account
If so, emit a transactionAmountChangedEvt event
Otherwise, emit a transactionAddedEvt event
Corresponding event handler handles the right event
If this case is handled by the event handler
Before command :
transaction
transactionLine1(amount=100, account=2)
transactionLine2(amount=50, account=1)
Command :
addNewTransaction(amount=25, account=1)
Dispatches event
transactionLineAdded(transactionLine=3, amount=25)
Handler // Detects the case
transactionLine2.amount = 75
AddTransactionLine command is received
TransactionLineAdded event is dispatched
TransactionLineAdded is handled
Check if the added transaction's transactionLine points to the same account as an existing transactionLine in this account
If so, just add the amount of the new transactionLine to the existing transactionLine
Otherwise, add a new transactionLine
Neither commands nor events should contain domain logic, only the domain should contain the domain logic. In your domain, aggregate roots represent transaction boundaries (not your transaction entities, but transactions for the logic). Handling logic within commands or events would bypass those boundaries and make your system very brittle.
The right place for that logic is the transaction entity.
So the best way would be
AddTransactionCommand finds the correct transaction entity and calls
Transaction.AddLine(...), which does the logic and publishes events of what happened
TransactionLineAddedEvent or TransactionLineChangedEvent depending on what happened.
Think of commands and events as 'containers', 'dtos' of data that you are going to need in order to hydrate your AggregateRoots or send out to the world (event) for other Bounded Contexts to consume them. That's it. Any other operation that is strictly related to your Domain has no place but your AggregateRoots, Entities and Value Objects.
You can add some 'validation' to your Commands, either by using DataAnnotations or your own implementation of a validate method.
public interface ICommand
{
void Validate();
}
public class ChangeCustomerName : ICommand
{
public string Name {get;set;}
public void Validate()
{
if(Name == "No one")
{
throw new InvalidOperationException("Sorry Aria Stark... we need a name here!");
}
}
}
I have a "task" model (collection) in my app, it has a due date and I'd like to send out notifications once the task is past due.
How should I implement the "past due" property so the system can detect "past due" at any time?
Do I set up cron job to check every minute or is there a better way?
I'd recommend using synced-cron for this. It has a nice interface, and if you expand to multiple instances, you don't have to worry about each instance trying to execute the task. Here is an example of how you could use it:
SyncedCron.add({
name: 'Notify users about past-due tasks',
schedule: function(parser) {
// check every two minutes
return parser.recur().on(2).minute();
},
job: function() {
if(Tasks.find(dueAt: {$lte: new Date}).count())
emailUsersAboutPastDueTasks()
}
});
Of course, you'd also want to record which users had been notified or run this less frequently so your users don't get bombarded with notifications.