0

In our sandbox, we have created Student, Role, and StudentRoleRelationship objects. Currently, we have a Drupal project that has its own Student and Role objects, therefore in Student and Role we have a drupalId field for referencing the object in Drupal.

I am attempting to create an API (apex class) that creates and deletes a StudentRoleRelationship. When I do this, I will request the student's and role's DrupalId, look up the respected objects based off that DrupalId, and then create a StudentRoleRelationship based on those values.

Here is what my code looks like so far:

@RestResource(urlMapping='/Student/Roles/')
global class api_YTPStudentRolesController {

    @HttpPost
    global static void addRole() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;

        try {

            // Get Request Params
            Decimal roleDrupalId = Decimal.valueOf(req.params.get('role_id'));
            Decimal studentDrupalId = Decimal.valueOf(req.params.get('student_id'));

            // Query for role and student
            Role__c role = [SELECT Id FROM Role__c WHERE drupalId__c = :roleDrupalId];
            Student__c student = [SELECT Id FROM Student__c WHERE drupalId__c = :studentDrupalId];

            // Create the new StudentRoleRelationship
            StudentRoleRelationship__c studentRoleRelationship = new StudentRoleRelationship__c();
            studentRoleRelationship.Role__c = role.Id;
            studentRoleRelationship.Student__c = student.Id;
            insert studentRoleRelationship;

            res.responseBody = Blob.valueOf(JSON.serialize(studentRoleRelationship));
            res.statusCode = 200;

        } catch (Exception e) {
            res.responseBody = Blob.valueOf(e.getMessage());
            res.statusCode = 500;
        }
    }

    @HttpDelete
    global static String deleteRole() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;

        try {
            // Get Request Params
            Decimal roleDrupalId = Decimal.valueOf(req.params.get('role_id'));
            Decimal studentDrupalId = Decimal.valueOf(req.params.get('student_id'));

            // Query for role and student
            Role__c role = [SELECT Id FROM Role__c WHERE drupalId__c = :roleDrupalId];
            Student__c student = [SELECT Id FROM Student__c WHERE drupalId__c = :studentDrupalId];

            // Delete StudentRoleRelationship
            StudentRoleRelationship__c studentRoleRelationship = [SELECT Id FROM StudentRoleRelationship__c WHERE Role__c = :role.Id AND Student__c = :student.Id];
            delete studentRoleRelationship;

            // Add Response
            res.responseBody = Blob.valueOf(JSON.serialize(studentRoleRelationship));
            res.statusCode = 200;

        } catch (Exception e) {
            // Error
            res.responseBody = Blob.valueOf(e.getMessage());
            res.statusCode = 500;
        }
    }
}    

As a newbie to Salesforce, I am curious of a couple of things:

  • Is this the best practice on how to do what I am attempting to do (i.e. am I handling responses correctly, errors handled properly, etc.)
  • How can I handle different error types? I would prefer to send back more than just a 500 error. For instance if there is no role_id in the request params or there is no 'studentfound with the givenstudent_id, I should be sending a4xx error` instead. How would you handle this?
1
  • I would rename your addRole method to something more like createStudentRoleRelationship
    – gNerb
    Dec 13, 2018 at 20:13

2 Answers 2

2

You already have what you need to return custom errors.

Basically, you just need to add a few if statements:

global static void addRole() {
    RestRequest req = RestContext.request;
    RestResponse res = RestContext.response;

    try {

        // Get Request Params
        Decimal roleDrupalId = Decimal.valueOf(req.params.get('role_id'));
        Decimal studentDrupalId = Decimal.valueOf(req.params.get('student_id'));

        if (roleDrupalId == null || roleDrupalId == '') {
            res.responseBody = Blob.valueOf('No Drupal Role Id provided');
            res.statusCode = 4xx; // What ever error code you want to send
            return;
        }

        if (studentDrupalId == null || studentDrupalId == '') {
            res.responseBody = Blob.valueOf('No student Drupal Id provided');
            res.statusCode = 4xx; // What ever error code you want to send
            return;
        }

        // Query for role and student
        Role__c role = [SELECT Id FROM Role__c WHERE drupalId__c = :roleDrupalId];
        Student__c student = [SELECT Id FROM Student__c WHERE drupalId__c = :studentDrupalId];

        if (role == null) {
            res.responseBody = Blob.valueOf('No Role Found with given id ' + studentDrupalId);
            res.statusCode = 4xx; // What ever error code you want to send
            return;
        }

        if (student == null) {
            res.responseBody = Blob.valueOf('No Student Found with given id ' + studentDrupalId);
            res.statusCode = 4xx; // What ever error code you want to send
            return;
        }

        // ... The rest of your code (truncated)
    } catch (Exception e) {
        res.responseBody = Blob.valueOf(e.getMessage());
        res.statusCode = 500;
    }
}

