• David Lappert
  • NEWBIE
  • 0 Points
  • Member since 2015

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 3
    Questions
  • 5
    Replies
I've been looking all over the place to figure out how to approach this. I'm trying to call a web service (services/apexrest) from the same org via the client. I'm recieving the following error:
XMLHttpRequest cannot load https://cs45.salesforce.com/services/apexrest/RimsSalesforceConnectRemote. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://c.cs45.visual.force.com' is therefore not allowed access. The response had HTTP status code 401.
I noticed that the origin is different than the target.
  • Is there no way to call this web service via JavaScript?
  • Is this simply unsupported functionality?
I am able to make successful calls via workbench.developerforce.com so I'm a bit confused.

What is also interesting is that the error eludes to a CORS issue, but the 401 code according to Salesforce has more to do with a bad token.

Instead of calling the Apex Rest web service via the $http Angular JS module, I decided to try a 2nd approach.  I leveraged the sforce.connect.remoteFunction nested inside of a promise to call the Apex rest web service. Unfortunately due to the requirements, I'm now bumping into a callout depth threshold:
[{"errorCode":"APEX_ERROR","message":"System.CalloutException: Callout loop not allowed\n\nClass.RimsSalesforceConnectRemote.sendRequest: line 19, column 1\nClass.RimsSalesforceConnectRemote.doPost: line 33, column 1"}]
To clarify my architecture is as follows: 
  • Client -> Apex Rest web service -> 3rd Party API
I'm utilizing this architecture as it allows me to securely store the 3rd party's static credenitals in the Apex web service.

So far I've tried to make direct calls to the Apex Rest web service via the $http AngularJS module and now via the sforce.connection.remoteFunction.

I definitely didn't think that simply calling the Apex Rest web service would be such a pain.  Testing via developer workbench was so simple. My hair is falling out.  

I'm looking for some advice in regards to architecture as well as what is the best way to call an Apex Rest web service (/services/apexrest/) via the client.
As the title suggests, I would like to reference the non-sObject classes in my test class so I can achieve greater code coverage.  I'm relatively new to Salesforce and would like to know how I would go about including non-sObjects in Apex functional testing.  I like to create wrapper classes to cleanely package up related objets and entities, but I'm not sure how you go about testing this.

I'm currently at 54% code coverage, but need to fill the ViewModel in order to validate some of the conditions in the controller.

Controller:
public class ManageVendorProductsController{
	
	public static Id context = ApexPages.currentPage().getParameters().get('ID');
    
    public ViewModel viewModel { get; set; }
    
    public class ViewModel {
	    public Account account { get; set; }
	    public List<Product> products { get; set; }
	    public List<Vendor_Product_History__c> history { get; set; }    	
    }
    
    public class Product {
        public Reliance_Product__c relianceProduct { get; set; }
        public Boolean selected { get; set; }
    }
    
    public boolean compare(String key){
    	
        Map<String, Vendor_Products__c> vendorProductsMap = new Map<String, Vendor_Products__c>();        
        for(Vendor_Products__c vp: [SELECT Account__r.Id, Reliance_Product__r.Id FROM Vendor_Products__c WHERE Account__r.Id = :context]){
            vendorProductsMap.put(vp.Reliance_Product__r.Id, vp);
        }     
        return vendorProductsMap.containsKey(key);   
             
    }
    
