You need to sign in to do that
Don't have an account?
Stéphane Bernhardt SalesForce
Trailhead - Connect to Salesforce with Server-Side Controllers - Internal error
Hi,
I'm currently working on the "Connect to Salesforce with Server-Side Controllers" module and I'm facing an error.
The error is:
Error: An internal server error has occurred
Error ID: 615929029-122427 (-1189130377)
I already:
I did not have any issue on the previous unit, with expenses.
Please find my code below. Do you have an idea because I'm going crazy :)
Camping.cmp:
CampingItem.cmp:
campingListHelper.js:
I'm currently working on the "Connect to Salesforce with Server-Side Controllers" module and I'm facing an error.
The error is:
Error: An internal server error has occurred
Error ID: 615929029-122427 (-1189130377)
I already:
- Re-wrote the code from zero
- Checked other posts on this forum
- Checked fields security
- Tried to remove the "with sharing" from the Apex controller
I did not have any issue on the previous unit, with expenses.
Please find my code below. Do you have an idea because I'm going crazy :)
Camping.cmp:
<aura:component> <aura:attribute name="items" type="Camping_Item__c[]"/> <!-- PAGE HEADER --> <div class="slds-page-header" role="banner"> <div class="slds-grid"> <div class="slds-col"> <p class="slds-text-heading--label">Camping items</p> <h1 class="slds-text-heading--medium">Items</h1> </div> </div> </div> <!-- / PAGE HEADER --> <c:campingList items="{!v.items}"/> </aura:component>campingList.cmp:
<aura:component controller="CampingListController"> <aura:handler name="init" action="{!c.doInit}" value="{!this}"/> <aura:attribute name="items" type="Camping_Item__c[]"/> <aura:attribute name="newItem" type="Camping_Item__c" default="{ 'sobjectType': 'Expense__c', 'Name': '', 'Price__c': 0, 'Quantity__c': '', 'Packed__c': false }"/> <!-- NEW EXPENSE FORM --> <div class="slds-col slds-col--padded slds-p-top--large"> <div aria-labelledby="newitemform"> <!-- BOXED AREA --> <fieldset class="slds-box slds-theme--default slds-container--small"> <legend id="newItemform" class="slds-text-heading--small slds-p-vertical--medium"> Add Item </legend> <!-- CREATE NEW EXPENSE FORM --> <form class="slds-form--stacked"> <div class="slds-form-element slds-is-required"> <div class="slds-form-element__control"> <ui:inputText aura:id="itemname" label="Item Name" class="slds-input" labelClass="slds-form-element__label" value="{!v.newItem.Name}" required="true"/> </div> </div> <div class="slds-form-element slds-is-required"> <div class="slds-form-element__control"> <ui:inputNumber aura:id="itemprice" label="Price" class="slds-input" labelClass="slds-form-element__label" value="{!v.newItem.Price__c}" required="true"/> </div> </div> <div class="slds-form-element"> <div class="slds-form-element__control"> <ui:inputNumber aura:id="itemquantity" label="Quantity" class="slds-input" labelClass="slds-form-element__label" value="{!v.newItem.Quantity__c}"/> </div> </div> <div class="slds-form-element"> <ui:inputCheckbox aura:id="itempacked" label="Packed?" class="slds-checkbox" labelClass="slds-form-element__label" value="{!v.newItem.Packed__c}"/> </div> <div class="slds-form-element"> <ui:button label="Create Item" class="slds-button slds-button--brand" press="{!c.clickCreateItem}"/> </div> </form> <!-- / CREATE NEW EXPENSE FORM --> </fieldset> <!-- / BOXED AREA --> </div> <!-- / CREATE NEW EXPENSE --> </div> <div class="slds-card slds-p-top--medium"> <header class="slds-card__header"> <h3 class="slds-text-heading--small">Camping Items</h3> </header> <section class="slds-card__body"> <div id="list" class="row"> <aura:iteration items="{!v.items}" var="item"> <c:CampingItem item="{!item}"/> </aura:iteration> </div> </section> </div> </aura:component>
CampingItem.cmp:
<aura:component > <aura:attribute name="item" type="Camping_Item__c"/> <p>Name: <ui:outputText value="{!v.item.Name}"/> </p> <p>Price: <ui:outputNumber value="{!v.item.Price__c}"/> </p> <p>Quantity: <ui:outputNumber value="{!v.item.Quantity__c}"/> </p> <p>Packed?: <ui:outputCheckbox value="{!v.item.Packed__c}"/> </p> </aura:component>CampingListController.apxc:
public with sharing class CampingListController { @AuraEnabled public static List<Camping_Item__c> getItems() { // Perform isAccessible() checking first, then return [SELECT Name, Price__c, Quantity__c, Packed__c FROM Camping_Item__c]; } @AuraEnabled public static Camping_Item__c saveItem(Camping_Item__c newItem) { // Perform isUpdatable() checking first, then upsert newItem; return newItem; } }campingListController.js:
({ // Load items from Salesforce doInit: function(component, event, helper) { // Create the action var action = component.get("c.getItems"); // Add callback behavior for when response is received action.setCallback(this, function(response) { var state = response.getState(); if (component.isValid() && state === "SUCCESS") { component.set("v.items", response.getReturnValue()); } else { console.log("Failed with state: " + state); } }); // Send action off to be executed $A.enqueueAction(action); }, clickCreateItem: function(component, event, helper) { if(helper.validateItemForm(component)){ // Create the new item var newItem = component.get("v.newItem"); helper.createItem(component, newItem); } } })
campingListHelper.js:
({ validateItemForm: function(component) { // Simplistic error checking var validItem = true; /* // Name must not be blank var nameField = component.find("expname"); var expname = nameField.get("v.value"); if ($A.util.isEmpty(expname)){ validExpense = false; nameField.set("v.errors", [{message:"Expense name can't be blank."}]); } else { nameField.set("v.errors", null); } // Amount must be set, must be a positive number var amtField = component.find("amount"); var amt = amtField.get("v.value"); if ($A.util.isEmpty(amt) || isNaN(amt) || (amt <= 0.0)){ validExpense = false; amtField.set("v.errors", [{message:"Enter an expense amount."}]); } else { // If the amount looks good, unset any errors... amtField.set("v.errors", null); } */ return(validItem); }, createItem: function(component, newItem) { var action = component.get("c.saveItem"); alert(JSON.stringify(newItem)); action.setParams({"newItem": newItem}); action.setCallback(this, function(response){ var state = response.getState(); if (state === "ERROR") { var errors = response.getError(); if (errors && errors[0]) { console.log(errors[0]); } else { console.log("Unknown error"); } } else if (component.isValid() && state === "SUCCESS") { var items = component.get("v.items"); items.push(response.getReturnValue()); component.set("v.items", items); } }); $A.enqueueAction(action); } })Thanks !
All Answers
You're right. Thanks for you answer, I will be able to continue my trailhead module :)
After many attempts at restructuring, I finally solved it. Here's my code:
camping.cmp
<aura:component controller="CampingListController"><!-- the Apex server side controller -->
<aura:attribute name="items" type="Camping_Item__c[]"/>
<c:campingHeader />
<c:campingList items="{!v.items}"/>
</aura:component>
campingList.cmp
<aura:component controller="CampingListController"><aura:attribute name="items" type="Camping_Item__c[]"/>
<aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
<aura:attribute name="newItem" type="Camping_Item__c"
default="{'sobjectType' : 'Camping_Item__c',
'Name':'',
'Quantity__c' : '',
'Price__c' : ''}"/>
<!-- BOXED AREA -->
<fieldset class="slds-box slds-theme--default slds-container--small">
<legend id="newCampItemForm" class="slds-text-heading--small
slds-p-vertical--medium">
Add Camping Item
</legend>
<!-- CREATE NEW CAMPING ITEM FORM -->
<form class="slds-form--stacked">
<div class="slds-form-element slds-is-required">
<div class="slds-form-element__control">
<ui:inputText aura:id="campItemName" label="Camping Item Name"
class="slds-input"
labelClass="slds-form-element__label"
value="{!v.newItem.Name}"
required="true"/>
</div>
</div>
<div class="slds-form-element slds-is-required">
<div class="slds-form-element__control">
<ui:inputNumber aura:id="quantity" label="Quantity"
class="slds-input"
labelClass="slds-form-element__label"
value="{!v.newItem.Quantity__c}"
required="true"/>
</div>
</div>
<div class="slds-form-element">
<div class="slds-form-element__control">
<ui:inputCurrency aura:id="price" label="Price"
class="slds-input"
labelClass="slds-form-element__label"
value="{!v.newItem.Price__c}"/>
</div>
</div>
<div class="slds-form-element">
<ui:inputCheckbox aura:id="packed" label="Packed?"
class="slds-checkbox"
labelClass="slds-form-element__label"
value="{!v.newItem.Packed__c}"/>
</div>
<div class="slds-form-element">
<ui:button label="Create Camping Item"
class="slds-button slds-button--brand"
press="{!c.clickCreateItem}"/>
</div>
</form>
<!-- / CREATE NEW CAMPING ITEM FORM -->
</fieldset>
<!-- / BOXED AREA -->
<div class ="slds-card slds-p-top--medium">
<header class ="slds-card__header">
<h3 class = "slds-text-heading--small">Items</h3>
</header>
<section class ="slds-card__body">
<div id="list" class = "row">
<aura:iteration items="{!v.items}" var="item">
<c:campingListItem item="{!item}"/>
</aura:iteration>
</div>
</section>
</div>
</aura:component>
campingListController.js
({// Load camping items from Salesforce
doInit: function(component, event, helper) {
console.log("call c.getItems: ");
// Create the action
var action = component.get("c.getItems");
console.log("back from c.getItems: ");
// Add callback behavior for when response is received
action.setCallback(this, function(response) {
var state = response.getState();
if (component.isValid() && state === "SUCCESS") {
component.set("v.items", response.getReturnValue());
}
else {
console.log("Failed with state: " + state);
}
});
// Send action off to be executed
$A.enqueueAction(action);
}, //doInit
clickCreateItem: function(component, event, helper) {
if(helper.validateCampingItem(component)){
// Create the new camping item
var newCampingItem = component.get("v.newItem");
console.log(newCampingItem);
helper.createItem(component, newCampingItem);
}
}, //clickCreateItem
})
campingListHelper.js
({createItem: function(component, campItem) {
var action = component.get("c.saveItem");
console.log('create');
console.log('campitem='+campItem);
action.setParams({ "campItem": campItem });
console.log('back from set params');
action.setCallback(this, function(response){
var state = response.getState();
if (component.isValid() && state === "SUCCESS") {
var items = component.get("v.items");
items.push(response.getReturnValue());
component.set("v.items", items);
}
});
$A.enqueueAction(action);
},//createItem
validateCampingItem: function(component, item) {
// Simplistic error checking
var validItem = true;
console.log("validate item");
// Name must not be blank
var nameField = component.find("campItemName");
var itemname = nameField.get("v.value");
console.log("itemname="+itemname);
if ($A.util.isEmpty(itemname)){
validItem = false;
nameField.set("v.errors", [{message:"Item name can't be blank."}]);
}
else {
console.log("good name "+itemname);
nameField.set("v.errors", null);
}
// Price must not be blank
var priceField = component.find("price");
var itemprice = priceField.get("v.value");
console.log("itemprice="+itemprice);
if (isNaN(itemprice)){
validItem = false;
priceField.set("v.errors", [{message:"Item price can't be null."}]);
}
else {
priceField.set("v.errors", null);
}
if ($A.util.isEmpty(itemprice)){
validItem = false;
priceField.set("v.errors", [{message:"Item price can't be null."}]);
}
else {
priceField.set("v.errors", null);
}
if (itemprice<0.01){
validItem = false;
priceField.set("v.errors", [{message:"Item price can't be zero."}]);
}
else {
priceField.set("v.errors", null);
}
// Quantity must not be blank
var qtyField = component.find("quantity");
var itemqty = qtyField.get("v.value");
console.log("itemqty ="+itemqty);
console.log("now check empty"+qtyField);
if (isNaN(itemqty)){
console.log("bad qty ="+itemqty);
validItem = false;
qtyField.set("v.errors", [{message:"Item quantity can't be null."}]);
}
else {
console.log("good");
qtyField.set("v.errors", null);
}
if ($A.util.isEmpty(itemqty)){
console.log("bad qty ="+itemqty);
validItem = false;
qtyField.set("v.errors", [{message:"Item quantity can't be null."}]);
}
else {
console.log("good");
qtyField.set("v.errors", null);
}
if (itemqty<1){
console.log("bad qty <1 ="+itemqty);
validItem = false;
qtyField.set("v.errors", [{message:"Enter an Item quantity."}]);
}
else {
console.log("good");
qtyField.set("v.errors", null);
}
return(validItem);
} //validateCampingItem
})
campingListItem.cmp
<aura:component ><aura:attribute name="item" type="Camping_Item__c"/>
<p>Name:
<ui:outputText value="{!v.item.Name}"/>
</p>
<p>Quantity:
<ui:outputNumber value="{!v.item.Quantity__c}"/>
</p>
<p>Price:
<ui:outputCurrency value="{!v.item.Price__c}"/>
</p>
<p>Packed?:
<ui:outputCheckbox value="{!v.item.Packed__c}"/>
</p>
</aura:component>
CampingListController.apxc
public with sharing class CampingListController {@AuraEnabled
public static List<Camping_Item__c> getItems() {
// Check to make sure all fields are accessible to this user
String[] fieldsToCheck = new String[] {
'Id', 'Name', 'Quantity__c', 'Price__c', 'Packed__c' };
system.debug('fields: '+fieldsToCheck);
Map<String,Schema.SObjectField> fieldDescribeTokens =
Schema.SObjectType.Camping_Item__c.fields.getMap();
system.debug('map: '+fieldDescribeTokens);
for(String field : fieldsToCheck) {
if( ! fieldDescribeTokens.get(field).getDescribe().isAccessible()) {
throw new System.NoAccessException();
return null;
}
}
Hope this helps.
In hopes that a Salesforce employee will take pity on me and let me know the output from mine, here's the error I got:
Error ID: 39586319-139057 (119852647)
For future reference, though, how would I go about debugging this? The error output is extremely unhelpful.
To answer, "Why is there no way to view exception logs?":
Exceptions have always been suppressed so I can't say for sure what the founders of Salesforce were thinking, but my hunch is that exceptions (backtraces) provide details about your app architecture that is useful to developers but confusing to users. They could also provide an attack vector for malicious people looking for a weakness.
apex://CampingListController/ACTION$saveItem error: apex://CampingListController:11,1: common.apex.runtime.impl.ExecutionException: Attempted to upsert a null list
Hope this helps.