DataWorksは様々なAPI操作を提供しています。要件に基づいてAPI操作を呼び出してビジネスを管理できます。このトピックでは、DataWorks API操作を呼び出してタスクを迅速に開発、コミット、実行する方法について説明します。
背景情報
このトピックでは、以下のビジネスシナリオで呼び出すことができるDataWorks API操作について説明します。このトピックで説明されている手順を実行する前に、ビジネスシナリオに関連するコア機能と概念を理解することをお勧めします。
ワークスペース、ワークフロー、ノードフォルダ、ノードのクエリと管理、およびノードのコミットとデプロイ。CreateBusiness や ListBusiness などのDataStudio API操作が使用されます。
スモークテストの実行と実行ログの表示。RunSmokeTest などのオペレーションセンターAPI操作が使用されます。
以下のセクションでは、このプラクティスの主な手順とコアコードの例について説明します。
サンプルのソースコード全体を表示またはダウンロードするには、このトピックの参考資料:サンプルのソースコード全体をダウンロードするを参照してください。
バックエンドコードの開発
手順 1:ワークスペースをクエリするための ProjectService クラスを開発する
ProjectServiceクラスを開発する必要があります。このクラスは、ListProjects 操作を呼び出してワークスペースをクエリするために使用されるListProjects関数を定義します。操作を呼び出すと、フロントエンド開発に使用できるワークスペースが返されます。
package com.aliyun.dataworks.services;
import com.aliyun.dataworks_public20200518.models.ListProjectsRequest;
import com.aliyun.dataworks_public20200518.models.ListProjectsResponse;
import com.aliyun.dataworks_public20200518.models.ListProjectsResponseBody;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ProjectService {
@Autowired
private DataWorksOpenApiClient dataWorksOpenApiClient;
/**
* @param pageNumber
* @param pageSize
* @return
*/
public ListProjectsResponseBody.ListProjectsResponseBodyPageResult listProjects(Integer pageNumber, Integer pageSize) {
try {
ListProjectsRequest listProjectsRequest = new ListProjectsRequest();
listProjectsRequest.setPageNumber(pageNumber);
listProjectsRequest.setPageSize(pageSize);
ListProjectsResponse listProjectsResponse = dataWorksOpenApiClient.createClient().listProjects(listProjectsRequest);
System.out.println(listProjectsResponse.getBody().getRequestId());
return listProjectsResponse.getBody().getPageResult();
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return null;
}
}手順 2:ワークフローを処理するための BusinessService クラスを開発する
BusinessServiceクラスを開発する必要があります。このクラスは、以下の関数を定義します。
CreateBusiness 操作を呼び出してワークフローを作成するために使用できるCreateBusiness関数。
ListBusiness 操作を呼び出してワークフローをクエリするために使用できるListBusiness関数。
これらの関数は、フロントエンド開発中にサンプル ワークフローを作成し、ワークフローをクエリするために使用されます。
ディレクトリツリーを表示するために FolderService 関数を定義することもできます。ディレクトリツリーは、ワークフロー、ノードフォルダ、およびノードで構成されます。次の例では、ノードフォルダに関連する手順を省略し、コアプロセスのみを示しています。フォルダ操作に関連する FolderService 関数については、GitHub コードサンプルから完全なコードサンプルを入手できます。
package com.aliyun.dataworks.services;
import com.aliyun.dataworks.dto.CreateBusinessDTO;
import com.aliyun.dataworks.dto.DeleteBusinessDTO;
import com.aliyun.dataworks.dto.ListBusinessesDTO;
import com.aliyun.dataworks.dto.UpdateBusinessDTO;
import com.aliyun.dataworks_public20200518.models.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* @author dataworks デモ
*/
@Service
public class BusinessService {
@Autowired
private DataWorksOpenApiClient dataWorksOpenApiClient;
/**
* ビジネスを作成する
*
* @param createBusinessDTO
*/
public Long createBusiness(CreateBusinessDTO createBusinessDTO) {
try {
CreateBusinessRequest createBusinessRequest = new CreateBusinessRequest();
// ワークフローの名前。
createBusinessRequest.setBusinessName(createBusinessDTO.getBusinessName());
createBusinessRequest.setDescription(createBusinessDTO.getDescription());
createBusinessRequest.setOwner(createBusinessDTO.getOwner());
createBusinessRequest.setProjectId(createBusinessDTO.getProjectId());
// ワークフローが属するモジュール。有効な値: NORMAL (データ開発) および MANUAL_BIZ (手動トリガーワークフロー)。
createBusinessRequest.setUseType(createBusinessDTO.getUseType());
CreateBusinessResponse createBusinessResponse = dataWorksOpenApiClient.createClient().createBusiness(createBusinessRequest);
System.out.println("create business requestId:" + createBusinessResponse.getBody().getRequestId());
System.out.println("create business successful,the businessId:" + createBusinessResponse.getBody().getBusinessId());
return createBusinessResponse.getBody().getBusinessId();
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return null;
}
/**
* @param listBusinessesDTO
* @return
*/
public List<ListBusinessResponseBody.ListBusinessResponseBodyDataBusiness> listBusiness(ListBusinessesDTO listBusinessesDTO) {
try {
ListBusinessRequest listBusinessRequest = new ListBusinessRequest();
listBusinessRequest.setKeyword(listBusinessesDTO.getKeyword());
listBusinessRequest.setPageNumber(listBusinessesDTO.getPageNumber() < 1 ? 1 : listBusinessesDTO.getPageNumber());
listBusinessRequest.setPageSize(listBusinessesDTO.getPageSize() < 10 ? 10 : listBusinessesDTO.getPageSize());
listBusinessRequest.setProjectId(listBusinessesDTO.getProjectId());
ListBusinessResponse listBusinessResponse = dataWorksOpenApiClient.createClient().listBusiness(listBusinessRequest);
System.out.println("list business requestId:" + listBusinessResponse.getBody().getRequestId());
ListBusinessResponseBody.ListBusinessResponseBodyData data = listBusinessResponse.getBody().getData();
System.out.println("total count:" + data.getTotalCount());
if (!CollectionUtils.isEmpty(data.getBusiness())) {
for (ListBusinessResponseBody.ListBusinessResponseBodyDataBusiness businessItem : data.getBusiness()) {
System.out.println(businessItem.getBusinessId() + "," + businessItem.getBusinessName() + "," + businessItem.getUseType());
}
}
return data.getBusiness();
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return null;
}
/**
* ビジネスを更新する
* @param updateBusinessDTO
* @return
*/
public Boolean updateBusiness(UpdateBusinessDTO updateBusinessDTO) {
try {
UpdateBusinessRequest updateBusinessRequest = new UpdateBusinessRequest();
updateBusinessRequest.setBusinessId(updateBusinessDTO.getBusinessId());
updateBusinessRequest.setBusinessName(updateBusinessDTO.getBusinessName());
updateBusinessRequest.setDescription(updateBusinessDTO.getDescription());
updateBusinessRequest.setOwner(updateBusinessDTO.getOwner());
updateBusinessRequest.setProjectId(updateBusinessDTO.getProjectId());
UpdateBusinessResponse updateBusinessResponse = dataWorksOpenApiClient.createClient().updateBusiness(updateBusinessRequest);
System.out.println(updateBusinessResponse.getBody().getRequestId());
System.out.println(updateBusinessResponse.getBody().getSuccess());
return updateBusinessResponse.getBody().getSuccess();
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return false;
}
/**
* ビジネスを削除する
* @param deleteBusinessDTO
*/
public boolean deleteBusiness(DeleteBusinessDTO deleteBusinessDTO) {
try {
DeleteBusinessRequest deleteBusinessRequest = new DeleteBusinessRequest();
deleteBusinessRequest.setBusinessId(deleteBusinessDTO.getBusinessId());
deleteBusinessRequest.setProjectId(deleteBusinessDTO.getProjectId());
DeleteBusinessResponse deleteBusinessResponse = dataWorksOpenApiClient.createClient().deleteBusiness(deleteBusinessRequest);
System.out.println("delete business:" + deleteBusinessResponse.getBody().getRequestId());
System.out.println("delete business" + deleteBusinessResponse.getBody().getSuccess());
return deleteBusinessResponse.getBody().getSuccess();
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return false;
}
}手順 3:ファイルを処理するための FileService クラスを開発する
FileServiceクラスを開発する必要があります。このクラスは、ファイルを処理するために使用できる以下の関数を定義します。
ListFiles 操作を呼び出してファイルをクエリするために使用できるlistFiles関数。
CreateFile 操作を呼び出してファイルを作成するために使用できるcreateFile関数。
UpdateFile 操作を呼び出してファイルを更新するために使用できるupdateFile関数。
DeployFile 操作を呼び出してファイルをデプロイするために使用できるdeployFile関数。
RunSmokeTest 操作を呼び出してスモークテストを実行するために使用できるrunSmokeTest関数。
GetInstanceLog 操作を呼び出してインスタンスのログをクエリするために使用できるgetInstanceLog関数。
これらのメソッドは、UI から呼び出され、ファイルの作成、ファイルリストのプル、ファイルの保存、ファイルの送信と実行を行います。
package com.aliyun.dataworks.services;
import com.aliyun.dataworks.dto.*;
import com.aliyun.dataworks_public20200518.models.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* ide ファイルマネージャーサービス
*
* @author dataworks デモ
*/
@Service
public class FileService {
@Autowired
private DataWorksOpenApiClient dataWorksOpenApiClient;
public static final int CYCLE_NUM = 10;
/**
* ページ分割クエリ
*
* @param listFilesDTO
* @return
*/
public List<ListFilesResponseBody.ListFilesResponseBodyDataFiles> listFiles(ListFilesDTO listFilesDTO) {
try {
ListFilesRequest listFilesRequest = new ListFilesRequest();
// ファイルパス: 「Workflow/」 + ターゲットワークフロー名 + ディレクトリ名 + 最新のフォルダ名
// 例: Workflow/MyFirstWorkflow/MaxCompute/ods、「Data Development」は追加しないでください
listFilesRequest.setFileFolderPath(listFilesDTO.getFileFolderPath());
// ファイルのコードタイプ。複数のタイプがサポートされています。コンマ (,) で区切ります (例: 10,23)。
listFilesRequest.setFileTypes(listFilesDTO.getFileTypes());
// ファイル名のキーワード。あいまい一致がサポートされています。
listFilesRequest.setKeyword(listFilesDTO.getKeyword());
// スケジューリングノードの ID。
listFilesRequest.setNodeId(listFilesDTO.getNodeId());
// ファイルのオーナー。
listFilesRequest.setOwner(listFilesDTO.getOwner());
// リクエストされたデータページのページ番号。
listFilesRequest.setPageNumber(listFilesDTO.getPageNumber() <= 0 ? 1 : listFilesDTO.getPageNumber());
// 各ページに表示するエントリ数。
listFilesRequest.setPageSize(listFilesDTO.getPageSize() <= 10 ? 10 : listFilesDTO.getPageSize());
// DataWorks ワークスペースの ID。
listFilesRequest.setProjectId(listFilesDTO.getProjectId());
// ファイルが属するモジュール。
listFilesRequest.setUseType(listFilesDTO.getUseType());
ListFilesResponse listFilesResponse = dataWorksOpenApiClient.createClient()
.listFiles(listFilesRequest);
ListFilesResponseBody.ListFilesResponseBodyData fileData = listFilesResponse.getBody().getData();
if (fileData.getFiles() != null && !fileData.getFiles().isEmpty()) {
for (ListFilesResponseBody.ListFilesResponseBodyDataFiles file : fileData.getFiles()) {
// ワークフロー ID
System.out.println(file.getBusinessId());
// ファイル ID
System.out.println(file.getFileId());
// ファイル名
System.out.println(file.getFileName());
// ファイルタイプ、例: 10
System.out.println(file.getFileType());
// ノード ID
System.out.println(file.getNodeId());
// フォルダ ID
System.out.println(file.getFileFolderId());
}
}
return fileData.getFiles();
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return null;
}
/**
* ファイルを作成する
*
* @param createFileDTO
*/
public Long createFile(CreateFileDTO createFileDTO) {
try {
CreateFileRequest createFileRequest = new CreateFileRequest();
// タスクの詳細設定。
createFileRequest.setAdvancedSettings(createFileDTO.getAdvancedSettings());
// ファイルの自動解析を有効にするかどうかを指定します。このパラメーターは必須です。
createFileRequest.setAutoParsing(createFileDTO.getAutoParsing());
// エラー発生後の自動再実行の間隔 (ミリ秒)。最大値は 1,800,000 (30 分) です。
createFileRequest.setAutoRerunIntervalMillis(createFileDTO.getAutoRerunIntervalMillis());
// 自動再試行の回数。
createFileRequest.setAutoRerunTimes(createFileDTO.getAutoRerunTimes());
// ファイルがタスクとして公開された後のタスク実行に接続されるデータソース。このパラメーターは必須です。
createFileRequest.setConnectionName(createFileDTO.getConnectionName());
// ファイルのコード内容。このパラメーターは必須です。
createFileRequest.setContent(createFileDTO.getContent());
// 定期スケジュールの cron 式。このパラメーターは必須です。
createFileRequest.setCronExpress(createFileDTO.getCronExpress());
// スケジューリングサイクルのタイプ。このパラメーターは必須です。
createFileRequest.setCycleType(createFileDTO.getCycleType());
// 現在のサイクルの現在のノードのインスタンスが前のサイクルから依存するノードのリスト。
createFileRequest.setDependentNodeIdList(createFileDTO.getDependentNodeIdList());
// 前のサイクルへの依存タイプ。このパラメーターは必須です。
createFileRequest.setDependentType(createFileDTO.getDependentType());
// 自動スケジューリングを停止するタイムスタンプ (ミリ秒)。
createFileRequest.setEndEffectDate(createFileDTO.getEndEffectDate());
// ファイルの説明。
createFileRequest.setFileDescription(createFileDTO.getFileDescription());
// ファイルのパス。このパラメーターは必須です。
createFileRequest.setFileFolderPath(createFileDTO.getFileFolderPath());
// ファイルの名前。このパラメーターは必須です。
createFileRequest.setFileName(createFileDTO.getFileName());
// ファイルのコードタイプ。このパラメーターは必須です。
createFileRequest.setFileType(createFileDTO.getFileType());
// 現在のファイルが依存するアップストリームファイルの出力名。複数の出力をコンマ (,) で区切ります。このパラメーターは必須です。
createFileRequest.setInputList(createFileDTO.getInputList());
// ファイルオーナーの Alibaba Cloud ユーザー ID。このパラメーターが空の場合、デフォルトで呼び出し元のユーザー ID が使用されます。このパラメーターは必須です。
createFileRequest.setOwner(createFileDTO.getOwner());
// スケジューリングパラメーター。
createFileRequest.setParaValue(createFileDTO.getParaValue());
// プロジェクト ID。このパラメーターは必須です。
createFileRequest.setProjectId(createFileDTO.getProjectId());
// 再実行プロパティ。
createFileRequest.setRerunMode(createFileDTO.getRerunMode());
// タスクのリソースグループ。このパラメーターは必須です。
createFileRequest.setResourceGroupIdentifier(createFileDTO.getResourceGroupIdentifier());
// スケジューリングタイプ。
createFileRequest.setSchedulerType(createFileDTO.getSchedulerType());
// 自動スケジューリングを開始するタイムスタンプ (ミリ秒)。
createFileRequest.setStartEffectDate(createFileDTO.getStartEffectDate());
// 実行をスキップするかどうかを指定します。
createFileRequest.setStop(createFileDTO.getStop());
CreateFileResponse createFileResponse = dataWorksOpenApiClient.createClient()
.createFile(createFileRequest);
// requestId
System.out.println(createFileResponse.getBody().getRequestId());
// fileId
System.out.println(createFileResponse.getBody().getData());
return createFileResponse.getBody().getData();
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return null;
}
/**
* ファイルを更新する
*
* @param updateFileDTO
*/
public boolean updateFile(UpdateFileDTO updateFileDTO) {
try {
UpdateFileRequest updateFileRequest = new UpdateFileRequest();
// タスクの詳細設定。特定のフォーマットについては、関連ドキュメントをご参照ください。
updateFileRequest.setAdvancedSettings(updateFileDTO.getAdvancedSettings());
// ファイルの自動解析を有効にするかどうかを指定します。
updateFileRequest.setAutoParsing(updateFileDTO.getAutoParsing());
// エラー発生後の自動再実行の間隔 (ミリ秒)。最大値は 1,800,000 (30 分) です。
updateFileRequest.setAutoRerunIntervalMillis(updateFileDTO.getAutoRerunIntervalMillis());
// エラー発生後の自動再実行の回数。
updateFileRequest.setAutoRerunTimes(updateFileDTO.getAutoRerunTimes());
// タスク実行中にタスクが使用するデータソースの識別子。
updateFileRequest.setConnectionName(updateFileDTO.getConnectionName());
// ファイルのコード内容。
updateFileRequest.setContent(updateFileDTO.getContent());
// 定期スケジュールの cron 式。
updateFileRequest.setCronExpress(updateFileDTO.getCronExpress());
// スケジューリングサイクルのタイプ。有効な値: NOT_DAY (分、時) および DAY (日、週、月)。
updateFileRequest.setCycleType(updateFileDTO.getCycleType());
// DependentType が USER_DEFINE に設定されている場合、このパラメーターは現在のファイルが依存するノードの ID を指定します。複数のノード ID はコンマ (,) で区切ります。
updateFileRequest.setDependentNodeIdList(updateFileDTO.getDependentNodeIdList());
// 前のサイクルへの依存タイプ。
updateFileRequest.setDependentType(updateFileDTO.getDependentType());
// 自動スケジューリングを停止するタイムスタンプ (ミリ秒)。
updateFileRequest.setEndEffectDate(updateFileDTO.getEndEffectDate());
// ファイルの説明。
updateFileRequest.setFileDescription(updateFileDTO.getFileDescription());
// ファイルが配置されているパス。
updateFileRequest.setFileFolderPath(updateFileDTO.getFileFolderPath());
// ファイルの ID。
updateFileRequest.setFileId(updateFileDTO.getFileId());
// ファイルの名前。
updateFileRequest.setFileName(updateFileDTO.getFileName());
// 現在のファイルが依存するアップストリームファイルの出力名。複数の出力をコンマ (,) で区切ります。
updateFileRequest.setInputList(updateFileDTO.getInputList());
// ファイルの出力。
updateFileRequest.setOutputList(updateFileDTO.getOutputList());
// ファイルオーナーのユーザー ID。
updateFileRequest.setOwner(updateFileDTO.getOwner());
// スケジューリングパラメーター。
updateFileRequest.setParaValue(updateFileDTO.getParaValue());
// DataWorks ワークスペースの ID。
updateFileRequest.setProjectId(updateFileDTO.getProjectId());
// 再実行プロパティ。ALL_ALLOWED。
updateFileRequest.setRerunMode(updateFileDTO.getRerunMode());
// ファイルが公開された後のタスク実行に対応するリソースグループ。
updateFileRequest.setResourceGroupIdentifier(updateFileDTO.getResourceGroupIdentifier());
// スケジューリングタイプ。NORMAL。
updateFileRequest.setSchedulerType(updateFileDTO.getSchedulerType());
// 自動スケジューリングを開始するタイムスタンプ (ミリ秒)。
updateFileRequest.setStartEffectDate(updateFileDTO.getStartEffectDate());
// 公開後すぐに開始するかどうかを指定します。
updateFileRequest.setStartImmediately(updateFileDTO.getStartImmediately());
// 実行をスキップするかどうかを指定します。
updateFileRequest.setStop(updateFileDTO.getStop());
UpdateFileResponse updateFileResponse = dataWorksOpenApiClient.createClient()
.updateFile(updateFileRequest);
// requestId
System.out.println(updateFileResponse.getBody().getRequestId());
// 成功または失敗。
System.out.println(updateFileResponse.getBody().getSuccess());
return updateFileResponse.getBody().getSuccess();
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return false;
}
/**
* ファイルを削除する
*
* @param deleteFileDTO
* @return
* @throws InterruptedException
*/
public boolean deleteFile(DeleteFileDTO deleteFileDTO) throws InterruptedException {
try {
DeleteFileRequest deleteFileRequest = new DeleteFileRequest();
deleteFileRequest.setFileId(deleteFileDTO.getFileId());
deleteFileRequest.setProjectId(deleteFileDTO.getProjectId());
DeleteFileResponse deleteFileResponse = dataWorksOpenApiClient.createClient()
.deleteFile(deleteFileRequest);
System.out.println(deleteFileResponse.getBody().getRequestId());
System.out.println(deleteFileResponse.getBody().getDeploymentId());
GetDeploymentRequest getDeploymentRequest = new GetDeploymentRequest();
getDeploymentRequest.setProjectId(deleteFileDTO.getProjectId());
getDeploymentRequest.setDeploymentId(deleteFileResponse.getBody().getDeploymentId());
for (int i = 0; i < CYCLE_NUM; i++) {
GetDeploymentResponse getDeploymentResponse = dataWorksOpenApiClient.createClient()
.getDeployment(getDeploymentRequest);
// デプロイメントパッケージの現在のステータス。0: 準備完了、1: 成功、2: 失敗。
Integer deleteStatus = getDeploymentResponse.getBody().getData().getDeployment().getStatus();
// ここでループして削除ステータスを確認できます。
if (deleteStatus == 1) {
System.out.println("ファイルは正常に削除されました。");
break;
} else {
System.out.println("ファイルを削除しています...");
Thread.sleep(1000L);
}
}
GetProjectRequest getProjectRequest = new GetProjectRequest();
getProjectRequest.setProjectId(deleteFileDTO.getProjectId());
GetProjectResponse getProjectResponse = dataWorksOpenApiClient.createClient()
.getProject(getProjectRequest);
// 標準モードには DEV 環境と PROD 環境があります。基本モードには PROD 環境しかありません。
Boolean standardMode = getProjectResponse.getBody().getData().getEnvTypes().size() == 2;
if (standardMode) {
// 標準モードでは、削除を本番環境に公開する必要があります。
DeployFileRequest deployFileRequest = new DeployFileRequest();
deployFileRequest.setProjectId(deleteFileDTO.getProjectId());
deployFileRequest.setFileId(deleteFileDTO.getFileId());
DeployFileResponse deployFileResponse = dataWorksOpenApiClient.createClient()
.deployFile(deployFileRequest);
getDeploymentRequest.setDeploymentId(deployFileResponse.getBody().getData());
for (int i = 0; i < CYCLE_NUM; i++) {
GetDeploymentResponse getDeploymentResponse = dataWorksOpenApiClient.createClient()
.getDeployment(getDeploymentRequest);
// デプロイメントパッケージの現在のステータス。0: 準備完了、1: 成功、2: 失敗。
Integer deleteStatus = getDeploymentResponse.getBody().getData().getDeployment().getStatus();
// ここでループして削除ステータスを確認できます。
if (deleteStatus == 1) {
System.out.println("ファイルは正常に削除されました。");
break;
} else {
System.out.println("ファイルを削除しています...");
Thread.sleep(1000L);
}
}
}
return true;
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return false;
}
/**
* ファイルをクエリする
*
* @param getFileDTO
*/
public GetFileResponseBody.GetFileResponseBodyDataFile getFile(GetFileDTO getFileDTO) {
try {
GetFileRequest getFileRequest = new GetFileRequest();
getFileRequest.setFileId(getFileDTO.getFileId());
getFileRequest.setProjectId(getFileDTO.getProjectId());
getFileRequest.setNodeId(getFileDTO.getNodeId());
GetFileResponse getFileResponse = dataWorksOpenApiClient.createClient().getFile(getFileRequest);
System.out.println(getFileResponse.getBody().getRequestId());
GetFileResponseBody.GetFileResponseBodyDataFile file = getFileResponse.getBody().getData().getFile();
System.out.println(file.getFileName());
System.out.println(file.getFileType());
System.out.println(file.getNodeId());
System.out.println(file.getCreateUser());
return file;
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return null;
}
/**
* @param deployFileDTO
* @return
* @throws InterruptedException
*/
public Boolean deployFile(DeployFileDTO deployFileDTO) throws InterruptedException {
try {
GetProjectRequest getProjectRequest = new GetProjectRequest();
getProjectRequest.setProjectId(deployFileDTO.getProjectId());
GetProjectResponse getProjectResponse = dataWorksOpenApiClient.createClient()
.getProject(getProjectRequest);
// 標準モードには DEV 環境と PROD 環境があります。基本モードには PROD 環境しかありません。
Boolean standardMode = getProjectResponse.getBody().getData().getEnvTypes().size() == 2;
if (standardMode) {
SubmitFileRequest submitFileRequest = new SubmitFileRequest();
submitFileRequest.setFileId(deployFileDTO.getFileId());
submitFileRequest.setProjectId(deployFileDTO.getProjectId());
SubmitFileResponse submitFileResponse = dataWorksOpenApiClient.createClient()
.submitFile(submitFileRequest);
System.out.println("submit file requestId:" + submitFileResponse.getBody().getRequestId());
System.out.println("submit file deploymentId:" + submitFileResponse.getBody().getData());
for (int i = 0; i < CYCLE_NUM; i++) {
GetDeploymentRequest getDeploymentRequest = new GetDeploymentRequest();
getDeploymentRequest.setProjectId(deployFileDTO.getProjectId());
getDeploymentRequest.setDeploymentId(submitFileResponse.getBody().getData());
GetDeploymentResponse getDeploymentResponse = dataWorksOpenApiClient.createClient()
.getDeployment(getDeploymentRequest);
// デプロイメントパッケージの現在のステータス。0: 準備完了、1: 成功、2: 失敗。
Integer deleteStatus = getDeploymentResponse.getBody().getData().getDeployment().getStatus();
// ここでループして削除ステータスを確認できます。
if (deleteStatus == 1) {
System.out.println("ファイルは正常に送信されました。");
break;
} else {
System.out.println("ファイルを送信しています...");
Thread.sleep(1000L);
}
}
}
DeployFileRequest deployFileRequest = new DeployFileRequest();
deployFileRequest.setFileId(deployFileDTO.getFileId());
deployFileRequest.setProjectId(deployFileDTO.getProjectId());
DeployFileResponse deployFileResponse = dataWorksOpenApiClient.createClient()
.deployFile(deployFileRequest);
System.out.println("deploy file requestId:" + deployFileResponse.getBody().getRequestId());
System.out.println("deploy file deploymentId:" + deployFileResponse.getBody().getData());
for (int i = 0; i < CYCLE_NUM; i++) {
GetDeploymentRequest getDeploymentRequest = new GetDeploymentRequest();
getDeploymentRequest.setProjectId(deployFileDTO.getProjectId());
getDeploymentRequest.setDeploymentId(deployFileResponse.getBody().getData());
GetDeploymentResponse getDeploymentResponse = dataWorksOpenApiClient.createClient()
.getDeployment(getDeploymentRequest);
// デプロイメントパッケージの現在のステータス。0: 準備完了、1: 成功、2: 失敗。
Integer deleteStatus = getDeploymentResponse.getBody().getData().getDeployment().getStatus();
// ここでループして削除ステータスを確認できます。
if (deleteStatus == 1) {
System.out.println("ファイルは正常に公開されました。");
break;
} else {
System.out.println("ファイルを公開しています...");
Thread.sleep(1000L);
}
}
return true;
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return false;
}
public List<ListInstancesResponseBody.ListInstancesResponseBodyDataInstances> runSmokeTest(RunSmokeTestDTO runSmokeTestDTO) {
try {
RunSmokeTestRequest runSmokeTestRequest = new RunSmokeTestRequest();
runSmokeTestRequest.setBizdate(runSmokeTestDTO.getBizdate());
runSmokeTestRequest.setNodeId(runSmokeTestDTO.getNodeId());
runSmokeTestRequest.setNodeParams(runSmokeTestDTO.getNodeParams());
runSmokeTestRequest.setName(runSmokeTestDTO.getName());
runSmokeTestRequest.setProjectEnv(runSmokeTestDTO.getProjectEnv());
RunSmokeTestResponse runSmokeTestResponse = dataWorksOpenApiClient.createClient()
.runSmokeTest(runSmokeTestRequest);
System.out.println(runSmokeTestResponse.getBody().getRequestId());
// DAG ID
System.out.println(runSmokeTestResponse.getBody().getData());
ListInstancesRequest listInstancesRequest = new ListInstancesRequest();
listInstancesRequest.setDagId(runSmokeTestResponse.getBody().getData());
listInstancesRequest.setProjectId(runSmokeTestDTO.getProjectId());
listInstancesRequest.setProjectEnv(runSmokeTestDTO.getProjectEnv());
listInstancesRequest.setNodeId(runSmokeTestDTO.getNodeId());
listInstancesRequest.setPageNumber(1);
listInstancesRequest.setPageSize(10);
ListInstancesResponse listInstancesResponse = dataWorksOpenApiClient.createClient()
.listInstances(listInstancesRequest);
System.out.println(listInstancesResponse.getBody().getRequestId());
List<ListInstancesResponseBody.ListInstancesResponseBodyDataInstances> instances = listInstancesResponse.getBody().getData().getInstances();
if (CollectionUtils.isEmpty(instances)) {
return null;
}
return instances;
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return null;
}
public InstanceDetail getInstanceLog(Long instanceId, String projectEnv) {
try {
GetInstanceLogRequest getInstanceLogRequest = new GetInstanceLogRequest();
getInstanceLogRequest.setInstanceId(instanceId);
getInstanceLogRequest.setProjectEnv(projectEnv);
GetInstanceLogResponse getInstanceLogResponse = dataWorksOpenApiClient.createClient()
.getInstanceLog(getInstanceLogRequest);
System.out.println(getInstanceLogResponse.getBody().getRequestId());
GetInstanceRequest getInstanceRequest = new GetInstanceRequest();
getInstanceRequest.setInstanceId(instanceId);
getInstanceRequest.setProjectEnv(projectEnv);
GetInstanceResponse getInstanceResponse = dataWorksOpenApiClient.createClient()
.getInstance(getInstanceRequest);
System.out.println(getInstanceResponse.getBody().getRequestId());
System.out.println(getInstanceResponse.getBody().getData());
InstanceDetail instanceDetail = new InstanceDetail();
instanceDetail.setInstance(getInstanceResponse.getBody().getData());
instanceDetail.setInstanceLog(getInstanceLogResponse.getBody().getData());
return instanceDetail;
} catch (Exception e) {
e.printStackTrace();
// エラーメッセージ
System.out.println(e.getMessage());
}
return null;
}
}手順 4:IDEコントローラを開発する
フロントエンド開発中にルーティングのために呼び出すことができるAPI操作を提供するIDEコントローラを定義する必要があります。
package com.aliyun.dataworks.demo;
import com.aliyun.dataworks.dto.*;
import com.aliyun.dataworks.services.BusinessService;
import com.aliyun.dataworks.services.FileService;
import com.aliyun.dataworks.services.FolderService;
import com.aliyun.dataworks.services.ProjectService;
import com.aliyun.dataworks_public20200518.models.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author dataworks デモ
*/
@RestController
@RequestMapping("/ide")
public class IdeController {
@Autowired
private FileService fileService;
@Autowired
private FolderService folderService;
@Autowired
private BusinessService businessService;
@Autowired
private ProjectService projectService;
/**
* これらのファイルをリストする
*
* @param listFilesDTO
* @return ListFilesResponse.Data.File
*/
@CrossOrigin(origins = "http://localhost:8080")
@GetMapping("/listFiles")
public List<ListFilesResponseBody.ListFilesResponseBodyDataFiles> listFiles(ListFilesDTO listFilesDTO) {
return fileService.listFiles(listFilesDTO);
}
/**
* これらのフォルダをリストする
*
* @param listFoldersDTO
* @return ListFoldersResponse.Data.FoldersItem
*/
@CrossOrigin(origins = "http://localhost:8080")
@GetMapping("/listFolders")
public List<ListFoldersResponseBody.ListFoldersResponseBodyDataFolders> listFolders(ListFoldersDTO listFoldersDTO) {
return folderService.listFolders(listFoldersDTO);
}
/**
* フォルダを作成する
*
* @param createFolderDTO
* @return boolean
*/
@CrossOrigin(origins = "http://localhost:8080")
@PostMapping("/createFolder")
public boolean createFolder(@RequestBody CreateFolderDTO createFolderDTO) {
return folderService.createFolder(createFolderDTO);
}
/**
* フォルダを更新する
*
* @param updateFolderDTO
* @return boolean
*/
@CrossOrigin(origins = "http://localhost:8080")
@PostMapping("/updateFolder")
public boolean updateFolder(@RequestBody UpdateFolderDTO updateFolderDTO) {
return folderService.updateFolder(updateFolderDTO);
}
/**
* ファイルを取得する
*
* @param getFileDTO
* @return GetFileResponse.Data.File
*/
@CrossOrigin(origins = "http://localhost:8080")
@GetMapping("/getFile")
public GetFileResponseBody.GetFileResponseBodyDataFile getFile(GetFileDTO getFileDTO) {
return fileService.getFile(getFileDTO);
}
/**
* ファイルを作成する
*
* @param createFileDTO
* @return fileId
*/
@CrossOrigin(origins = "http://localhost:8080")
@PostMapping("/createFile")
public Long createFile(@RequestBody CreateFileDTO createFileDTO) {
return fileService.createFile(createFileDTO);
}
/**
* ファイルを更新する
*
* @param updateFileDTO
* @return boolean
*/
@CrossOrigin(origins = "http://localhost:8080")
@PostMapping("/updateFile")
public boolean updateFile(@RequestBody UpdateFileDTO updateFileDTO) {
return fileService.updateFile(updateFileDTO);
}
/**
* ファイルをデプロイする
*
* @param deployFileDTO
* @return boolean
*/
@CrossOrigin(origins = "http://localhost:8080")
@PostMapping("/deployFile")
public boolean deployFile(@RequestBody DeployFileDTO deployFileDTO) {
try {
return fileService.deployFile(deployFileDTO);
} catch (Exception e) {
System.out.println(e);
}
return false;
}
/**
* ファイルを削除する
*
* @param deleteFileDTO
* @return
*/
@CrossOrigin(origins = "http://localhost:8080")
@DeleteMapping("/deleteFile")
public boolean deleteFile(DeleteFileDTO deleteFileDTO) {
try {
return fileService.deleteFile(deleteFileDTO);
} catch (Exception e) {
System.out.println(e);
}
return false;
}
/**
* フォルダを削除する
*
* @param deleteFolderDTO
* @return
*/
@CrossOrigin(origins = "http://localhost:8080")
@DeleteMapping("/deleteFolder")
public boolean deleteFolder(DeleteFolderDTO deleteFolderDTO) {
return folderService.deleteFolder(deleteFolderDTO);
}
/**
* ビジネスをリストする
*
* @param listBusinessesDTO
* @return
*/
@CrossOrigin(origins = "http://localhost:8080")
@GetMapping("/listBusinesses")
public List<ListBusinessResponseBody.ListBusinessResponseBodyDataBusiness> listBusiness(ListBusinessesDTO listBusinessesDTO) {
return businessService.listBusiness(listBusinessesDTO);
}
/**
* ビジネスを作成する
*
* @param createBusinessDTO
* @return
*/
@CrossOrigin(origins = "http://localhost:8080")
@PostMapping("/createBusiness")
public Long createBusiness(@RequestBody CreateBusinessDTO createBusinessDTO) {
return businessService.createBusiness(createBusinessDTO);
}
/**
* ビジネスを更新する
*
* @param updateBusinessDTO
* @return
*/
@CrossOrigin(origins = "http://localhost:8080")
@PostMapping("/updateBusiness")
public boolean updateBusiness(@RequestBody UpdateBusinessDTO updateBusinessDTO) {
return businessService.updateBusiness(updateBusinessDTO);
}
/**
* ビジネスを削除する
*
* @param deleteBusinessDTO
* @return
*/
@CrossOrigin(origins = "http://localhost:8080")
@PostMapping("/deleteBusiness")
public boolean deleteBusiness(@RequestBody DeleteBusinessDTO deleteBusinessDTO) {
return businessService.deleteBusiness(deleteBusinessDTO);
}
/**
* @param pageNumber
* @param pageSize
* @return
*/
@CrossOrigin(origins = "http://localhost:8080")
@GetMapping("/listProjects")
public ListProjectsResponseBody.ListProjectsResponseBodyPageResult listProjects(Integer pageNumber, Integer pageSize) {
return projectService.listProjects(pageNumber, pageSize);
}
/**
* @param runSmokeTestDTO
* @return
*/
@CrossOrigin(origins = "http://localhost:8080")
@PutMapping("/runSmokeTest")
public List<ListInstancesResponseBody.ListInstancesResponseBodyDataInstances> runSmokeTest(@RequestBody RunSmokeTestDTO runSmokeTestDTO) {
return fileService.runSmokeTest(runSmokeTestDTO);
}
/**
* @param instanceId
* @param projectEnv
* @return
*/
@CrossOrigin(origins = "http://localhost:8080")
@GetMapping("/getLog")
public InstanceDetail getLog(@RequestParam Long instanceId, @RequestParam String projectEnv) {
return fileService.getInstanceLog(instanceId, projectEnv);
}
}フロントエンドコードの開発
エディタ、ディレクトリツリー、およびターミナルを初期化します。
サンプルコード:
const App: FunctionComponent<Props> = () => { const editorRef = useRef<HTMLDivElement>(null); const termianlRef = useRef<HTMLDivElement>(null); const [terminal, setTerminal] = useState<NextTerminal>(); const [editor, setEditor] = useState<monaco.editor.IStandaloneCodeEditor>(); const [expnadedKeys, setExpandedKeys] = useState<any[]>(); const [workspace, setWorkspace] = useState<number>(); const [workspaces, setWorkspaces] = useState<{ label: string, value: number }[]>([]); const [dataSource, setDataSource] = useState<any[]>(); const [selectedFile, setSelectedFile] = useState<number>(); const [loading, setLoading] = useState<boolean>(false); // エディターインスタンスを作成します。 useEffect(() => { if (editorRef.current) { const nextEditor = monaco.editor.create(editorRef.current, editorOptions); setEditor(nextEditor); return () => { nextEditor.dispose(); }; } }, [editorRef.current]); // ファイルを保存するための keydown イベントを追加します。 useEffect(() => { editor?.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => { if (!workspace) { showTips('Please select workspace first'); return; } saveFile(workspace, editor, selectedFile); }); }, [editor, workspace, selectedFile]); // ターミナルインスタンスを作成します。 useEffect(() => { if (termianlRef.current) { const term: NextTerminal = new Terminal(terminalOptions) as any; term.pointer = -1; term.stack = []; setTerminal(term); const fitAddon = new FitAddon(); term.loadAddon(fitAddon); term.open(termianlRef.current); fitAddon.fit(); term.write('$ '); return () => { term.dispose(); }; } }, [termianlRef.current]); // ターミナル入力イベントを登録します。 useEffect(() => { const event = terminal?.onKey(e => onTerminalKeyChange(e, terminal, dataSource, workspace)); return () => { event?.dispose(); }; }, [terminal, dataSource, workspace]); // ディレクトリツリーのデータソースを取得します。 useEffect(() => { workspace && (async () => { setLoading(true); const nextDataSource = await getTreeDataSource(workspace, workspaces); const defaultKey = nextDataSource?.[0]?.key; defaultKey && setExpandedKeys([defaultKey]); setDataSource(nextDataSource); setLoading(false); })(); }, [workspace]); // ディレクトリツリー内のファイルがクリックされたら、ファイルの詳細を取得してコードを表示します。 useEffect(() => { workspace && selectedFile && (async () => { setLoading(true); const file = await getFileInfo(workspace, selectedFile); editor?.setValue(file.content); editor?.getAction('editor.action.formatDocument').run(); setLoading(false); })(); }, [selectedFile]); // ワークスペースのリストを取得します。 useEffect(() => { (async () => { const list = await getWorkspaceList(); setWorkspaces(list); })(); }, []); const onExapnd = useCallback((keys: number[]) => { setExpandedKeys(keys); }, []); const onWorkspaceChange = useCallback((value: number) => { setWorkspace(value) }, []); const onTreeNodeSelect = useCallback((key: number[]) => { key[0] && setSelectedFile(key[0]) }, []); return ( <div className={cn(classes.appWrapper)}> <div className={cn(classes.leftArea)}> <div className={cn(classes.workspaceWrapper)}> Workspace: <Select value={workspace} dataSource={workspaces} onChange={onWorkspaceChange} autoWidth={false} showSearch /> </div> <div className={cn(classes.treeWrapper)}> <Tree dataSource={dataSource} isNodeBlock={{ defaultPaddingLeft: 20 }} expandedKeys={expnadedKeys} selectedKeys={[selectedFile]} onExpand={onExapnd} onSelect={onTreeNodeSelect} defaultExpandAll /> </div> </div> <div className={cn(classes.rightArea)}> <div className={cn(classes.monacoEditorWrapper)} ref={editorRef} /> <div className={cn(classes.panelWrapper)} ref={termianlRef} /> </div> <div className={cn(classes.loaderLine)} style={{ display: loading ? 'block' : 'none' }} /> </div> ); };サンプルのワークフローとファイルをクエリし、ディレクトリツリーを表示します。
次のフローチャートは、サンプルのワークフローとファイルをクエリする方法を示しています。
/** * ディレクトリツリーのデータソースを取得します。 * @param workspace ワークスペース ID。 * @param dataSource ワークスペースのリスト。 */ async function getTreeDataSource(workspace: number, dataSource: { label: string, value: number }[]) { try { const businesses = await services.ide.getBusinessList(workspace, openPlatformBusinessName); businesses.length === 0 && await services.ide.createBusiness(workspace, openPlatformBusinessName); } catch (e) { showError('You have no permission to access this workspace.'); return; } const fileFolderPath = `Workflow/${openPlatformBusinessName}/MaxCompute`; const files = await services.ide.getFileList(workspace, fileFolderPath); let children: { key: number, label: string }[] = []; if (files.length === 0) { try { const currentWorkspace = dataSource.find(i => i.value === workspace); const file1 = await services.ide.createFile(workspace, currentWorkspace!.label, fileFolderPath, 'simpleSQL.mc.sql', 'SELECT 1'); const file2 = await services.ide.createFile(workspace, currentWorkspace!.label, fileFolderPath, 'createTable.mc.sql', 'CREATE TABLE IF NOT EXISTS _qcc_mysql1_odps_source_20220113100903_done_ (\ncol string\n)\nCOMMENT \'DONE table that marks the completion of full data synchronization\'\nPARTITIONED BY\n(\nstatus STRING COMMENT \'DONE partition\'\n)\nLIFECYCLE 36500;'); children = children.concat([ { key: file1, label: 'simpleSQL.mc.sql' }, { key: file2, label: 'createTable.mc.sql' }, ]); } catch (e) { showError('Create file failed. The datasource odps_source does not exist.'); return; } } else { children = files.map((i) => ({ key: i.fileId, label: i.fileName })); } return [{ key: 1, label: openPlatformBusinessName, children }]; }ファイルを編集して保存した後、編集したファイルをバックエンドに渡してファイルを更新する必要があります。
サンプルコード:
/** * ファイルを保存します。Ctrl+S でトリガーされます。 * @param workspace ワークスペース ID。 * @param editor エディターインスタンス。 * @param selectedFile 選択されたファイル。 */ async function saveFile(workspace: number, editor: monaco.editor.IStandaloneCodeEditor, selectedFile?: number) { if (!selectedFile) { showTips('Please select a file.'); return; } const content = editor.getValue(); const result = await services.ide.updateFile(workspace, selectedFile, { content }); result ? showTips('Saved file') : showError('Failed to save file'); }ユーザーがターミナルで
dw run ...を入力すると、ファイルはスケジューリングシステムに送信され、スモークテストが実行されます。処理フローを以下に示し、コード例も示します。

/** * ターミナルのキーボードイベントを処理します。 * @param e イベントオブジェクト。 * @param term ターミナルインスタンス。 * @param dataSource ディレクトリツリーのデータソース。 * @param workspace ワークスペース ID。 */ function onTerminalKeyChange(e: { key: string; domEvent: KeyboardEvent; }, term: NextTerminal, dataSource: any, workspace?: number) { const ev = e.domEvent; const printable = !ev.altKey && !ev.ctrlKey && !ev.metaKey; term.inputText = typeof term.inputText === 'string' ? term.inputText : ''; switch (ev.key) { case 'ArrowUp': term.pointer = term.pointer < (term.stack.length - 1) ? term.pointer + 1 : term.pointer; term.inputText = term.stack[term.pointer]; term.write(`\x1b[2K\r$ ${term.inputText}`); break; case 'ArrowDown': term.pointer = term.pointer > -1 ? term.pointer - 1 : -1; term.inputText = term.pointer === -1 ? '' : term.stack[term.pointer]; term.write(`\x1b[2K\r$ ${term.inputText}`); break; case 'ArrowLeft': (term as any)._core.buffer.x > 2 && printable && term.write(e.key); break; case 'ArrowRight': (term as any)._core.buffer.x <= (term.inputText.length + 1) && printable && term.write(e.key); break; case 'Enter': commandHandler(term, dataSource, workspace); break; case 'Backspace': if ((term as any)._core.buffer.x > 2) { term.inputText = term.inputText.slice(0, -1); term.write('\b \b'); } break; default: if (printable) { term.inputText += e.key; term.write(e.key); } } } /** * タスク送信を処理するメソッド。ターミナルで 'dw run ...' が入力されたときにトリガーされます。 * @param term ターミナルインスタンス。 * @param dataSource ディレクトリツリーのデータソース。 * @param workspace ワークスペース ID。 */ async function commandHandler(term: NextTerminal, dataSource: any, workspace?: number) { term.write('\r\n$ '); const input = term.inputText; term.inputText = ''; if (['', undefined].includes(input)) { return; } term.stack = [input!, ...term.stack]; term.pointer = -1; if (!workspace) { term.write(highlight.text('[ERROR] You should select workspace first.\r\n$ ', brush)); return; } // これは入力コマンドラインの単純なパーサーです。コマンドが 'dw' で始まり、アクションが 'run' の場合、プロセスは続行されます。それ以外の場合は、エラーが報告されます。 const words = input?.split(' '); const tag = words?.[0].toLowerCase(); const command = words?.[1]?.toLowerCase(); const fileName = words?.[2]; if (tag !== 'dw' || !validCommands.includes(command!)) { term.write(highlight.text('[ERROR] Invalid command.\r\n$ ', brush)); return; } // 入力ファイルを取得します。 const source = dataSource?.[0]?.children.find((i: any) => i.label === fileName); const file = await services.ide.getFile(workspace, source.key); if (!file) { term.write(highlight.text('[ERROR] File name does not exist.\r\n$ ', brush)); return; } term.write(highlight.text('[INFO] Submitting file.\r\n$ ', brush)); // deploy file API を呼び出して、ファイルをスケジューリングシステムに公開します。 const response = await services.ide.deployFile(workspace, source.key); if (response) { term.write(highlight.text('[INFO] Submit file success.\r\n$ ', brush)); } else { term.write(highlight.text('[ERROR] Submit file failed.\r\n$ ', brush)); return; } // スモークテストを実行して、スケジューリングタスクを実行します。 let dag: services.ide.Dag; try { term.write(highlight.text('[INFO] Start to run task.\r\n$ ', brush)); dag = (await services.ide.runSmoke(workspace, file.nodeId, openPlatformBusinessName))[0]; term.write(highlight.text('[INFO] Trigger sql task success.\r\n$ ', brush)); } catch (e) { term.write(highlight.text('[ERROR] Trigger sql task failed.\r\n$ ', brush)); return; } // ポーリングしてタスクログを取得します。 const event = setInterval(async () => { try { const logInfo = await services.ide.getLog(dag.instanceId, 'DEV'); let log: string; switch (logInfo.instance.status) { case 'WAIT_TIME': log = 'Waiting for the scheduled time.'; break; case 'WAIT_RESOURCE': log = 'Waiting for resources...'; break; default: log = logInfo.instanceLog; } term.write(`${highlight.text(log, brush).replace(/\n/g, '\r\n')}\r\n$ `); const finished = ['SUCCESS', 'FAILURE', 'NOT_RUN'].includes(logInfo.instance.status); finished && clearInterval(event); } catch (e) { term.write(highlight.text('[ERROR] SQL Task run failed.\r\n$ ', brush)); return; } }, 3000); }
ローカルでデプロイして実行する
環境を準備するには、GitHub コードサンプルの指示に従ってください。依存関係には、Java 8 以降、Maven ビルドツール、Node.js 環境、および pnpm ツールが含まれます。次に、初期化コマンドを実行します。
pnpm installまた、ルートパスのAccessKeyペアに関連する情報を変更する必要があります。プロジェクトのルートディレクトリで次のコマンドを実行して、サンプルコードを実行します。
npm run example:ideブラウザのアドレスバーに https://localhost:8080 と入力して、結果を確認できます。
https://localhost:8080参考資料:完全なサンプルソースコードのダウンロード
GitHub から完全なサンプルソースコードをダウンロードできます。このトピックのすべての操作のサンプルコード:
import { useEffect, useRef, useState, useCallback } from 'react';
import type { FunctionComponent } from 'react';
import cn from 'classnames';
import * as monaco from 'monaco-editor';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { Tree, Select, Message } from '@alifd/next';
import * as highlight from '../helpers/highlight';
import * as services from '../services';
import classes from '../styles/app.module.css';
export interface Props {}
export interface NextTerminal extends Terminal {
inputText?: string;
stack: string[];
pointer: number;
}
const brush = {
rules: [
{ regex: /\bERROR\b/gmi, theme: 'red' },
{ regex: /\bWARN\b/gmi, theme: 'yellow' },
{ regex: /\bINFO\b/gmi, theme: 'green' },
{ regex: /^FAILED:.*$/gmi, theme: 'red' },
],
};
// サンプルワークフローの名前。
const openPlatformBusinessName = 'Open Platform Sample Workflow';
// エディターを作成するためのパラメーター。
const editorOptions = {
content: '',
language: 'sql',
theme: 'vs-dark',
automaticLayout: true,
fontSize: 16,
};
// ターミナルを作成するためのパラメーター。
const terminalOptions = {
cursorBlink: true,
cursorStyle: 'underline' as const,
fontSize: 16,
};
const validCommands = [
'run',
];
/**
* エラーメッセージのポップアップを表示するメソッド。
* @param message エラーメッセージ。
*/
function showError(message: string) {
Message.error({ title: 'Error Message', content: message });
}
/**
* ヒントのポップアップを表示するメソッド。
* @param message ヒントメッセージ。
*/
function showTips(message: string) {
Message.show({ title: 'Tips', content: message });
}
/**
* タスク送信を処理するメソッド。ターミナルで 'dw run ...' が入力されたときにトリガーされます。
* @param term ターミナルインスタンス。
* @param dataSource ディレクトリツリーのデータソース。
* @param workspace ワークスペース ID。
*/
async function commandHandler(term: NextTerminal, dataSource: any, workspace?: number) {
term.write('\r\n$ ');
const input = term.inputText;
term.inputText = '';
if (['', undefined].includes(input)) {
return;
}
term.stack = [input!, ...term.stack];
term.pointer = -1;
if (!workspace) {
term.write(highlight.text('[ERROR] You should select workspace first.\r\n$ ', brush));
return;
}
// これは入力コマンドラインの単純なパーサーです。コマンドが 'dw' で始まり、アクションが 'run' の場合、プロセスは続行されます。それ以外の場合は、エラーが報告されます。
const words = input?.split(' ');
const tag = words?.[0].toLowerCase();
const command = words?.[1]?.toLowerCase();
const fileName = words?.[2];
if (tag !== 'dw' || !validCommands.includes(command!)) {
term.write(highlight.text('[ERROR] Invalid command.\r\n$ ', brush));
return;
}
// 入力ファイルを取得します。
const source = dataSource?.[0]?.children.find((i: any) => i.label === fileName);
const file = await services.ide.getFile(workspace, source.key);
if (!file) {
term.write(highlight.text('[ERROR] File name does not exist.\r\n$ ', brush));
return;
}
term.write(highlight.text('[INFO] Submitting file.\r\n$ ', brush));
// deploy file API を呼び出して、ファイルをスケジューリングシステムに公開します。
const response = await services.ide.deployFile(workspace, source.key);
if (response) {
term.write(highlight.text('[INFO] Submit file success.\r\n$ ', brush));
} else {
term.write(highlight.text('[ERROR] Submit file failed.\r\n$ ', brush));
return;
}
// スモークテストを実行して、スケジューリングタスクを実行します。
let dag: services.ide.Dag;
try {
term.write(highlight.text('[INFO] Start to run task.\r\n$ ', brush));
dag = (await services.ide.runSmoke(workspace, file.nodeId, openPlatformBusinessName))[0];
term.write(highlight.text('[INFO] Trigger sql task success.\r\n$ ', brush));
} catch (e) {
term.write(highlight.text('[ERROR] Trigger sql task failed.\r\n$ ', brush));
return;
}
// ポーリングしてタスクログを取得します。
const event = setInterval(async () => {
try {
const logInfo = await services.ide.getLog(dag.instanceId, 'DEV');
let log: string;
switch (logInfo.instance.status) {
case 'WAIT_TIME':
log = 'Waiting for the scheduled time.';
break;
case 'WAIT_RESOURCE':
log = 'Waiting for resources...';
break;
default:
log = logInfo.instanceLog;
}
term.write(`${highlight.text(log, brush).replace(/\n/g, '\r\n')}\r\n$ `);
const finished = ['SUCCESS', 'FAILURE', 'NOT_RUN'].includes(logInfo.instance.status);
finished && clearInterval(event);
} catch (e) {
term.write(highlight.text('[ERROR] SQL Task run failed.\r\n$ ', brush));
return;
}
}, 3000);
}
/**
* ターミナルのキーボードイベントを処理します。
* @param e イベントオブジェクト。
* @param term ターミナルインスタンス。
* @param dataSource ディレクトリツリーのデータソース。
* @param workspace ワークスペース ID。
*/
function onTerminalKeyChange(e: { key: string; domEvent: KeyboardEvent; }, term: NextTerminal, dataSource: any, workspace?: number) {
const ev = e.domEvent;
const printable = !ev.altKey && !ev.ctrlKey && !ev.metaKey;
term.inputText = typeof term.inputText === 'string' ? term.inputText : '';
switch (ev.key) {
case 'ArrowUp':
term.pointer = term.pointer < (term.stack.length - 1) ? term.pointer + 1 : term.pointer;
term.inputText = term.stack[term.pointer];
term.write(`\x1b[2K\r$ ${term.inputText}`);
break;
case 'ArrowDown':
term.pointer = term.pointer > -1 ? term.pointer - 1 : -1;
term.inputText = term.pointer === -1 ? '' : term.stack[term.pointer];
term.write(`\x1b[2K\r$ ${term.inputText}`);
break;
case 'ArrowLeft':
(term as any)._core.buffer.x > 2 && printable && term.write(e.key);
break;
case 'ArrowRight':
(term as any)._core.buffer.x <= (term.inputText.length + 1) && printable && term.write(e.key);
break;
case 'Enter':
commandHandler(term, dataSource, workspace);
break;
case 'Backspace':
if ((term as any)._core.buffer.x > 2) {
term.inputText = term.inputText.slice(0, -1);
term.write('\b \b');
}
break;
default:
if (printable) {
term.inputText += e.key;
term.write(e.key);
}
}
}
/**
* ワークスペースのリストを取得します。
*/
async function getWorkspaceList() {
const response = await services.tenant.getProjectList();
const list = response.projectList.filter(i => i.projectStatusCode === 'AVAILABLE').map(i => (
{ label: i.projectName, value: i.projectId }
));
return list;
}
/**
* ディレクトリツリーのデータソースを取得します。
* @param workspace ワークスペース ID。
* @param dataSource ワークスペースのリスト。
*/
async function getTreeDataSource(workspace: number, dataSource: { label: string, value: number }[]) {
try {
const businesses = await services.ide.getBusinessList(workspace, openPlatformBusinessName);
businesses.length === 0 && await services.ide.createBusiness(workspace, openPlatformBusinessName);
} catch (e) {
showError('You have no permission to access this workspace.');
return;
}
const fileFolderPath = `Workflow/${openPlatformBusinessName}/MaxCompute`;
const files = await services.ide.getFileList(workspace, fileFolderPath);
let children: { key: number, label: string }[] = [];
if (files.length === 0) {
try {
const currentWorkspace = dataSource.find(i => i.value === workspace);
const file1 = await services.ide.createFile(workspace, currentWorkspace!.label, fileFolderPath, 'simpleSQL.mc.sql', 'SELECT 1');
const file2 = await services.ide.createFile(workspace, currentWorkspace!.label, fileFolderPath, 'createTable.mc.sql', 'CREATE TABLE IF NOT EXISTS _qcc_mysql1_odps_source_20220113100903_done_ (\ncol string\n)\nCOMMENT \'DONE table that marks the completion of full data synchronization\'\nPARTITIONED BY\n(\nstatus STRING COMMENT \'DONE partition\'\n)\nLIFECYCLE 36500;');
children = children.concat([
{ key: file1, label: 'simpleSQL.mc.sql' },
{ key: file2, label: 'createTable.mc.sql' },
]);
} catch (e) {
showError('Create file failed. The datasource odps_source does not exist.');
return;
}
} else {
children = files.map((i) => ({ key: i.fileId, label: i.fileName }));
}
return [{ key: 1, label: openPlatformBusinessName, children }];
}
/**
* ファイルの詳細を取得します。
* @param workspace ワークスペース ID。
* @param fileId ファイル ID。
*/
async function getFileInfo(workspace: number, fileId: number) {
const response = await services.ide.getFile(workspace, fileId);
return response;
}
/**
* ファイルを保存します。Ctrl+S でトリガーされます。
* @param workspace ワークスペース ID。
* @param editor エディターインスタンス。
* @param selectedFile 選択されたファイル。
*/
async function saveFile(workspace: number, editor: monaco.editor.IStandaloneCodeEditor, selectedFile?: number) {
if (!selectedFile) {
showTips('Please select a file.');
return;
}
const content = editor.getValue();
const result = await services.ide.updateFile(workspace, selectedFile, { content });
result ? showTips('Saved file') : showError('Failed to save file');
}
const App: FunctionComponent<Props> = () => {
const editorRef = useRef<HTMLDivElement>(null);
const termianlRef = useRef<HTMLDivElement>(null);
const [terminal, setTerminal] = useState<NextTerminal>();
const [editor, setEditor] = useState<monaco.editor.IStandaloneCodeEditor>();
const [expnadedKeys, setExpandedKeys] = useState<any[]>();
const [workspace, setWorkspace] = useState<number>();
const [workspaces, setWorkspaces] = useState<{ label: string, value: number }[]>([]);
const [dataSource, setDataSource] = useState<any[]>();
const [selectedFile, setSelectedFile] = useState<number>();
const [loading, setLoading] = useState<boolean>(false);
// エディターインスタンスを作成します。
useEffect(() => {
if (editorRef.current) {
const nextEditor = monaco.editor.create(editorRef.current, editorOptions);
setEditor(nextEditor);
return () => { nextEditor.dispose(); };
}
}, [editorRef.current]);
// ファイルを保存するための keydown イベントを追加します。
useEffect(() => {
editor?.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
if (!workspace) {
showTips('Please select workspace first');
return;
}
saveFile(workspace, editor, selectedFile);
});
}, [editor, workspace, selectedFile]);
// ターミナルインスタンスを作成します。
useEffect(() => {
if (termianlRef.current) {
const term: NextTerminal = new Terminal(terminalOptions) as any;
term.pointer = -1;
term.stack = [];
setTerminal(term);
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
term.open(termianlRef.current);
fitAddon.fit();
term.write('$ ');
return () => { term.dispose(); };
}
}, [termianlRef.current]);
// ターミナル入力イベントを登録します。
useEffect(() => {
const event = terminal?.onKey(e => onTerminalKeyChange(e, terminal, dataSource, workspace));
return () => {
event?.dispose();
};
}, [terminal, dataSource, workspace]);
// ディレクトリツリーのデータソースを取得します。
useEffect(() => {
workspace && (async () => {
setLoading(true);
const nextDataSource = await getTreeDataSource(workspace, workspaces);
const defaultKey = nextDataSource?.[0]?.key;
defaultKey && setExpandedKeys([defaultKey]);
setDataSource(nextDataSource);
setLoading(false);
})();
}, [workspace]);
// ディレクトリツリー内のファイルがクリックされたら、ファイルの詳細を取得してコードを表示します。
useEffect(() => {
workspace && selectedFile && (async () => {
setLoading(true);
const file = await getFileInfo(workspace, selectedFile);
editor?.setValue(file.content);
editor?.getAction('editor.action.formatDocument').run();
setLoading(false);
})();
}, [selectedFile]);
// ワークスペースのリストを取得します。
useEffect(() => {
(async () => {
const list = await getWorkspaceList();
setWorkspaces(list);
})();
}, []);
const onExapnd = useCallback((keys: number[]) => { setExpandedKeys(keys); }, []);
const onWorkspaceChange = useCallback((value: number) => { setWorkspace(value) }, []);
const onTreeNodeSelect = useCallback((key: number[]) => { key[0] && setSelectedFile(key[0]) }, []);
return (
<div className={cn(classes.appWrapper)}>
<div className={cn(classes.leftArea)}>
<div className={cn(classes.workspaceWrapper)}>
Workspace:
<Select
value={workspace}
dataSource={workspaces}
onChange={onWorkspaceChange}
autoWidth={false}
showSearch
/>
</div>
<div className={cn(classes.treeWrapper)}>
<Tree
dataSource={dataSource}
isNodeBlock={{ defaultPaddingLeft: 20 }}
expandedKeys={expnadedKeys}
selectedKeys={[selectedFile]}
onExpand={onExapnd}
onSelect={onTreeNodeSelect}
defaultExpandAll
/>
</div>
</div>
<div className={cn(classes.rightArea)}>
<div
className={cn(classes.monacoEditorWrapper)}
ref={editorRef}
/>
<div
className={cn(classes.panelWrapper)}
ref={termianlRef}
/>
</div>
<div className={cn(classes.loaderLine)} style={{ display: loading ? 'block' : 'none' }} />
</div>
);
};
export default App;