    public PageReference updateVendorProducts(){
        
        ViewModel vm = new ViewModel();
        
        boolean canUpdate = TRUE;

        for(Review__c r: [SELECT Id, Account__c, RecordType.Name, Name, Is_Completed__c, In_Progress__c, Review_Type__c, Review_Stat__c FROM Review__c WHERE Account__c = :context]){
            if(r.RecordType.Name == 'Supplemental' && r.Is_Completed__c){
                canUpdate = FALSE;
            }
            else if(r.RecordType.Name == 'Initial Application' && r.Review_Stat__c == 'Vendor Booked'){
                canUpdate = FALSE;
            }
            else if(r.RecordType.Name == 'Supplemental' && r.In_Progress__c){
                canUpdate = TRUE; 
                break;   
            }
        }
        
        if(canUpdate){
            
            Datetime d = Datetime.now();     
            string currTime = d.format('MM/dd/yyyy HH:mm:ss');
            
            Set<String> relianceProductIds = new Set<String>();
            
            List<Vendor_Products__c> vendorProductsToInsert = new List<Vendor_Products__c>();
            List<Vendor_Product_History__c> vendorProductHistoryToInsert = new List<Vendor_Product_History__c>();
            
            for(Product p: viewModel.products){
            	
                if(p.selected && !compare(p.relianceProduct.id)){
                	
                    Vendor_Products__c v = new Vendor_Products__c();
                    v.Account__c = context;
                    v.Reliance_Product__c = p.relianceProduct.Id;
                    vendorProductsToInsert.add(v);
                    
                    Vendor_Product_History__c vph = new Vendor_Product_History__c();
                    vph.Related_Account__c = context;
                    vph.Related_Reliance_Product__c = p.relianceProduct.Id;
                    vph.User__c = UserInfo.getName();
                    vph.Action__c = 'Product Added';
                    vph.Timestamp__c = currTime;
                    vendorProductHistoryToInsert.add(vph);
                    
                }
                
                if(!p.selected && compare(p.relianceProduct.id)){
                	
                    relianceProductIds.add(p.relianceProduct.id);
                    
                    Vendor_Product_History__c vph = new Vendor_Product_History__c();
                    vph.Related_Account__c = context;
                    vph.Related_Reliance_Product__c = p.relianceProduct.Id;
                    vph.User__c = UserInfo.getName();
                    vph.Action__c = 'Product Removed';
                    vph.Timestamp__c = currTime;
                    vendorProductHistoryToInsert.add(vph);
                    
                }
                
            }
            
            List<Vendor_Products__c> vendorProductsToDelete = [SELECT Id, Reliance_Product__r.Id FROM Vendor_Products__c WHERE Reliance_Product__r.Id IN :relianceProductIds];
            
            if(!vendorProductsToInsert.isEmpty()){
            	try{
                	INSERT vendorProductsToInsert; 
            	}catch(Exception e){
           			ApexPages.addmessage(new ApexPages.message(ApexPages.severity.WARNING, e.getMessage()));
            	}
            }
            
            if(!vendorProductsToDelete.isEmpty()){
            	try{
                	DELETE vendorProductsToDelete;
            	}catch(Exception e){
           			ApexPages.addmessage(new ApexPages.message(ApexPages.severity.WARNING, e.getMessage()));
            	}
            }
            
            if(!vendorProductHistoryToInsert.isEmpty()){
            	try{
            		INSERT vendorProductHistoryToInsert;
            	}catch(Exception e){
           			ApexPages.addmessage(new ApexPages.message(ApexPages.severity.WARNING, e.getMessage()));
            	}
            }
            
            List<Account> accs = new List<Account>{viewModel.account};
            new UpdateRelianceText(accs);
            
            PageReference ret = new PageReference(ApexPages.currentPage().getParameters().get('retURL'));
            return ret;        
		    
        }else{
            
            ApexPages.addmessage(new ApexPages.message(ApexPages.severity.WARNING, 'Vendor Products cannot be modified without an active Supplemental Review.'));
            return null;
            
        }
                    
    }
    
    public PageReference cancel(){
    	
        PageReference ret = new PageReference(ApexPages.currentPage().getParameters().get('retURL'));
        return ret;
          
    }
    
    public ManageVendorProductsController(ApexPages.StandardSetController controller){
                
        ViewModel vm = new ViewModel();
        
        vm.account = [SELECT Id, Name, Lease_Volume__c FROM Account WHERE Id = :context];      
        
        vm.products = new List<Product>();
        for(Reliance_Product__c r: [SELECT Id, Name, Reliance_Level__c, Reliance_Sort__c, Product_Details__c FROM Reliance_Product__c]){
            Product p = new Product();
            p.relianceProduct = r;
           	p.selected = compare(r.Id);
            vm.products.add(p);
        } 
        
        vm.history = new List<Vendor_Product_History__c>([
            SELECT Related_Account__r.Name, Related_Reliance_Product__r.Name, Related_Reliance_Product__r.Reliance_Level__c, User__c, Action__c, Timestamp__c
            FROM Vendor_Product_History__c WHERE Related_Account__r.Id = :context ORDER BY Timestamp__c DESC
        ]);
        
        viewModel = vm;
        
    }   
    
}

