Tenho um serviço Salesforce Apex REST (LeadService) que processa leads recebidos no formato JSON. Além disso, estou procurando maneiras de aprimorar o tratamento de erros e tornar o código mais fácil de manter. estrutura geral do código
@RestResource(urlMapping='/api/lead')
global class Service {
@HttpPost
global static string createdata(){
RestResponse res = Restcontext.response;
String requestBody = RestContext.request.requestBody.toString();
try {
// Deserialize JSON data into a list of LeadDataDeserializer objects
List<LeadDataDeserializer> externalLeads = (List<LeadDataDeserializer>)
JSON.deserializeStrict(requestBody, List<LeadDataDeserializer>.class);
// Transform LeadDataDeserializer objects into Lead data
List<Lead> students = new List<Lead>();
for(LeadDataDeserializer info : externalLeads) {
Lead t_leads = setLeadFields(info);
t_leads.company='Test Company';
students.add(t_leads);
}
if(students.isEmpty()){
res.statusCode=400;
return 'Empty list';
}
else{
List<Response> responseretn=new List<Response>();
Database.UpsertResult[] srList = Database.upsert(students, Lead.External_Id__c, false);
// Process upsert results if needed
Integer i=0;
for(Database.UpsertResult upResult:srList){
if(upResult.isSuccess()){
responseretn.add(new Response(upResult.getId(),true,students[i].MobilePhone));
}
else{
System.debug(upResult.getErrors());
responseretn.add(new Response(upResult.getId(),false,students[i].MobilePhone));
}
i+=1;
}
String jsonReqBody=JSON.serialize(responseretn);
res.statusCode = 201;
return jsonReqBody;
}
} catch(Exception e) {
// Handle exceptions
res.Statuscode = 500;
return 'Internal Server Error';
}
}
//Response wrapper to return
public class Response{
public string leadId{get;set;}
public boolean isSuccess{get;set;}
public string mobilePhone{get;set;}
public Response(String leadId,Boolean isSuccess,String mobilePhone){
this.leadId=leadId;
this.isSuccess=isSuccess;
this.mobilePhone=mobilePhone;
}
}
public static Lead setLeadFields(LeadDataDeserializer info){
Lead extLead=new Lead();
extLead.LastName=info.Name;
extLead.CountryCode__c=info.countryCode;
extLead.MobilePhone=info.phoneNumber;
extLead.mx_WhatsApp_Number__c=extLead.MobilePhone;
extLead.mx_IP_Address__c=info.ipAddress;
extLead.External_Id__c=extLead.MobilePhone;
if(info.leadStage!=null){
extLead.Status=info.leadStage;
}
if(info.campaignName!=null){
extLead.mx_Campaign_Name__c=info.campaignName;
}
if(info.campaignSource!=null){
extLead.SourceCampaign__c=info.campaignSource;
}
//20 more if conditions with null check like above (serializeddata.field!=null)
return extLead;
}}
Estou usando várias instruções if para tratar apenas para incluir campos que possuem valor e não para substituir o valor do registro anterior se o valor proveniente da integração estiver vazio
Como isso pode ser tratado de forma eficiente?
isso é um pouco tarde. Não mudará muito no desempenho, mas logicamente seria melhor verificar a lista desserializada mais cedo e retornar então, não depois de extrair dela.
Eu não acho que você precise retornar uma string. Você poderia facilmente retornar
List<Response>
e SF irá serializá-lo para você.Como você deseja lidar com os problemas? Salve o que puder? Você pode inserir o sObject auxiliar para qualquer problema e executar um relatório sobre isso. Ou use eventos de plataforma com "publicar imediatamente" para que algum sistema de monitoramento ou até mesmo um gatilho especial possa processá-los.
Talvez eu incluísse um contador de erros, então, se a taxa de falha for 100%, retorne algo diferente de 201
E se você fizer isso se ele enviar> 10 mil linhas (ou os efeitos colaterais resultarão em> 10 mil dmls) ... Se for uma preocupação legítima, eu provavelmente a reescreveria para iniciar um trabalho em lote (os lotes podem assumir o escopo e iterar isso, eles nem sempre precisam começar com uma consulta). Pontos de bônus que você poderia
implements Database.RaisesPlatformEvents
e trabalho quase concluído, SF fará muitos tratamentos de erros para você sem a necessidade de "tarefa savepoint-try-catch-rollback-insert manual ou qualquer outra coisa"Quanto ao código de mapeamento real... não é ótimo, mas também não é muito ruim. Você pode ser um pouco ingênuo com verificações de nulos - pois algumas variáveis
String.isNotBlank
serão melhores, dependendo de qual JSON é produzido pela fonte.Eu manteria o mapeamento do campo origem-destino em outro lugar (configuração personalizada? Metadados personalizados?) Para que você não precise recompilá-lo, implantar etc. toda vez que adicionar um novo campo.
Se você realmente quiser, pode ler sobre JSON.serialize com o parâmetro para pular nulos (então, por exemplo, você pode desserializar a entrada, serializá-la novamente com os nulos ignorados, desserializar na segunda vez, limpar). Ou defina cegamente todos os campos com base na entrada, então sObject.getPopulatedFieldsAsMap e itere através deles verificando o que é nulo ... mas parece um pouco inteligente demais para o que é necessário. Às vezes, o simples é mais eficaz.
Precisa mesmo ser uma API REST personalizada? Se você tiver controle sobre o formato de origem, poderá considerar
composite
a API padrão, com menos trabalho. Dê uma olhada em meu https://salesforce.stackexchange.com/a/274696/799 (incluindo o cabeçalho "allOrNone"). O upsert normal não lida bem com vários registros, é verdade - mas isso é próximo o suficiente!E bem... Código nem sempre é a resposta. Se você tem certeza absoluta de que a API padrão não funcionará - você sabia que pode chamar fluxos pela API REST ?