+ Start a Discussion
Rebecca Hendricks 23Rebecca Hendricks 23 

Apex Specialist Superbadge - Step 2 Unable to run successfully

I'm trying to complete the Apex Specialist Superbadge.  I have created my WarehouseCalloutService apex class and it seems to match how others have created their class.  However when I run my code and then try and check the challenge, I'm getting the error that seems many others are getting about the Challenge Not Yet Complete.... "The runWarehouseEquipmentSync method does not appear to have run successfully. Could not find a successfully completed @future job for this method. Make sure that you run this method at least one before attempting this challenge. Since this method is annotated with the @future method, you may want to wait for a few seconds to ensure that it has processed successfully."

Here is my code:
public with sharing class WarehouseCalloutService {

    private static final String endpoint = 'https://th-superbadge-apex.herokuapp.com/equipment';

    @future(callout = true)	//need this so that it knows to call to external source
    public static void runWarehouseEquipmentSync(){
        Http http = new Http();
        HttpRequest httpRequest = new HttpRequest();
        httpRequest.setEndpoint(endpoint);
        httpRequest.setMethod('GET');
        HttpResponse httpResponse = http.send(httpRequest);
        
        //if successfully get the JSON file, need to parse out to different equipment objects
        if (httpResponse.getStatusCode() == 200){ //status = "OK" (this is for GET or HEAD requests)
            List<Object> equipmentList = (List<Object>) JSON.deserializeUntyped(httpResponse.getBody());
            List<Product2> products = new List<Product2>();
            
            for(Object item: equipmentList){
                Map<String, Object> productMap = (Map<String,Object>) item;	//map of item(s) in JSON file
                Product2 product = new Product2();	//list of products to insert/update in system
                
                product.Replacement_Part__c = (Boolean) productMap.get('replacement');
                product.Cost__c = (Integer) productMap.get('cost');                
                product.Current_Inventory__c = (Integer) productMap.get('quantity');
                product.Lifespan_Months__c = (Integer) productMap.get('lifespan');
                product.Maintenance_Cycle__c = (Integer) productMap.get('maintenanceperiod');
                product.Warehouse_SKU__c = (String) productMap.get('sku');
                product.Name = (String) productMap.get('name');
                product.ProductCode = (String) productMap.get('_id');
                products.add(product);
            }
            
            if(products.size() > 0){	//only need to upsert if items actually exist
                System.debug(products);
                upsert products;
            }
        }
    }
}