View: 
<apex:page standardController="Vendor_Products__c" extensions="ManageVendorProductsController" recordSetVar="vendorProducts">  
    <head>
        <apex:includescript value="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></apex:includescript>  
        <apex:includescript value="https://cdn.datatables.net/1.10.10/js/jquery.dataTables.min.js"></apex:includescript>  
        <apex:includescript value="https://cdn.datatables.net/plug-ins/1.10.11/sorting/custom-data-source/dom-checkbox.js"></apex:includescript>  
        <apex:stylesheet value="https://cdn.datatables.net/1.10.10/css/jquery.dataTables.min.css"/>
        <script>
        $.fn.dataTable.ext.type.detect.unshift(      
            function ( d ) {              
                return d === 'Low'|| d === 'Medium' || d === 'High' ? 'reliance-level' : null;              
            }          
        );
          
        $.fn.dataTable.ext.type.order['reliance-level-pre'] = function ( d ) {
            switch ( d ) {       
                case 'Low':    return 1;     
                case 'Medium': return 2;   
                case 'High':   return 3;   
            }
            return 0;    
        };
        
        $.fn.dataTable.ext.order['dom-checkbox'] = function  ( settings, col )
        {
            return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
                return $('input', td).prop('checked') ? '1' : '0';
            } );
        };       
        
        $(document).ready( function () {
            var table = $('#relianceProductTable').DataTable({
                order: [[ 2, "asc"]],
                scrollY: "400px",
                scrollCollapse: true,
                paging: false,
                columns: [
                    { "orderDataType": "dom-checkbox" },
                    null,
                    null,
                    null
                ]
                
            });

            $('#vendorProductsHistoryTable').dataTable({
                "order": [[ 2, "desc"]]
            });
            
            $('.dataTables_filter input').unbind().bind('keyup', function() {
                var searchTerm = this.value.toLowerCase();
                $.fn.dataTable.ext.search.push( function( settings, data, dataIndex ) {
                    if (table.cells({ row:dataIndex, column:0 }).nodes().to$().find('input').is(':checked')) return true;
                    for (var i=0;i<data.length;i++) {
                        if (~data[i].toLowerCase().indexOf(searchTerm)) return true;
                    }
                    return false;
                })
                table.draw();
                $.fn.dataTable.ext.search.pop();
            })
            
        });
        </script>
    </head>
    <body>
        
        <apex:form > 
            <apex:pageBlock title="Vendor Products">
                <apex:outputText >Add Reliance Products to the {!viewModel.account.Name} Account's list of Vendor Products.</apex:outputText>
                <br/><br/>
                <center>
                    <apex:commandButton action="{!updateVendorProducts}" value="Update"/>
                    <apex:commandButton action="{!cancel}" value="Cancel"/>
                </center>
                <br/>
               	<apex:pageMessages ></apex:pageMessages>
            </apex:pageBlock>
            
            <apex:pageBlock >                
                <table id="relianceProductTable" class="display">
                    <thead>
                        <tr>
                            <th>Selection</th>
                            <th>Reliance Product Name</th>
                            <th>Reliance Product Level</th>
                            <th>Reliance Product Details</th>
                        </tr>
                    </thead>
                    <tbody>
                        <apex:repeat value="{!viewModel.products}" var="p">
                            <tr>
                                <td><apex:inputCheckbox value="{!p.selected}"/></td>
                                <td>{!p.relianceProduct.name}</td>
                                <td>{!p.relianceProduct.Reliance_Level__c}</td>
                                <td>{!p.relianceProduct.Product_Details__c}</td>
                            </tr>
                        </apex:repeat>
                    </tbody>
                </table> 
            </apex:pageBlock>
        </apex:form>
        
        <apex:pageBlock title="Product History">
            <table id="vendorProductsHistoryTable" class="display">
                <thead>
                    <tr>                       
                        <th>Reliance Product Name</th>
                        <th>Action</th>
                        <th>Timestamp</th>
                        <th>User</th>
                        <th>Reliance Product Level</th>
                        <th>Account Name</th>
                    </tr>
                </thead>
                <tbody>
                    <apex:repeat value="{!viewModel.history}" var="h">
                        <tr>
                            <td>{!h.Related_Reliance_Product__r.Name}</td>
                            <td>{!h.Action__c}</td>
                            <td>{!h.Timestamp__c}</td>
                            <td>{!h.User__c}</td>
                            <td>{!h.Related_Reliance_Product__r.Reliance_Level__c}</td>
                            <td>{!h.Related_Account__r.Name}</td>
                        </tr>
                    </apex:repeat>
                </tbody>                
            </table>
        </apex:pageBlock>
        
    </body>
