If your component's init action invokes an server-side method which queries(i.e Read) records, then you are fine.
For example :
<aura:handler name="init" value="{!this}" action="{!c.initializeComponent}"/>
Controller.js
({
initializeComponent: function(component, event, helper) {
// api request to my external endpoint
var action = component.get("c.getContacts");
action.setCallback(this, function(response){
var images = JSON.parse(response.getReturnValue());
component.set("v.images", images);
});
$A.enqueueAction(action, true);
})
Apex Controller :
public class LightningCtrl {
// we are good here
@AuraEnabled
public static List<Contact> getCotacts(){
return [SELECT id,Name FROM Contact];
}
}
But if the server-side method invoked during init/afterRender, does an Create/Update/Delete then, you are vulnerable to CSRF attacks.That is what the below line states:
In order to prevent CSRF attacks, do not invoke any server-side
controller method that performs a DML operation automatically as the
result of a page load. Specifically, do not invoke server-side DML
controller method as onInit handlers, or afterRender handlers (if
rendering is performed automatically on page load).
For example :
public class LightningCtrl {
@AuraEnabled
public static List<Contact> getCotacts(){
App_Visit__c visit = [SELECT Id,Count__c FROM App_Visit__c WHERE Name = 'TestApp'];
if(visit.isEmpty()){
visit = new App_Visit__c(Name = 'TestApp');
}
visit.Count__c += 1;
upsert visit; // CLEAR NO...NO...
return [SELECT id,Name FROM Contact];
}
}
I ran into something similar to what you're describing here. I had all data setup in a testSetup
method and committed no DML's in between Test.startTest()
and Test.stopTest()
, just querying a record and passing that to the data to a web service.
I also confirmed that there were no fresh DML statements by adding a Limits.getDMLStatements()
right before the call out, it returned 0. I have scoured the boards and this is the best example that's similar to the behavior I have run into.
I'm certain it's a bug on the Salesforce side, and unfortunately those can take a while to resolve and I need to have this deployed! I came up with a workaround, and I wanted to share it since it may save you (if you're still having the issue) or someone else a lot of misery!
In my mock class that I was calling during the test, I had my sample JSON responses defined as static strings. So I did a tweaked version of a response built in to the call out:
//This is one of the callout functions that I'm using to hit a REST API
global HttpResponse getAllTemplates() {
HttpRequest req = setupBaseRequest(null);
req.setEndpoint(baseEndPoint+'/templates');
req.setMethod('GET');
HttpResponse res;
if (Test.isRunningTest()) {
res = new HttpResponse();
res.setBody(MyMockClass.templateExampleJSON);
res.setStatusCode(200);
res.setStatus('OK');
}
if (!Test.isRunningTest()) res = new Http().send(req);
return res;
}
Here is my mock class (example only):
@isTest
global class MyMockClass implements HttpCalloutMock {
global static String templateExampleJSON = '{"templateName":"TestName","TemplateId":"12345"}';
global HTTPResponse respond(HTTPRequest req) {
HttpResponse res = new HttpResponse();
res.setStatusCode(200);
res.setStatus('OK');
if (req.getEndPoint().contains('/templates')) {
res.setBody(templateExampleJSON);
} else {
res.setStatusCode(400);
res.setStatus('Bad Request');
}
return res;
}
Doing it this way allows my test class to fire correctly. It also allows the actual response to go out when it's not testing. I feel like it's a little bit of a hack; however, it accomplishes my end goal of testing the functionality.
Best Answer
The restriction is only about making permanent database changes, such as a DML statement, calling a future method, etc. Modifying data in memory is allowed.