Also,

Role__c role = [SELECT Id FROM Role__c WHERE drupalId__c = :roleDrupalId];
Student__c student = [SELECT Id FROM Student__c WHERE drupalId__c = :studentDrupalId];

These two lines are likely to cause you errors. Instead try:

List<Role__c> roles = [SELECT Id FROM Role__c WHERE drupalId__c = :roleDrupalId];

if (roles.isEmpty()) {
    res.responseBody = Blob.valueOf('No Role Found with given Id: ' + roleDrupalId);
    res.statusCode = 404;
}

Lastly, I generally advice that if you have full control on both sides, you want to design a bulkified api that can handle more than 1 request at a time and send your requests in bulk. So instead of taking in 1 id, it should take in a map of roles to student ids and an operation type or something.

2
  • Thanks. Luckily drupalId__c in both objects are unique so I shouldn't have an issue not throwing them in a list.
    – BlondeSwan
    Dec 13, 2018 at 22:51
  • Its not about whether or not they are unique. Its about whether or not the exist. If you pass an ID that doesn't exist in SF you'll get an exception. Something like list has no rows for assignment to sobject
    – gNerb
    Dec 16, 2018 at 21:47
1

You can try to check the size of the results and throw a custom exception and handle that custom exception to throw 4xx error

 @HttpDelete
 global static String deleteRole() {
     RestRequest req = RestContext.request;
     RestResponse res = RestContext.response;

     try {

         string roleDrupalIdString = req.params.get('role_id');
         string studentDrupalIdString = req.params.get('student_id');

         if (string.isblank(roleDrupalIdString)) {
             throw new RecordNotFoundException('Drupal RoleId is blank or not provided');
         }

         if (string.isblank(studentDrupalIdString)) {
             throw new RecordNotFoundException('Drupal StudentId is blank or not provided');

         }
         // Get Request Params
         Decimal roleDrupalId = Decimal.valueOf(roleDrupalIdString);
         Decimal studentDrupalId = Decimal.valueOf(studentDrupalIdString);



         // Query for role and student

         List<Role__c> roleList = [SELECT Id FROM Role__c WHERE drupalId__c =: roleDrupalId];
         List<Student__c> studentList = [SELECT Id FROM Student__c WHERE drupalId__c =: studentDrupalId];

         if (roleList.size() == 0) {
             throw new RecordNotFoundException('Role not found');
         }

         if (StudentList.size() == 0) {
             throw new RecordNotFoundException('Studentnot found');
         }


         // Delete StudentRoleRelationship
         StudentRoleRelationship__c studentRoleRelationship = [SELECT Id FROM StudentRoleRelationship__c WHERE Role__c =: role.Id AND Student__c =: student.Id];
         delete studentRoleRelationship;

         // Add Response
         res.responseBody = Blob.valueOf(JSON.serialize(studentRoleRelationship));
         res.statusCode = 200;

     } catch (RecordNotFoundException e) {
         // Error
         res.responseBody = Blob.valueOf(e.getMessage());
         res.statusCode = 400;
     } catch (Exception e) {
         // Error
         res.responseBody = Blob.valueOf(e.getMessage());
         res.statusCode = 500;
     }
 }


 public class RecordNotFoundException extends Exception {}
1
  • There's no need to throw exceptions at all. It's just extra code. You should look at my answer.
    – gNerb
    Dec 13, 2018 at 20:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .