プログラマ38の日記

主にプログラムメモです。

Salesforce: メタデータAPIの renameMetadata を使って 統一感のあるfullNameにする

急いで開発していて、設定の物理名(API名やdeveloper名など)はあまり考えている時間がなかったり、ネーミングルールを後から作ったため、それまでの設定がルールとあっていなかったりする場合があります。

 

メンテナンス性を考えてそれまでの設定の物理名を変更するのにメタデータAPIの renameMetadata が便利です。(ラベルの変更には、deploy もしくは、 updateMetadata を使います)

 

jaxws-ri-2.3.2 を使ったサンプルが次になります。
※Lightning Platform Web Services Connector (WSC) でも良かったのですがあまり面白みがなかったのでこちらにしました。

jaxws-ri の使い方については、以前記事を書きました。

 

crmprogrammer38.hatenablog.com

 

サンプルコード  対象のメタデータタイプは一部のものになります。

以下のソースは次の環境で動作します。 

java8,  jaxws-ri-2.3.2, wsimportはjava8のものを使いました。

オブジェクトの物理名はObject__cとして表記しています。

メタデータタイプは例として次をピックアップしました。

  • レコードタイプ
  • カスタム項目
  • リストビュー
  • 入力規則
  • ワークフロールール
  • メールアラート
  • 項目自動更新
  • Lightningレコードページ、ユーティリティバー、ホームページ
  • アクション
  • グローバルアクション
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.MessageContext;

import com.sforce.soap._2006._04.metadata.CustomField;
import com.sforce.soap._2006._04.metadata.FlexiPage;
import com.sforce.soap._2006._04.metadata.ListView;
import com.sforce.soap._2006._04.metadata.MetadataPortType;
import com.sforce.soap._2006._04.metadata.MetadataService;
import com.sforce.soap._2006._04.metadata.QuickAction;
import com.sforce.soap._2006._04.metadata.SaveResult;
import com.sforce.soap._2006._04.metadata.ValidationRule;
import com.sforce.soap._2006._04.metadata.WorkflowAlert;
import com.sforce.soap._2006._04.metadata.WorkflowFieldUpdate;
import com.sforce.soap._2006._04.metadata.WorkflowRule;
import com.sforce.soap.partner.LoginResult;
import com.sforce.soap.partner.SforceService;
import com.sforce.soap.partner.Soap;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.Headers;
import com.sun.xml.ws.developer.WSBindingProvider;

public class Main {

  public static Soap soapBinding = new SforceService().getSoap();
  public static MetadataPortType metadataBinding = new MetadataService().getMetadata();

  public static String endpoint = "https://login.salesforce.com/services/Soap/u/48.0";
  public static String username = "sample@user.name";
  public static String password = "samplepassword";
  
