Get form key from historic task - camunda

We get the form key from task service likes following snipped code
for (Task task : getTaskService().createTaskQuery().taskCandidateGroupIn(candidateGroup).initializeFormKeys().list()) {
task.getFormKey()
....
....
...
}
but now for some special reason we wanna get the form key value from HistoricTaskInstance, and we try several ways to get it but all of them fail.
We are wondering that how we can get the form key value from completed task?

The form key is not available for historic tasks. Usually forms are not displayed for historic tasks since the tasks have been completed. If the task has not been completed (history contains both active and completed tasks), then you can use the id of the historic task to get the form key using the form service.
If the task has already been completed, then you need to use the model api to get the form key from the XML:
HistoricTaskInstance historicTask = historyService.createHistoricTaskInstanceQuery().singleResult();
BpmnModelInstance bpmnModelInstance = repositoryService.getBpmnModelInstance(historicTask.getProcessDefinitionId());
org.camunda.bpm.model.bpmn.instance.Task task = bpmnModelInstance.getModelElementById(historicTask.getTaskDefinitionKey());
String formKey = task.getAttributeValueNs(BpmnModelConstants.CAMUNDA_NS, "formKey");

Related

Django function execution

In views, I have a function defined which is executed when the user submits the form online. After the form submission there are some database transactions that I perform and then based on the existing data in the database API's are triggered:
triggerapi():
execute API to send Email to the user and the administrator about
the submitted form
def databasetransactions():
check the data in the submitted form with the data in DB
if the last data submitted by the user is before 10 mins or more:
triggerapi()
def formsubmitted(request):
save the user input in variables
Databasetransactions()
save the data from the submitted form in the DB
In the above case, the user clicks on submit button 2 times in less than 5 milliseond duration. So 2 parallel data starts to process and both trigger Email which is not the desired behavior.
Is there a way to avoid this ? So that for a user session, the application should only accept the data once all the older data processing is completed ?
Since we are talking in pseudo-code, one way could be to use a singleton pattern for triggerapi() and return Not Allowed in case it is already istantiated.
There are multiple ways to solve this issue.
One of them would be to create a new session variable
request.session['activetransaction'] = True
This would however require you to pass request, unless it is already passed and we got a changed code portion. You can also add an instance/ class flag for it in the same way and check with it.
Another way, which might work if you need those submissions handled after the previous one, you can always add a while request.session['activetransaction']: and do the handling afterwards.
def formsubmitted(request):
if 'activetransaction' not in request.session or not request.session['activetransaction']:
request.session['activetransaction'] = True
# save the user input in variables
Databasetransactions()
# save the data from the submitted form in the DB
request.session['activetransaction'] = False
...

django viewflow - StartFunction not assigning task owner info

Followed the answer provided to How to create a django ViewFlow process programmatically
However it is not assigning (or persisting) owner info in the activation record.
#flow_start_view
def start_process(request):
request.activation.prepare(request.POST or None,)
request.activation.flow_task.owner = request.user
request.activation.flow_task.task_title = "Start Process"
Also tried below and it is resulting in an error "'ManagedStartViewActivation' object has no attribute 'assign'"
#flow_start_view
def start_process(request):
request.activation.prepare(request.POST or None,)
request.activation.assign(request.user)
request.activation.flow_task.task_title = "Start Process"
That's hard to understand what's you are going to achieve with that. #start_flow_view is the decorator for the Django view.That means that process started manually by a user through the browser.
StartActivation class has ho assign method.
http://docs.viewflow.io/viewflow_core_activation.html#viewflow.activation.StartActivation
To assign a task means preventing an existing task to be executed by another user. Start task instances does not exist in the database. Each new start view invocation creates new process instance started with new start task instance.
If you need to track a user who performed a start task, you can directly initialize a start activation with a user instance
self.activation.prepare(request.POST or None, user=request.user)
Or just use viewflow StartFlowMixinfor your class based view.