</apex:page>

Test Class:
@isTest
private class ManageVendorProductsControllerTest {
   
    static TestMethod void test01 (){
                
        PageReference ref = Page.SearchVendorProducts;
        
        Account a = new Account();
        a.Name = 'TESTACCOUNT';
        a.BillingState = 'NJ';
        INSERT a;
        
        Reliance_Product__c rp = new Reliance_Product__c();
        rp.Reliance_Level__c = 'Medium';
        rp.Reliance_Sort__c = 1;
        rp.Name = 'TESTRELIANCEPRODUCT';
        INSERT rp;
        
        Account account = [SELECT Id FROM Account LIMIT 1];
        Reliance_Product__c relianceProduct = [SELECT Id FROM Reliance_Product__c LIMIT 1];
        
        Vendor_Products__c vp = new Vendor_Products__c();
        vp.Account__c = account.Id;
        vp.Reliance_Product__c = relianceProduct.Id;
        INSERT vp;
        
        List<Vendor_Products__c> vendorProduct = [SELECT Id FROM Vendor_Products__c];
        
        Test.setCurrentPage(ref);  
                   
        ref.getParameters().put('retURL', account.Id);     
        ref.getParameters().put('id', account.Id);   
        
        ApexPages.StandardSetController ssc = new ApexPages.StandardSetController(vendorProduct);
        ManageVendorProductsController controller = new ManageVendorProductsController(ssc);
      
       	Product p = new Product();
        p.relianceProduct = relianceProduct;
        p.selected = true;
        
        ViewModel vm = new ViewModel(); 
        vm.account = account;
        vm.products = new List<Product>{p};
            
        System.assertNotEquals(null, controller.updateVendorProducts());
        System.assertNotEquals(null, controller.cancel());
        
    }
    
}


 
So I have a unique use case I am attempting to implement and would like know if there is anyway to implement this functionality via a Field Update.

We have a multi-picklist with the following values:

- Secretary of State Review
- Existing Exposure Search
- Web Search
- Other Value N1
- Other Value N2
- Other Value N3
- Other Value N4

We are using the below formula to handle the first three Picklist (Multi-Select) values.  We would like to also handle 'N'  number of "Other Values" and output the text as "Other".

IF(INCLUDES(CompletedReviewActions__c , "Secretary of State Review"), "SoS; ", "") & 
IF(INCLUDES(CompletedReviewActions__c , "Existing Exposure Search"), "Exposure; ", "" ) & 
IF(INCLUDES(CompletedReviewActions__c , "Web Search"), "Web; ", "" ) 

Basically I would like to add a condition that handles the other 'N' values without having to directly reference them since they will all be set to "Other" in the text field and we may grow this Picklist (Multi-Select) greatly.  I'd hate have such tight coupling if there is a way to get around it.

Pseudo Code: 

IF(INCLUDES(CompletedReviewActions__c , "Secretary of State Review"), "SoS; ", "") & 
IF(INCLUDES(CompletedReviewActions__c , "Existing Exposure Search"), "Exposure; ", "" ) & 
IF(INCLUDES(CompletedReviewActions__c , "Web Search"), "Web; ", "" ) &
IF("The selected value doesn't equal the above values", "Other", "")

This has been hard to implement since each value in the Picklist (Multi-Select) isn't iterated over and evaulated on a case by case basis, but instead evaluated within the INCLUDES() function.  I would like to avoid having to do the following:

IF(
    OR(
        INCLUDES(CompletedReviewActions__c , "Other Value N1"),
        INCLUDES(CompletedReviewActions__c , "Other Value N2"),
        INCLUDES(CompletedReviewActions__c , "Other Value N3"),
        INCLUDES(CompletedReviewActions__c , "Other Value N4")
    ), "Other", "" 
)

I feel like this is not possible to implement, but I'd like to think I haven't thought of a viable solution yet :)