  public static void main(String[] args) throws Exception {
    
    login();
    
    //レコードタイプ
    rename(RecordType.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //カスタム項目
    rename(CustomField.class.getSimpleName(), "Object__c.fullName1__c" , "Object__c.fullName2__c");
    //リストビュー
    rename(ListView.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //入力規則
    rename(ValidationRule.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //ワークフロールール
    rename(WorkflowRule.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //メールアラート
    rename(WorkflowAlert.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //項目自動更新
    rename(WorkflowFieldUpdate.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //Lightningレコードページ、ユーティリティバー、ホームページ
    rename(FlexiPage.class.getSimpleName(), "fullName1" , "fullName2");
    //アクション
    rename(QuickAction.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //グローバルアクション
    rename(QuickAction.class.getSimpleName(), "fullName1" , "fullName2");

  }
  
  public static void rename(String metadataType, String oldname, String newname) {
    boolean resultSuccess = false;
    String errorMessage = "";
    
    try{
      SaveResult sr = metadataBinding.renameMetadata(metadataType, oldname, newname);
      
      if(sr.isSuccess()){
        resultSuccess = true;
      } else {
        errorMessage = sr.getErrors().get(0).getMessage();
      }
    
    }catch(Exception e){
      errorMessage = e.getMessage();
    }
    
    
    LinkedHashMap<String, String> info= new LinkedHashMap<>();
    info.put("metadataType", metadataType);
    info.put("oldname", oldname);
    info.put("newname", newname);
    
    if( resultSuccess ){
      System.out.println("success : " + info );
    } else {
      System.out.println("error   : " + info + " error : " + errorMessage );
    }
  }
  
  
  public static void login() throws Exception{

    

    WSBindingProvider provider = (WSBindingProvider) soapBinding;

    Map<String,Object> reqContext = provider.getRequestContext();
    reqContext.put(WSBindingProvider.ENDPOINT_ADDRESS_PROPERTY,endpoint        );
    reqContext.put("javax.xml.ws.client.connectionTimeout", "180");
    reqContext.put("javax.xml.ws.client.receiveTimeout", "180");
    
    

    // Enable GZip compression
    Map<String, List<String>> httpHeaders = new HashMap<String, List<String>>();
    httpHeaders.put("Content-Encoding", Collections.singletonList("gzip"));
    httpHeaders.put("Accept-Encoding", Collections.singletonList("gzip"));
    reqContext.put(MessageContext.HTTP_REQUEST_HEADERS, httpHeaders);

    LoginResult loginResult = soapBinding.login(username,password);

    String serverUrl = loginResult.getServerUrl();
    String metadataUrl = loginResult.getMetadataServerUrl();
    String sessionId = loginResult.getSessionId();

    reqContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, serverUrl);

    com.sforce.soap.partner.SessionHeader sh = new com.sforce.soap.partner.SessionHeader();
    sh.setSessionId(sessionId);

    JAXBContext jaxbContext = null;
    try {
      jaxbContext = JAXBContext.newInstance(com.sforce.soap.partner.ObjectFactory.class);
    } catch (JAXBException e) {
    }

    ArrayList<Header> sessionheaderlst = new ArrayList<Header>();
    sessionheaderlst.add(Headers.create((JAXBContext) jaxbContext, sh));
    provider.setOutboundHeaders(sessionheaderlst);
    
    buildMetadataBinding(metadataUrl, sessionId);
    


  }
  
  public static void buildMetadataBinding(String metadataUrl, String sessionId) throws Exception{
    WSBindingProvider provider = (WSBindingProvider) metadataBinding;
    Map<String,Object> reqContext = provider.getRequestContext();
    reqContext.put(WSBindingProvider.ENDPOINT_ADDRESS_PROPERTY,metadataUrl        );
    reqContext.put("javax.xml.ws.client.connectionTimeout", "180");
    reqContext.put("javax.xml.ws.client.receiveTimeout", "180");
    // Enable GZip compression
    Map<String, List<String>> httpHeaders = new HashMap<String, List<String>>();
    httpHeaders.put("Content-Encoding", Collections.singletonList("gzip"));
    httpHeaders.put("Accept-Encoding", Collections.singletonList("gzip"));
    reqContext.put(MessageContext.HTTP_REQUEST_HEADERS, httpHeaders);
    
    com.sforce.soap._2006._04.metadata.SessionHeader sh = new com.sforce.soap._2006._04.metadata.SessionHeader();
    sh.setSessionId(sessionId);

    JAXBContext jaxbContext = null;
    try {
      jaxbContext = JAXBContext.newInstance(com.sforce.soap._2006._04.metadata.ObjectFactory.class);
    } catch (JAXBException e) {
    }

    ArrayList<Header> sessionheaderlst = new ArrayList<Header>();
    sessionheaderlst.add(Headers.create((JAXBContext) jaxbContext, sh));
    provider.setOutboundHeaders(sessionheaderlst);
    
  }

}

最後に

よくやってしまいがちなのが、LightningレコードページでFlexiPage1などシステムで自動で付与されたものをそのまま使ってしまったりなどです。

また、ユーティリティバーについては、身に覚えがないのにUtilityBar1などとなってしまうので後から修正することも多いのかなと思います。