According to some other strings I have found on this error (https://developer.salesforce.com/forums/?id=906F0000000kE7DIAU), this is what I've looked at and the current status of it:
  • Field Level Security for Lifespan_Months__c field on Equipment (Product2) object: Visible for All profiles
  • User-added image
  • Remote Site Settings: Added this URL as a Remote Site and confirmed it is active - https://th-superbadge-apex.herokuapp.com
  • User-added image
  • Apex Jobs: Confirmed it is listed in Apex Jobs log and that it's listing as a "Future" job type and a "Completed" status.
  • User-added image
  • Execution Log: Confirmed that it shows that 1 of 50 Future classes were executed.
  • User-added image
  • SOQL Query: Confirmed that the job was placed into the system.
  • User-added image

Any assistance as to why I am not getting a complete on this task would be much appreciated!
SandhyaSandhya (Salesforce Developers) 
Hi,

Refer below link that may help you.

https://salesforce.stackexchange.com/questions/134346/could-not-find-a-successfully-completed-future-job
 
Please mark it as solved if my reply was helpful. It will make it available for other as the proper solution.
 
Best Regards
Sandhya
 
Rebecca Hendricks 9Rebecca Hendricks 9
@Sandhya

As I mentioned, I have already ran the SOQL query as the link suggests and confirmed that the Apex job has been ran and completed.  I also did as the second comment had suggested and ensured that my endpoint had been added to the Remote Site Settings.
Christopher Thaxter 4Christopher Thaxter 4
For those who are looking - I had the same problems as above. I was able to solve it by selecting the proper Trailhead Playground on the challenge page! I had two playgrounds, but wasn't used to having two, and the one I was using was not my default. Sometimes the answer is somewhere completely separate from where you're looking!
Swapnil GuravSwapnil Gurav
I was also facing same problem. I added the Endpoint URL at Setup->Security->Remote site settings. Then I executed WarehouseCalloutService.runWarehouseEquipmentSync(); from Execute Anonymous Window. I ran query below query after this which was showing Status as Completed.
SELECT Id, JobType, MethodName, Status FROM AsyncApexJob WHERE CompletedDate = TODAY
Once it shows Completed in Query result, click on Check Challenge in Trailhead. It will complete without any issue.
Monika MekalaMonika Mekala
public with sharing class WarehouseCalloutService {

    private static final String WAREHOUSE_URL = 'https://th-superbadge-apex.herokuapp.com/equipment';
    
    @future(callout=true)
    public static void runWarehouseEquipmentSync() {
        //ToDo: complete this method to make the callout (using @future) to the
        //      REST endpoint and update equipment on hand.
    
		
		HttpResponse response = getResponse(); 
        if(response.getStatusCode() == 200)
        {
            List<Product2> results = getProductList(response); //get list of products from Http callout response
            
            if(results.size() >0)
            upsert results Warehouse_SKU__c; //Upsert the products in your org based on the external ID SKU
        }
        
    }
        //Get the product list from the external link
        public static List<Product2> getProductList(HttpResponse response)
        {
            
            List<Object> externalProducts = (List<Object>) JSON.deserializeUntyped(response.getBody()); //desrialize the json response
            List<Product2> newProducts = new List<Product2>();
            
            for(Object p : externalProducts)
            {
                Map<String, Object> productMap = (Map<String, Object>) p;
                Product2 pr = new Product2();
            	//Map the fields in the response to the appropriate fields in the Equipment object
                pr.Replacement_Part__c = (Boolean)productMap.get('replacement');
                pr.Cost__c = (Integer)productMap.get('cost');
                pr.Current_Inventory__c = (Integer)productMap.get('quantity');
                pr.Lifespan_Months__c = (Integer)productMap.get('lifespan') ;
                pr.Maintenance_Cycle__c = (Integer)productMap.get('maintenanceperiod');
                pr.Warehouse_SKU__c = (String)productMap.get('sku');
                pr.ProductCode = (String)productMap.get('_id');
                pr.Name = (String)productMap.get('name');
                
            
                newProducts.add(pr);
            }
            
            return newProducts;
            
        }
        
        // Send Http GET request and receive Http response
    
    public static HttpResponse getResponse() {
        
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(WAREHOUSE_URL);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        
        return response;
        
    }
    }

I was able to complete the challenge successfully.  This was the code I wrote.  Pelase make sure you Run this atleast once in the Execute anonymous window before checking the challenge. : WarehouseCalloutService.runWarehouseEquipmentSync(); 
Brendon Wilkinson 73Brendon Wilkinson 73
Hi Rebecca,

Probably too late but for anyone else who sees this. The issue was that you were performing an upsert without specifying the correct external id field: 
upsert products Warehouse_SKU__c;
 
Best,
Brendon
Rutu PatelRutu Patel

Hi,

I have done challenge 2 

First create In Quick Box -> Setting -> Remote Site  -> New
Remote Site Name : th_superbadge
Remote Site URL : https://th-superbadge-apex.herokuapp.com

Then open Developer Console and write code In WarehouseCalloutService .apxc:

public with sharing class WarehouseCalloutService {

    private static final String WAREHOUSE_URL = 'https://th-superbadge-apex.herokuapp.com/equipment';
    
    @future(callout=true)
    public static void runWarehouseEquipmentSync() {
        //ToDo: complete this method to make the callout (using @future) to the
        //      REST endpoint and update equipment on hand.
    
        
        HttpResponse response = getResponse(); 
        if(response.getStatusCode() == 200)
        {
            List<Product2> results = getProductList(response); //get list of products from Http callout response
            
            if(results.size() >0)
            upsert results Warehouse_SKU__c; //Upsert the products in your org based on the external ID SKU
        }
        
    }
        //Get the product list from the external link
        public static List<Product2> getProductList(HttpResponse response)
        {
            
            List<Object> externalProducts = (List<Object>) JSON.deserializeUntyped(response.getBody()); //desrialize the json response
            List<Product2> newProducts = new List<Product2>();
            
            for(Object p : externalProducts)
            {
                Map<String, Object> productMap = (Map<String, Object>) p;
                Product2 pr = new Product2();
                //Map the fields in the response to the appropriate fields in the Equipment object
                pr.Replacement_Part__c = (Boolean)productMap.get('replacement');
                pr.Cost__c = (Integer)productMap.get('cost');
                pr.Current_Inventory__c = (Integer)productMap.get('quantity');
                pr.Lifespan_Months__c = (Integer)productMap.get('lifespan') ;
                pr.Maintenance_Cycle__c = (Integer)productMap.get('maintenanceperiod');
                pr.Warehouse_SKU__c = (String)productMap.get('sku');
                pr.ProductCode = (String)productMap.get('_id');
                pr.Name = (String)productMap.get('name');
                
            
                newProducts.add(pr);
            }
            
            return newProducts;
            
        }
        
        // Send Http GET request and receive Http response
    
    public static HttpResponse getResponse() {
        
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(WAREHOUSE_URL);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        
        return response;
        
    }
    }

And open anonymous window and write code:

WarehouseCalloutService.runWarehouseEquipmentSync();

Then execute once before check challenge.I have do it and it work currectly. 

Thanks.

Aliaksandr KotAliaksandr Kot
hi!
add site in remote
Setup > Quick search > Remote Site Settings > New Remote Site :
Remote Site Name: th_superbadge
Remote Site URLhttps: //th-superbadge-apex.herokuapp.com
Urvashi BabbarUrvashi Babbar
After being stuck on this challenge for so long I finally got my solution:

1. Add remote site:
Setup -> Quick Search -> Remote SIte Settings -> New
Remote Site Name: Equipment
Remote Site  URL: https: //th-superbadge-apex.herokuapp.com

2. WarehouseCalloutService.apxc
public with sharing class WarehouseCalloutService implements Queueable, Database.AllowsCallouts {

    private static final String WAREHOUSE_URL = 'https://th-superbadge-apex.herokuapp.com/equipment';
    
    @future(callout=true)
    public static void runWarehouseEquipmentSync(){   	

        Http http = new Http();
        HttpRequest request = new HttpRequest();
        
        request.setMethod('GET');
        request.setEndpoint(WAREHOUSE_URL);
        HttpResponse response = http.send(request);
        
        List<Product2> lstProduct = new List<Product2>();
        
        if(response.getStatusCode() == 200){
            List<Object> jsonResponse = (List<Object>) Json.deserializeUntyped(response.getBody());
            
            for(Object obj : jsonResponse){
                Map<String, Object> mapJson = (Map<String, Object>) obj;
                Product2 objProduct = new Product2();
                objProduct.Replacement_Part__c = true;
                objProduct.Name = (String) mapJson.get('name'); 
                objProduct.Maintenance_Cycle__c = (Integer) mapJson.get('maintenanceperiod');
                objProduct.Lifespan_Months__c = (Integer) mapJson.get('lifespan');
                objProduct.Cost__c = (Decimal) mapJson.get('cost');
                objProduct.Warehouse_SKU__c = (String) mapJson.get('sku');
                objProduct.Current_Inventory__c = (Integer) mapJson.get('quantity');
                lstProduct.add(objProduct);
            }
            if(lstProduct.size() > 0){
                upsert lstProduct;
            }
        }
    }
    
    public static void execute(QueueableContext context){
        runWarehouseEquipmentSync();
    }
    
    
}

3. In Developer Console, Debug -> Open Execute Anonymous Window
Type the following in it:
WarehouseCalloutService.runWarehouseEquipmentSync();
Urvashi BabbarUrvashi Babbar
4. Run in Query Editor in Developer Console
SELECT Id, JobType, MethodName, Status FROM AsyncApexJob WHERE CompletedDate = TODAY
Sara AvciSara Avci
Not Completed yet...
User-added image

I controlled Field Accessibility for all fields. All field editable for my profile.User-added image

I added endpoint Url in Remote Site Setting.

Just when i upsert records in apex class i used this;
 >    upsert productList Warehouse_SKU__c;
And i use this part in mapping;
>    newProduct.Replacement_Part__c = true;

My future job works exactly. Job completed without any error.
User-added image
And i can see product record has comes. But i never pass the challenge.

Please help me, any ideas?

 
CHEN FEIYANCHEN FEIYAN
As message:enqueue the job at least once to confirm that it's working as expected.

In Developer Console, Debug -> Open Execute Anonymous Window
System.enqueueJob(New WarehouseCalloutService());
Sara AvciSara Avci
Thank you @Chen Feiyan, i passed the challenge :))
Ultan KearnsUltan Kearns
Please ensure to implement Database.AllowsCallouts in your class, spent ages working on this and kept getting erroneous error messgaes from trailhead
Bruno Rodriguez MolinaBruno Rodriguez Molina
This is how I got my challenge step 2 solved.