FK validation within Django

Good afternoon,
I have my django server running with a REST api on top to serve my mobile devices. Now, at some point, the mobile device will communicate with Django.
Let's say the device is asking Django to add an object in the database, and within that object, I need to set a FK like this:
objectA = ObjectA.objects.create(title=title,
category_id = c_id, order = order, equipment_id = e_id,
info_maintenance = info_m, info_security = info_s,
info_general = info_g, alphabetical_notation = alphabetical_notation,
allow_comments = allow_comments,
added_by_id = user_id,
last_modified_by_id = user_id)
If the e_id and c_id is received from my mobile devices, should I check before calling this creation if they actually still exists in the DB? That is two extra queries... but if they can avoid any problems, I don't mind!
Thanks a lot!
It think that Django creates constraint on Foreign Key by default ( might depend on database though ). This means that if your foreign keys point to something that does not exist, then saving will fail ( resulting in Exception on Python side ).
You can reduce it to a single query (it should be a single query at least, warning I haven't tested the code):
if MyObject.objects.filter(id__in=[e_id, c_id]).distinct().count() == 2:
# create the object
ObjectA.objects.create(...)
else:
# objects corresponding e_id and c_id do not exist, do NOT create ObjectA
You should always validate any information that's coming from a user or that can be altered by a determined user. It wouldn't be difficult for someone to sniff the traffic and start constructing their own REST requests to your server. Always clean and validate external data that's being added to the system.

Implementing unread/read checking for a message

I'm having a message model. To this model I want to add a read/unread field, which I did by using a boolean field. Now, if someone reads this message, I want this boolean field to be turned to true. I access these messages at different parts in my app, so updating the field manually is going to be tedious.
Is there any way I can get some messages according to some condition, and when the message is fetched from db, the field gets auto updated?
Why don't you create a read_message() method on a custom model manager. Have this method return the messages you want, whilst also updating the field on each message returned.
You new method allow you to replace Message.objects.get() with Message.objects.read_message()
class MessageManager(models.Manager):
def read_message(self, message_id):
# This won't fail quietly it'll raise an ObjectDoesNotExist exception
message = super(MessageManager, self).get(pk=message_id)
message.read = True
message.save()
return message
Then include the manager on your model -
class Message(models.Model):
objects = MessageManager()
Obviously you could write other methods that return querysets whilst marking all the messages returned as read.
If you don't want to update your code (places where you call Message.objects.get()), then you could always actually override get() so that it updates the read field. Just replace the read_message function name above with get.
Depending on your database management system, you may be able to install a trigger:
PostgreSQL: http://www.postgresql.org/docs/9.1/static/sql-createtrigger.html
MySQL: http://dev.mysql.com/doc/refman/5.0/en/triggers.html
SQLite: http://www.sqlite.org/lang_createtrigger.html
Of course, this will need to be done manually in the database - outside of the Django application.

How to avoid MIXED_DML_OPERATION error in Salesforce tests that create Users

Sometimes in Salesforce tests you need to create User objects to run part of the test as a speciifc type of user.
However since the Salesforce Summer 08 update, attempts to create both User objects and normal objects (such as Accounts) in the same test lead to the following error:
MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): User, original object: Account
Note that the error doesn't happen when you run the tests from Eclipse/Force.com IDE, but it does happen when you deploy to Salesforce and then run the tests from within Salesforce.
How do I re-write my tests to avoid this error?
Here's a simple example of a test that causes the error:
static testMethod void test_mixed_dmlbug() {
Profile p = [select id from profile where name='(some profile)'];
UserRole r = [Select id from userrole where name='(some role)'];
User u = new User(alias = 'standt', email='standarduser#testorg.com',
emailencodingkey='UTF-8', lastname='Testing',
languagelocalekey='en_US',
localesidkey='en_US', profileid = p.Id, userroleid = r.Id,
timezonesidkey='America/Los_Angeles',
username='standarduser#testorg.com');
Account a = new Account(Firstname='Terry', Lastname='Testperson');
insert a;
System.runAs(u) {
a.PersonEmail = 'test#madeupaddress.com';
update a;
}
}
Not many Salesforce people on here yet, I guess.
I found a solution, I don't know why it works, but it works.
All parts of the test that access normal objects need to be wrapped in a System.runAs that explicitly uses the current user, like this:
User thisUser = [ select Id from User where Id = :UserInfo.getUserId() ];
System.runAs ( thisUser ) {
// put test setup code in here
}
So, the example text_mixed_dmlbug method given in the question, would become:
static testMethod void test_mixed_dmlbug() {
User u;
Account a;
User thisUser = [ select Id from User where Id = :UserInfo.getUserId() ];
System.runAs ( thisUser ) {
Profile p = [select id from profile where name='(some profile)'];
UserRole r = [Select id from userrole where name='(some role)'];
u = new User(alias = 'standt', email='standarduser#testorg.com',
emailencodingkey='UTF-8', lastname='Testing',
languagelocalekey='en_US',
localesidkey='en_US', profileid = p.Id, userroleid = r.Id,
timezonesidkey='America/Los_Angeles',
username='standarduser#testorg.com');
a = new Account(Firstname='Terry', Lastname='Testperson');
insert a;
}
System.runAs(u) {
a.PersonEmail = 'test#madeupaddress.com';
update a;
}
}
Then the MIXED_DML_OPERATION errors stop happening.
It seems like you've found a workaround. I just wanted to try and clear up why you where getting this error.
I think you are running into this issue (per http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_dml_non_mix_sobjects.htm):
sObjects That Cannot Be Used Together in DML Operations
Some sObjects require that you perform DML operations on only one type per transaction. For example, you cannot insert an account, then insert a user or a group member in a single transaction. The following sObjects cannot be used together in a transaction:
* Group1
* GroupMember
* QueueSObject
* User2
* UserRole
* UserTerritory
* Territory
Important The primary exception to
this is when you are using the runAs
method in a test.
In addition, the Summer 08 Release notes (that link is a PDF) say:
In previous releases, in a single
transaction that involved triggers,
you could perform DML operations on
more than one type of sObject, for
example, you could insert an account,
then insert a user. As of Summer
'08, you can only perform DML
operations on a single type of sObject
from the following list of sObjects.
For example, you cannot insert an
account, then insert a user, or update
a group, then insert a group
member.
Group
GroupMember
QueueSObject
User
UserRole
UserTerritory
Territory
In addition, User and Territory now
support the insert and update DML
operations, and UserRole
now supports the insert, update delete
and upsert DML operations.
Apex DML operations are not supported
on the following sObjects:
AccountTerritoryAssignmentRule
AccountTerritoryAssignmentRuleItem
UserAccountTeamMember
This behavior is actually documented in the salesforce documentation: http://www.salesforce.com/us/developer/docs/apexcode/index_Left.htm#StartTopic=Content/apex_dml_non_mix_sobjects.htm?SearchType. Read where it say "Important
The primary exception to this is when you are using the runAs method in a test"
Just found this in the documentation:
Other Uses of runAs
You can also use the runAs method to perform mixed DML operations in your test by enclosing the DML operations within the runAs block. In this way, you bypass the mixed DML error that is otherwise returned when inserting or updating setup objects together with other sObjects. See sObjects That Cannot Be Used Together in DML Operations.
So it looks like the RunAs workaround is not a workaround but is assumed by Salesforce as the only way of going by the mixed DML issue.
Hope this helps
Reference
This error is so common when attempting to create user and other objects records in a single transaction in apex.
Workaround in apex class/trigger : use future method for creating user when encountered the error
Workaround in test class : don't try creating a new user data, instead use ))>
code-snippet at -
https://thesalesforcedev.blogspot.com/2019/07/mixeddmloperation-dml-operation-on.html