Thanks in advance.

 
I've been looking all over the place to figure out how to approach this. I'm trying to call a web service (services/apexrest) from the same org via the client. I'm recieving the following error:
XMLHttpRequest cannot load https://cs45.salesforce.com/services/apexrest/RimsSalesforceConnectRemote. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://c.cs45.visual.force.com' is therefore not allowed access. The response had HTTP status code 401.
I noticed that the origin is different than the target.
  • Is there no way to call this web service via JavaScript?
  • Is this simply unsupported functionality?
I am able to make successful calls via workbench.developerforce.com so I'm a bit confused.

What is also interesting is that the error eludes to a CORS issue, but the 401 code according to Salesforce has more to do with a bad token.

Instead of calling the Apex Rest web service via the $http Angular JS module, I decided to try a 2nd approach.  I leveraged the sforce.connect.remoteFunction nested inside of a promise to call the Apex rest web service. Unfortunately due to the requirements, I'm now bumping into a callout depth threshold:
[{"errorCode":"APEX_ERROR","message":"System.CalloutException: Callout loop not allowed\n\nClass.RimsSalesforceConnectRemote.sendRequest: line 19, column 1\nClass.RimsSalesforceConnectRemote.doPost: line 33, column 1"}]
To clarify my architecture is as follows: 
  • Client -> Apex Rest web service -> 3rd Party API
I'm utilizing this architecture as it allows me to securely store the 3rd party's static credenitals in the Apex web service.

So far I've tried to make direct calls to the Apex Rest web service via the $http AngularJS module and now via the sforce.connection.remoteFunction.

I definitely didn't think that simply calling the Apex Rest web service would be such a pain.  Testing via developer workbench was so simple. My hair is falling out.  

I'm looking for some advice in regards to architecture as well as what is the best way to call an Apex Rest web service (/services/apexrest/) via the client.
Hi ,

I have a piece of code, is it possible to acheive with dynamic apex code
 
list<Account> Acc = [select id,name from Account where name = 'test'];

list<id> lst = new list<id>();
for(Account a: Acc){

lst.add(a.id);
}

Adv Thnx,
VSK
  • April 26, 2016
  • Like
  • 0
So I have a unique use case I am attempting to implement and would like know if there is anyway to implement this functionality via a Field Update.

We have a multi-picklist with the following values:

- Secretary of State Review
- Existing Exposure Search
- Web Search
- Other Value N1
- Other Value N2
- Other Value N3
- Other Value N4

We are using the below formula to handle the first three Picklist (Multi-Select) values.  We would like to also handle 'N'  number of "Other Values" and output the text as "Other".

IF(INCLUDES(CompletedReviewActions__c , "Secretary of State Review"), "SoS; ", "") & 
IF(INCLUDES(CompletedReviewActions__c , "Existing Exposure Search"), "Exposure; ", "" ) & 
IF(INCLUDES(CompletedReviewActions__c , "Web Search"), "Web; ", "" ) 

Basically I would like to add a condition that handles the other 'N' values without having to directly reference them since they will all be set to "Other" in the text field and we may grow this Picklist (Multi-Select) greatly.  I'd hate have such tight coupling if there is a way to get around it.

Pseudo Code: 

IF(INCLUDES(CompletedReviewActions__c , "Secretary of State Review"), "SoS; ", "") & 
IF(INCLUDES(CompletedReviewActions__c , "Existing Exposure Search"), "Exposure; ", "" ) & 
IF(INCLUDES(CompletedReviewActions__c , "Web Search"), "Web; ", "" ) &
IF("The selected value doesn't equal the above values", "Other", "")

This has been hard to implement since each value in the Picklist (Multi-Select) isn't iterated over and evaulated on a case by case basis, but instead evaluated within the INCLUDES() function.  I would like to avoid having to do the following:

IF(
    OR(
        INCLUDES(CompletedReviewActions__c , "Other Value N1"),
        INCLUDES(CompletedReviewActions__c , "Other Value N2"),
        INCLUDES(CompletedReviewActions__c , "Other Value N3"),
        INCLUDES(CompletedReviewActions__c , "Other Value N4")
    ), "Other", "" 
)

I feel like this is not possible to implement, but I'd like to think I haven't thought of a viable solution yet :)

Thanks in advance.