WarehouseCalloutSevice class
public with sharing class WarehouseCalloutService implements Queueable, Database.AllowsCallouts {

    private static final String WAREHOUSE_URL = 'https://th-superbadge-apex.herokuapp.com/equipment';
    
    public void execute(QueueableContext context) {
        Http http = new Http();
    HttpRequest req = new HttpRequest();
    req.setEndpoint(WAREHOUSE_URL);
    req.setMethod('GET');
    HttpResponse res = http.send(req);
        
        List<Product2> lstProducts = new List<Product2>();
        
        if (res.getStatusCode() == 200) {
            
            List<Object> results = (List<Object>) JSON.deserializeUntyped(res.getBody());
            
            
    
           for(Object obj: results){
               
               Map<String, Object> mapObj = (Map<String, Object>) obj;
               
            	Product2 product = new Product2();
               
               product.ExternalId = (String) mapObj.get('_id'); // _id
               product.Replacement_Part__c =(boolean) mapObj.get('replacement'); //replacement
               product.Current_Inventory__c = (Decimal) mapObj.get('quantity'); //quantity
               product.Name = (String) mapObj.get('name'); //name
               product.Maintenance_Cycle__c = (decimal) mapObj.get('maintenanceperiod'); //maintenanceperiod
               product.Lifespan_Months__c = (decimal) mapObj.get('lifespan'); //lifespan
               product.Cost__c = (decimal) mapObj.get('cost'); //cost
               product.StockKeepingUnit = (String) mapObj.get('sku'); //sku
               
               lstProducts.add(product);
            }
            
            Database.upsert (lstProducts, false);
        }
    
    }
    
}
To run the queueable job, in annonymous window:
System.enqueueJob(new WarehouseCalloutService());
You need to add the remote site in your org.

After you run the job, verify accessing Apex Jobs from Setup menu.
Apex Job result
Shivam LaidwarShivam Laidwar

Open Execute Anonymous window and type 
System.enqueueJob(New WarehouseCalloutService());

excecute it...