From 2bb13ee98dc8d8fbac3f16ae1816e26b1632ceb6 Mon Sep 17 00:00:00 2001 From: Satya Date: Fri, 15 Nov 2024 17:37:54 +0800 Subject: [PATCH] Refactor and add support for Yaci DevKit --- build.gradle | 36 +--- .../idea/account/ui/ListAccountDialog.java | 4 +- .../ui/details/AccountBasicDetailsForm.java | 4 +- .../ui/details/AccountDetailsDialog.java | 15 +- .../ui/details/AccountTransactionsUI.java | 17 +- .../idea/aiken/action/AikenActionGroup.java | 50 +++-- .../compile/action/AikenBuildAction.java | 11 +- .../compile/action/AikenFormatAction.java | 11 +- .../folding/AikenFolderingBuilder.java | 2 +- .../folding/AikenPairedBraceMatcher.java | 2 +- .../idea/aiken/module/AikenModuleBuilder.java | 6 +- .../configuration/common/CardanoNodeType.java | 2 +- .../idea/configuration/model/RemoteNode.java | 22 ++ .../configuration/ui/CLIProviderDialog.java | 4 +- .../ui/CardanoNodeConfigDialog.java | 20 +- .../ui/DevKitNodeConfigPanel.form | 139 ++++++++++++ .../ui/DevKitNodeConfigPanel.java | 204 ++++++++++++++++++ ...l.form => LocalYaciDevKitConfigPanel.form} | 66 +++++- ...l.java => LocalYaciDevKitConfigPanel.java} | 94 ++++++-- .../ui/RemoteNodeConfigPanel.java | 15 +- .../idea/core/action/BaseTxnAction.java | 2 +- .../idea/core/util/CLIProviderUtil.java | 12 +- .../idea/core/util/NetworkUrls.java | 2 + .../intelliada/idea/core/util/NodeType.java | 8 +- .../idea/nodeint/devkit/DevKitDownloader.java | 204 ++++++++++++++++++ .../service/CardanoServiceFactory.java | 38 ++++ .../nodeint/service/NodeServiceFactory.java | 12 +- .../service/api/CardanoAccountService.java | 3 +- .../nodeint/service/impl/NodeBaseService.java | 2 +- .../yaciprovider/YaciAccountServiceImpl.java | 157 ++++++++++++++ .../impl/yaciprovider/YaciBaseService.java | 144 +++++++++++++ .../yaciprovider/model/AddressBalanceDto.java | 25 +++ .../service/impl/yaciprovider/model/Amt.java | 25 +++ .../idea/nodeint/util/NetworkHelper.java | 16 +- .../idea/toolwindow/ui/CardanoExplorer.java | 1 + .../transaction/ui/TransactionEntryForm.java | 4 +- .../idea/utxos/ui/ListUtxosDialog.java | 4 +- src/main/resources/META-INF/plugin.xml | 70 +----- .../YaciAccountServiceImplTest.java | 52 +++++ 39 files changed, 1310 insertions(+), 195 deletions(-) rename src/main/java/com/bloxbean/intelliada/idea/{ => aiken}/folding/AikenFolderingBuilder.java (97%) rename src/main/java/com/bloxbean/intelliada/idea/{ => aiken}/folding/AikenPairedBraceMatcher.java (94%) create mode 100644 src/main/java/com/bloxbean/intelliada/idea/configuration/ui/DevKitNodeConfigPanel.form create mode 100644 src/main/java/com/bloxbean/intelliada/idea/configuration/ui/DevKitNodeConfigPanel.java rename src/main/java/com/bloxbean/intelliada/idea/configuration/ui/{CLIProviderPanel.form => LocalYaciDevKitConfigPanel.form} (63%) rename src/main/java/com/bloxbean/intelliada/idea/configuration/ui/{CLIProviderPanel.java => LocalYaciDevKitConfigPanel.java} (59%) create mode 100644 src/main/java/com/bloxbean/intelliada/idea/nodeint/devkit/DevKitDownloader.java create mode 100644 src/main/java/com/bloxbean/intelliada/idea/nodeint/service/CardanoServiceFactory.java create mode 100644 src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciAccountServiceImpl.java create mode 100644 src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciBaseService.java create mode 100644 src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/model/AddressBalanceDto.java create mode 100644 src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/model/Amt.java create mode 100644 src/test/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciAccountServiceImplTest.java diff --git a/build.gradle b/build.gradle index 6d00968..e09f36e 100644 --- a/build.gradle +++ b/build.gradle @@ -37,20 +37,13 @@ repositories { } compileJava { - sourceCompatibility = '17' - targetCompatibility = '17' + sourceCompatibility = '21' + targetCompatibility = '21' } -//compileKotlin { -// kotlinOptions.jvmTarget = "1.8" -//} -//compileTestKotlin { -// kotlinOptions.jvmTarget = "1.8" -//} - dependencies { intellijPlatform { - intellijIdeaCommunity("2023.3.6") + intellijIdeaCommunity("2024.2") plugin("com.redhat.devtools.lsp4ij:0.5.0") pluginVerifier() zipSigner() @@ -58,23 +51,25 @@ dependencies { } implementation fileTree(include: ['*.jar'], dir: 'lib') - implementation('com.bloxbean.cardano:cardano-client-lib:0.4.3') { + implementation('com.bloxbean.cardano:cardano-client-lib:0.6.2') { exclude group: 'org.slf4j', module: 'slf4j-api' } - implementation('com.bloxbean.cardano:cardano-client-backend-blockfrost:0.4.3') { + implementation('com.bloxbean.cardano:cardano-client-backend-blockfrost:0.6.2') { exclude group: 'org.slf4j', module: 'slf4j-api' exclude group: 'com.bloxbean.cardano', module: 'cardano-client-lib' } - implementation('com.bloxbean.cardano:cardano-client-backend-koios:0.4.3') { + implementation('com.bloxbean.cardano:cardano-client-backend-koios:0.6.2') { exclude group: 'org.slf4j', module: 'slf4j-api' exclude group: 'com.bloxbean.cardano', module: 'cardano-client-lib' } implementation('com.moandjiezana.toml:toml4j:0.7.2') implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10") - compileOnly 'org.projectlombok:lombok:1.18.20' + implementation 'org.json:json:20240303' + + compileOnly 'org.projectlombok:lombok:1.18.34' - annotationProcessor 'org.projectlombok:lombok:1.18.20' + annotationProcessor 'org.projectlombok:lombok:1.18.34' testImplementation group: 'junit', name: 'junit', version: '4.12' } @@ -103,17 +98,6 @@ grammarKit { } } -//intellij { -// version = '2023.2' -//// version = "232-EAP-SNAPSHOT" -//// type = "IU" -//// version = "2024.1.4" -//// type = "IU" -// type = 'IC' -// plugins = ['java'] -// -// updateSinceUntilBuild = false -//} patchPluginXml { changeNotes = changeLogAsHtml() } diff --git a/src/main/java/com/bloxbean/intelliada/idea/account/ui/ListAccountDialog.java b/src/main/java/com/bloxbean/intelliada/idea/account/ui/ListAccountDialog.java index 547825a..993be8c 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/account/ui/ListAccountDialog.java +++ b/src/main/java/com/bloxbean/intelliada/idea/account/ui/ListAccountDialog.java @@ -32,9 +32,9 @@ import com.bloxbean.intelliada.idea.core.util.Networks; import com.bloxbean.intelliada.idea.nodeint.CardanoNodeConfigurationHelper; import com.bloxbean.intelliada.idea.nodeint.exception.TargetNodeNotConfigured; +import com.bloxbean.intelliada.idea.nodeint.service.CardanoServiceFactory; import com.bloxbean.intelliada.idea.nodeint.service.api.CardanoAccountService; import com.bloxbean.intelliada.idea.nodeint.service.api.LogListenerAdapter; -import com.bloxbean.intelliada.idea.nodeint.service.impl.AccountServiceImpl; import com.bloxbean.intelliada.idea.toolwindow.CardanoConsole; import com.bloxbean.intelliada.idea.util.IdeaUtil; import com.intellij.icons.AllIcons; @@ -400,7 +400,7 @@ public void run() { CardanoAccountService cardanoAccountService = null; try { - cardanoAccountService = new AccountServiceImpl(project, new LogListenerAdapter(console)); + cardanoAccountService = CardanoServiceFactory.getAccountService(project, new LogListenerAdapter(console)); } catch (TargetNodeNotConfigured targetNodeNotConfigured) { console.showErrorMessage(targetNodeNotConfigured.getMessage()); IdeaUtil.showNotification(project, "Node Configuration", diff --git a/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountBasicDetailsForm.java b/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountBasicDetailsForm.java index bc4d78c..8566794 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountBasicDetailsForm.java +++ b/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountBasicDetailsForm.java @@ -2,10 +2,10 @@ import com.bloxbean.intelliada.idea.account.model.CardanoAccount; import com.bloxbean.intelliada.idea.account.service.AccountService; +import com.bloxbean.intelliada.idea.nodeint.service.CardanoServiceFactory; import com.bloxbean.intelliada.idea.nodeint.service.api.CardanoAccountService; import com.bloxbean.intelliada.idea.nodeint.service.api.LogListenerAdapter; import com.bloxbean.intelliada.idea.nodeint.service.api.model.AssetBalance; -import com.bloxbean.intelliada.idea.nodeint.service.impl.AccountServiceImpl; import com.bloxbean.intelliada.idea.toolwindow.CardanoConsole; import com.bloxbean.intelliada.idea.utxos.service.UtxoChooser; import com.intellij.openapi.application.ApplicationManager; @@ -118,7 +118,7 @@ public void run() { return; try { - CardanoAccountService accountService = new AccountServiceImpl(project, new LogListenerAdapter(console)); + CardanoAccountService accountService = CardanoServiceFactory.getAccountService(project, new LogListenerAdapter(console)); List assetBalanceList = accountService.getBalance(address); assetComboBoxModel.addAll(assetBalanceList); if (assetBalanceList.size() > 0) diff --git a/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountDetailsDialog.java b/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountDetailsDialog.java index 83dc276..f4ec92c 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountDetailsDialog.java +++ b/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountDetailsDialog.java @@ -1,6 +1,9 @@ package com.bloxbean.intelliada.idea.account.ui.details; import com.bloxbean.intelliada.idea.account.model.CardanoAccount; +import com.bloxbean.intelliada.idea.configuration.model.RemoteNode; +import com.bloxbean.intelliada.idea.core.util.NodeType; +import com.bloxbean.intelliada.idea.nodeint.CardanoNodeConfigurationHelper; import com.bloxbean.intelliada.idea.toolwindow.CardanoConsole; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; @@ -43,7 +46,17 @@ public boolean getAccountInfoUpdated() { private void createUIComponents() { // TODO: place custom component creation code here + + if (project != null) { + RemoteNode remoteNode = CardanoNodeConfigurationHelper.getTargetRemoteNode(project); + if (remoteNode != null) { + if (remoteNode.getNodeType() == NodeType.YaciDevKit) { + transactionUI = new AccountTransactionsUI(project, false); + return; //we don't support this yet + } + } + } //Need to initialize here because as non-null project required for JsonEditorTextField - transactionUI = new AccountTransactionsUI(project); + transactionUI = new AccountTransactionsUI(project, true); } } diff --git a/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountTransactionsUI.java b/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountTransactionsUI.java index d3095ed..efa8844 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountTransactionsUI.java +++ b/src/main/java/com/bloxbean/intelliada/idea/account/ui/details/AccountTransactionsUI.java @@ -5,10 +5,10 @@ import com.bloxbean.cardano.client.backend.model.metadata.MetadataJSONContent; import com.bloxbean.intelliada.idea.account.model.CardanoAccount; import com.bloxbean.intelliada.idea.common.ui.JsonEditorTextField; +import com.bloxbean.intelliada.idea.nodeint.service.CardanoServiceFactory; import com.bloxbean.intelliada.idea.nodeint.service.api.CardanoAccountService; import com.bloxbean.intelliada.idea.nodeint.service.api.LogListenerAdapter; import com.bloxbean.intelliada.idea.nodeint.service.api.TransactionInfoService; -import com.bloxbean.intelliada.idea.nodeint.service.impl.AccountServiceImpl; import com.bloxbean.intelliada.idea.nodeint.service.impl.TransactionInfoServiceImpl; import com.bloxbean.intelliada.idea.toolwindow.CardanoConsole; import com.bloxbean.intelliada.idea.util.JsonUtil; @@ -39,8 +39,11 @@ public class AccountTransactionsUI { private CardanoAccount account; private DefaultListModel txnListModel; - public AccountTransactionsUI(Project project) { + private boolean supported; + + public AccountTransactionsUI(Project project, boolean supported) { this.project = project; + this.supported = supported; } public void initialize(CardanoAccount account, CardanoConsole console) { @@ -50,6 +53,14 @@ public void initialize(CardanoAccount account, CardanoConsole console) { attachListeners(); getRecentTransactions(); + + if(!supported) { + transactionContentTf.setText("Transaction details are not supported for the selected node type"); + metadataEditorTf.setText("Transaction metadata are not supported for the selected node type"); + + transactionContentTf.setEnabled(false); + metadataEditorTf.setEnabled(false); + } } private void attachListeners() { @@ -100,7 +111,7 @@ public void run() { return; try { - CardanoAccountService accountService = new AccountServiceImpl(project, new LogListenerAdapter(console)); + CardanoAccountService accountService = CardanoServiceFactory.getAccountService(project, new LogListenerAdapter(console)); List txnList = accountService.getRecentTransactions(address, 100, 1, OrderEnum.desc); txnListModel.addAll(txnList); } catch (Exception e) { diff --git a/src/main/java/com/bloxbean/intelliada/idea/aiken/action/AikenActionGroup.java b/src/main/java/com/bloxbean/intelliada/idea/aiken/action/AikenActionGroup.java index c349b7c..7a41093 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/aiken/action/AikenActionGroup.java +++ b/src/main/java/com/bloxbean/intelliada/idea/aiken/action/AikenActionGroup.java @@ -3,39 +3,47 @@ import com.bloxbean.intelliada.idea.aiken.common.AikenIcons; import com.bloxbean.intelliada.idea.aiken.module.AikenModuleType; import com.bloxbean.intelliada.idea.aiken.module.pkg.AikenTomlService; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; public class AikenActionGroup extends DefaultActionGroup { @Override - public void update(AnActionEvent event) { + public void update(@NotNull AnActionEvent event) { Project project = event.getProject(); + if (project == null) { + event.getPresentation().setVisible(false); + return; + } DataContext dataContext = event.getDataContext(); - final Module module = LangDataKeys.MODULE.getData(dataContext); - final ModuleType moduleType = module == null ? null : ModuleType.get(module); - boolean isAikenModule = moduleType instanceof AikenModuleType; + // Wrap logic in ReadAction.nonBlocking or ReadAction.compute to move off EDT + boolean isAikenModule = ReadAction.compute(() -> { + final Module module = LangDataKeys.MODULE.getData(dataContext); + if (module == null) return false; - //Try to check if aiken.toml file available. - //For non-Aiken modules - if(!isAikenModule) { - AikenTomlService aikenTomlService = AikenTomlService.getInstance(project); - if (aikenTomlService != null) - isAikenModule = aikenTomlService.isAikenProject(); - } + final ModuleType moduleType = ModuleType.get(module); + return moduleType instanceof AikenModuleType; + }); - if(isAikenModule) { - event.getPresentation().setVisible(true); - event.getPresentation().setIcon(AikenIcons.AIKEN_ICON); - } else { - event.getPresentation().setVisible(false); - event.getPresentation().setIcon(AikenIcons.AIKEN_ICON); + // For non-Aiken modules, check if aiken.toml is available in background + if (!isAikenModule) { + isAikenModule = ReadAction.nonBlocking(() -> { + AikenTomlService aikenTomlService = AikenTomlService.getInstance(project); + return aikenTomlService != null && aikenTomlService.isAikenProject(); + }).executeSynchronously(); } + + event.getPresentation().setVisible(isAikenModule); + event.getPresentation().setIcon(AikenIcons.AIKEN_ICON); + } + + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.BGT; } } diff --git a/src/main/java/com/bloxbean/intelliada/idea/aiken/compile/action/AikenBuildAction.java b/src/main/java/com/bloxbean/intelliada/idea/aiken/compile/action/AikenBuildAction.java index 3587a16..7551544 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/aiken/compile/action/AikenBuildAction.java +++ b/src/main/java/com/bloxbean/intelliada/idea/aiken/compile/action/AikenBuildAction.java @@ -14,10 +14,7 @@ import com.intellij.execution.process.OSProcessHandler; import com.intellij.icons.AllIcons; import com.intellij.notification.NotificationType; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileDocumentManager; @@ -163,4 +160,10 @@ public void run(@NotNull ProgressIndicator indicator) { ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, new BackgroundableProcessIndicator(task)); } + + @NotNull + @Override + public ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.BGT; + } } diff --git a/src/main/java/com/bloxbean/intelliada/idea/aiken/compile/action/AikenFormatAction.java b/src/main/java/com/bloxbean/intelliada/idea/aiken/compile/action/AikenFormatAction.java index 0e53464..75246da 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/aiken/compile/action/AikenFormatAction.java +++ b/src/main/java/com/bloxbean/intelliada/idea/aiken/compile/action/AikenFormatAction.java @@ -13,10 +13,7 @@ import com.intellij.execution.process.OSProcessHandler; import com.intellij.icons.AllIcons; import com.intellij.notification.NotificationType; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileDocumentManager; @@ -159,4 +156,10 @@ public void run(@NotNull ProgressIndicator indicator) { ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, new BackgroundableProcessIndicator(task)); } + + @NotNull + @Override + public ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.BGT; + } } diff --git a/src/main/java/com/bloxbean/intelliada/idea/folding/AikenFolderingBuilder.java b/src/main/java/com/bloxbean/intelliada/idea/aiken/folding/AikenFolderingBuilder.java similarity index 97% rename from src/main/java/com/bloxbean/intelliada/idea/folding/AikenFolderingBuilder.java rename to src/main/java/com/bloxbean/intelliada/idea/aiken/folding/AikenFolderingBuilder.java index 3900588..3c81312 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/folding/AikenFolderingBuilder.java +++ b/src/main/java/com/bloxbean/intelliada/idea/aiken/folding/AikenFolderingBuilder.java @@ -1,4 +1,4 @@ -package com.bloxbean.intelliada.idea.folding; +package com.bloxbean.intelliada.idea.aiken.folding; import com.bloxbean.intelliada.idea.aiken.lang.psi.*; import com.intellij.lang.ASTNode; diff --git a/src/main/java/com/bloxbean/intelliada/idea/folding/AikenPairedBraceMatcher.java b/src/main/java/com/bloxbean/intelliada/idea/aiken/folding/AikenPairedBraceMatcher.java similarity index 94% rename from src/main/java/com/bloxbean/intelliada/idea/folding/AikenPairedBraceMatcher.java rename to src/main/java/com/bloxbean/intelliada/idea/aiken/folding/AikenPairedBraceMatcher.java index ca830d2..1f42727 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/folding/AikenPairedBraceMatcher.java +++ b/src/main/java/com/bloxbean/intelliada/idea/aiken/folding/AikenPairedBraceMatcher.java @@ -1,4 +1,4 @@ -package com.bloxbean.intelliada.idea.folding; +package com.bloxbean.intelliada.idea.aiken.folding; import com.bloxbean.intelliada.idea.aiken.lang.psi.AikenTypes; import com.intellij.lang.BracePair; diff --git a/src/main/java/com/bloxbean/intelliada/idea/aiken/module/AikenModuleBuilder.java b/src/main/java/com/bloxbean/intelliada/idea/aiken/module/AikenModuleBuilder.java index ae721ab..af98b19 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/aiken/module/AikenModuleBuilder.java +++ b/src/main/java/com/bloxbean/intelliada/idea/aiken/module/AikenModuleBuilder.java @@ -96,7 +96,7 @@ public void moduleCreated(@NotNull Module module) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - ProjectGeneratorUtil.createNewContract(module.getProject(), srcRoot, AikenContractTemplates.AK_HELLOWORLD_TEMPLATE, module.getName() + ".ak"); + ProjectGeneratorUtil.createNewContract(module.getProject(), srcRoot, AikenContractTemplates.AK_HELLOWORLD_TEMPLATE, module.getName().toLowerCase() + ".ak"); build(module); } @@ -189,10 +189,10 @@ public void run(@NotNull ProgressIndicator indicator) { public void setupRootModel(@NotNull ModifiableRootModel rootModel) throws ConfigurationException { rootModel.inheritSdk(); - String moduleName = rootModel.getModule().getName(); + String moduleName = rootModel.getModule().getName().toLowerCase(); String ownerInputFieldVal= ownerInputField.getValue(); - String owner = ownerInputFieldVal != null? ownerInputFieldVal.trim(): System.getProperty("user.name"); + String owner = ownerInputFieldVal != null? ownerInputFieldVal.trim().toLowerCase(): System.getProperty("user.name").toLowerCase(); Project project = rootModel.getProject(); String basePath = project.getBasePath(); diff --git a/src/main/java/com/bloxbean/intelliada/idea/configuration/common/CardanoNodeType.java b/src/main/java/com/bloxbean/intelliada/idea/configuration/common/CardanoNodeType.java index 1e039a8..80914f7 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/configuration/common/CardanoNodeType.java +++ b/src/main/java/com/bloxbean/intelliada/idea/configuration/common/CardanoNodeType.java @@ -2,5 +2,5 @@ //This is just for UI public enum CardanoNodeType { - Blockfrost, Koios + LocalYaciDevKit, YaciDevKit, Blockfrost, Koios } diff --git a/src/main/java/com/bloxbean/intelliada/idea/configuration/model/RemoteNode.java b/src/main/java/com/bloxbean/intelliada/idea/configuration/model/RemoteNode.java index 1991f8b..3d32c14 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/configuration/model/RemoteNode.java +++ b/src/main/java/com/bloxbean/intelliada/idea/configuration/model/RemoteNode.java @@ -17,6 +17,10 @@ public class RemoteNode { private Map headers; private int timeout; + //local node properties + private String home; + private String version; + public RemoteNode() { } @@ -108,6 +112,22 @@ public void setTimeout(int timeout) { this.timeout = timeout; } + public String getHome() { + return home; + } + + public void setHome(String home) { + this.home = home; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + public void updateValues(RemoteNode node) { //Update everything except id if (node == null) return; @@ -120,6 +140,8 @@ public void updateValues(RemoteNode node) { //Update everything except id this.setProtocolMagic(node.getProtocolMagic()); this.setHeaders(node.getHeaders()); this.setTimeout(node.getTimeout()); + this.setHome(node.getHome()); + this.setVersion(node.getVersion()); } @Override diff --git a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CLIProviderDialog.java b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CLIProviderDialog.java index 39484cb..0d05915 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CLIProviderDialog.java +++ b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CLIProviderDialog.java @@ -11,7 +11,7 @@ public class CLIProviderDialog extends DialogWrapper { - private CLIProviderPanel localSDKPanel; + private LocalYaciDevKitConfigPanel localSDKPanel; public CLIProviderDialog(Project project) { this(project, null); @@ -19,7 +19,7 @@ public CLIProviderDialog(Project project) { public CLIProviderDialog(Project project, CLIProvider provider) { super(project); - localSDKPanel = new CLIProviderPanel(provider); + // localSDKPanel = new LocalYaciDevKitConfigPanel(provider); init(); setTitle("Cardano Installation"); } diff --git a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CardanoNodeConfigDialog.java b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CardanoNodeConfigDialog.java index 60c2d73..8312e85 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CardanoNodeConfigDialog.java +++ b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CardanoNodeConfigDialog.java @@ -15,10 +15,12 @@ public class CardanoNodeConfigDialog { private JPanel mainPanel; private JComboBox nodeTypesCB; private JPanel contentPanel; + private DevKitNodeConfigPanel devKitNodeConfigPanel; private RemoteNodeConfigPanel blockfrostConfigPanel; private KoiosNodeConfigPanel koiosNodeConfigPanel; public CardanoNodeConfigDialog(Project project, RemoteNode remoteNode) { + nodeTypesCB.addItem(CardanoNodeType.YaciDevKit); nodeTypesCB.addItem(CardanoNodeType.Blockfrost); nodeTypesCB.addItem(CardanoNodeType.Koios); @@ -26,10 +28,12 @@ public CardanoNodeConfigDialog(Project project, RemoteNode remoteNode) { } public void initialize(Project project, RemoteNode remoteNode) { + devKitNodeConfigPanel = new DevKitNodeConfigPanel(remoteNode); blockfrostConfigPanel = new RemoteNodeConfigPanel(remoteNode); koiosNodeConfigPanel = new KoiosNodeConfigPanel(project); //contentPanel.setLayout(new CardLayout()); + contentPanel.add(devKitNodeConfigPanel.getMainPanel(), CardanoNodeType.YaciDevKit.toString()); contentPanel.add(blockfrostConfigPanel.getMainPanel(), CardanoNodeType.Blockfrost.toString()); contentPanel.add(koiosNodeConfigPanel.getMainPanel(), CardanoNodeType.Koios.toString()); @@ -47,9 +51,15 @@ public void itemStateChanged(ItemEvent e) { remoteNode.getNodeType().equals(NodeType.KOIOS_CUSTOM)) { nodeTypesCB.setSelectedItem(CardanoNodeType.Koios); koiosNodeConfigPanel.setNodeData(remoteNode); - } else { //Default is Blocfrost + } else if (remoteNode.getNodeType().equals(NodeType.BLOCKFROST_MAINNET) || + remoteNode.getNodeType().equals(NodeType.BLOCKFROST_PREPROD) || + remoteNode.getNodeType().equals(NodeType.BLOCKFROST_PREVIEW) || + remoteNode.getNodeType().equals(NodeType.BLOCKFROST_CUSTOM)) { nodeTypesCB.setSelectedItem(CardanoNodeType.Blockfrost); //default blockfrostConfigPanel.setNodeData(remoteNode); + } else { + nodeTypesCB.setSelectedItem(CardanoNodeType.YaciDevKit); + devKitNodeConfigPanel.setNodeData(remoteNode); } nodeTypesCB.setEnabled(false); @@ -58,7 +68,9 @@ public void itemStateChanged(ItemEvent e) { public ValidationInfo doValidate() { CardanoNodeType cardanoNodeType = (CardanoNodeType) nodeTypesCB.getSelectedItem(); - if (CardanoNodeType.Blockfrost.equals(cardanoNodeType)) { + if (CardanoNodeType.YaciDevKit.equals(cardanoNodeType)) { + return devKitNodeConfigPanel.doValidate(); + } else if (CardanoNodeType.Blockfrost.equals(cardanoNodeType)) { return blockfrostConfigPanel.doValidate(); } else if (CardanoNodeType.Koios.equals(cardanoNodeType)) { return koiosNodeConfigPanel.doValidate(); @@ -69,7 +81,9 @@ public ValidationInfo doValidate() { public NodeConfigurator getNodeConfigurator() { CardanoNodeType cardanoNodeType = (CardanoNodeType) nodeTypesCB.getSelectedItem(); - if (CardanoNodeType.Blockfrost.equals(cardanoNodeType)) { + if (CardanoNodeType.YaciDevKit.equals(cardanoNodeType)) { + return devKitNodeConfigPanel; + } else if (CardanoNodeType.Blockfrost.equals(cardanoNodeType)) { return blockfrostConfigPanel; } else if (CardanoNodeType.Koios.equals(cardanoNodeType)) { return koiosNodeConfigPanel; diff --git a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/DevKitNodeConfigPanel.form b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/DevKitNodeConfigPanel.form new file mode 100644 index 0000000..83ca12c --- /dev/null +++ b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/DevKitNodeConfigPanel.form @@ -0,0 +1,139 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/DevKitNodeConfigPanel.java b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/DevKitNodeConfigPanel.java new file mode 100644 index 0000000..2bf4533 --- /dev/null +++ b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/DevKitNodeConfigPanel.java @@ -0,0 +1,204 @@ +package com.bloxbean.intelliada.idea.configuration.ui; + +import com.bloxbean.cardano.client.api.model.Result; +import com.bloxbean.intelliada.idea.configuration.model.RemoteNode; +import com.bloxbean.intelliada.idea.core.util.Network; +import com.bloxbean.intelliada.idea.core.util.NetworkUrls; +import com.bloxbean.intelliada.idea.core.util.Networks; +import com.bloxbean.intelliada.idea.core.util.NodeType; +import com.bloxbean.intelliada.idea.nodeint.service.api.LogListener; +import com.bloxbean.intelliada.idea.nodeint.service.api.NetworkInfoService; +import com.bloxbean.intelliada.idea.nodeint.service.impl.NetworkServiceImpl; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator; +import com.intellij.openapi.ui.ComboBox; +import com.intellij.openapi.ui.ValidationInfo; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.Map; +import java.util.UUID; + +//Config panel for YaciDevKit api +public class DevKitNodeConfigPanel implements NodeConfigurator { + private JPanel mainPanel; + private JTextField nameTf; + private JButton testConnectionBtn; + private JComboBox nodeTypesCB; + private JTextField protocolMagicTf; + private JTextField apiEndpointTf; + private JLabel connectionTestLabel; + private boolean newConfig = true; + + public DevKitNodeConfigPanel() { + this(null); + } + + public DevKitNodeConfigPanel(RemoteNode node) { + super(); + + initialize(node); + } + + private void initialize(RemoteNode node) { + handleNodeTypeSelection(); + + testConnectionBtn.addActionListener(e -> { + testNetworkConnection(); + }); + } + + @Override + public void setNodeData(RemoteNode node) { + if (node != null) { + newConfig = false; + nameTf.setText(node.getName()); + nodeTypesCB.setSelectedItem(node.getNodeType()); + apiEndpointTf.setText(node.getApiEndpoint()); +// authKey.setText(node.getAuthKey()); +// networkTf.setText(node.getNetwork()); +// networkIdTf.setText(node.getNetworkId()); + protocolMagicTf.setText(node.getProtocolMagic()); + } + } + + private void handleNodeTypeSelection() { + nodeTypesCB.addActionListener(e -> { + apiEndpointTf.setText(NetworkUrls.YACI_DEVKIT_BASEURL); + apiEndpointTf.setEnabled(true); + }); + } + + private void setNetwork(Network network) { + if (network != null) { + protocolMagicTf.setText(network.getProtocolMagic()); + } + } + + public @Nullable ValidationInfo doValidate() { + if (StringUtil.isEmpty(getName())) { + return new ValidationInfo("Please enter a valid name", nameTf); + } + + if (getNodeType() == null || getNodeType().equals(NodeType.EMPTY)) { + return new ValidationInfo("Please select a valid node type", nodeTypesCB); + } + + if (StringUtil.isEmpty(getApiEndpoint())) { + return new ValidationInfo("Please enter a valid api endpoint url", apiEndpointTf); + } + + return null; + } + + public JPanel getMainPanel() { + return mainPanel; + } + + public String getName() { + return StringUtil.trim(nameTf.getText()); + } + + public String getApiEndpoint() { + return StringUtil.trim(apiEndpointTf.getText()); + } + + public String getAuthKey() { + return "dummy key"; + } + + public NodeType getNodeType() { + return NodeType.YaciDevKit; + } + + public String getNetwork() { + return "devkit_network"; + } + + public String getNetworkId() { + return "devkit_network_id"; + //return networkIdTf.getText(); + } + + public String getProtocolMagic() { + return protocolMagicTf.getText(); + } + + @Override + public Map getHeaders() { + return null; + } + + @Override + public int getTimeout() { + return 120; //Not used for Blockfrost + } + + private void testNetworkConnection() { + + Task.Backgroundable task = new Task.Backgroundable(null, "Network Info") { + + @Override + public void run(@NotNull ProgressIndicator indicator) { + RemoteNode remoteNode = new RemoteNode(); + remoteNode.setId(UUID.randomUUID().toString()); //Some random id + remoteNode.setName(getName()); + remoteNode.setApiEndpoint(getApiEndpoint()); +// remoteNode.setAuthKey(getAuthKey()); + remoteNode.setNodeType(getNodeType()); + remoteNode.setProtocolMagic(getProtocolMagic()); + // remoteNode.setNetworkId(getNetworkId()); + + LogListener logListener = new LogListener() { + @Override + public void info(String msg) { + + } + + @Override + public void error(String msg) { + + } + + @Override + public void warn(String msg) { + + } + }; + + try { + //First remove + NetworkInfoService networkService = new NetworkServiceImpl(remoteNode, logListener); + Long currentSlot = networkService.getCurrentSlot(); + + if (currentSlot != null && currentSlot > 0) { + connectionTestLabel.setForeground(Color.black); + connectionTestLabel.setText("Successfully connected !!!"); + } else { + connectionTestLabel.setForeground(Color.red); +// String response = result.getResponse(); +// if (response != null && response.length() > 30) +// response = response.substring(0, 27) + "..."; + + connectionTestLabel.setText("Could not connect to node " ); +// connectionTestLabel.setToolTipText(result.getResponse()); + } + } catch (Exception exception) { + connectionTestLabel.setText("Could not connect to node. Reason: " + exception.getMessage()); + } + } + }; + + ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, new BackgroundableProcessIndicator(task)); + } + + private void createUIComponents() { + // TODO: place custom component creation code here + nodeTypesCB = new ComboBox(new NodeType[]{NodeType.YaciDevKit}); + } +} diff --git a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CLIProviderPanel.form b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/LocalYaciDevKitConfigPanel.form similarity index 63% rename from src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CLIProviderPanel.form rename to src/main/java/com/bloxbean/intelliada/idea/configuration/ui/LocalYaciDevKitConfigPanel.form index 5e1a224..9d3735d 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CLIProviderPanel.form +++ b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/LocalYaciDevKitConfigPanel.form @@ -1,5 +1,5 @@ -
+ @@ -21,6 +21,12 @@ + + + + + + @@ -28,7 +34,7 @@ - + @@ -45,7 +51,7 @@ - + @@ -54,7 +60,7 @@ - + @@ -83,7 +89,7 @@ - + @@ -95,13 +101,61 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CLIProviderPanel.java b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/LocalYaciDevKitConfigPanel.java similarity index 59% rename from src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CLIProviderPanel.java rename to src/main/java/com/bloxbean/intelliada/idea/configuration/ui/LocalYaciDevKitConfigPanel.java index 7c7ec39..6f2414b 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/CLIProviderPanel.java +++ b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/LocalYaciDevKitConfigPanel.java @@ -1,7 +1,9 @@ package com.bloxbean.intelliada.idea.configuration.ui; -import com.bloxbean.intelliada.idea.configuration.model.CLIProvider; +import com.bloxbean.intelliada.idea.configuration.model.RemoteNode; import com.bloxbean.intelliada.idea.core.util.CLIProviderUtil; +import com.bloxbean.intelliada.idea.core.util.NodeType; +import com.bloxbean.intelliada.idea.nodeint.devkit.DevKitDownloader; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.text.StringUtil; @@ -11,34 +13,40 @@ import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; -import static com.bloxbean.intelliada.idea.core.util.CLIProviderUtil.getCardanoCLICommand; +import static com.bloxbean.intelliada.idea.core.util.CLIProviderUtil.getDevKitScript; import static com.bloxbean.intelliada.idea.core.util.CLIProviderUtil.getSuggestedCLIFolder; -public class CLIProviderPanel { - private final static Logger LOG = Logger.getInstance(CLIProviderPanel.class); +public class LocalYaciDevKitConfigPanel implements NodeConfigurator { + private final static Logger LOG = Logger.getInstance(LocalYaciDevKitConfigPanel.class); private JTextField versionTf; private JPanel mainPanel; private JTextField nameTf; private TextFieldWithBrowseButton homeTfWithBrowserBtn; private JLabel errorMsgLabel; + private JButton installDevKitBtn; + private JTextField restEndpointTf; + private JTextField protocolMagicTf; private JTextField homeTf; - public CLIProviderPanel() { + public LocalYaciDevKitConfigPanel() { this(null); } - public CLIProviderPanel(CLIProvider cliProvider) { + public LocalYaciDevKitConfigPanel(RemoteNode node) { super(); - if (cliProvider != null) { - nameTf.setText(cliProvider.getName()); - homeTf.setText(cliProvider.getHome()); - versionTf.setText(cliProvider.getVersion()); + if (node != null) { + nameTf.setText(node.getName()); + homeTf.setText(node.getHome()); + versionTf.setText(node.getVersion()); } - homeTf.setToolTipText("Folder where the \'cardano-cli\' binary is available. \n"); + homeTf.setToolTipText("Folder where the \'Yaci DevKit\' is available. \n"); homeTf.addFocusListener(new FocusAdapter() { @Override @@ -46,6 +54,23 @@ public void focusLost(FocusEvent e) { checkCardanoCLIExecutable(); } }); + + initializeListeners(); + } + + private void initializeListeners() { + installDevKitBtn.addActionListener(e -> { + //Install Yaci DevKit + //TODO + //Download the latest Yaci Devkit zip and extract it to the selected folder + //fomr https://github.com/bloxbean/yaci-devkit/releases + + String path = homeTf.getText(); + + Path installDir = Paths.get(path + File.separator + "yaci-devkit"); + DevKitDownloader devKitDownloader = new DevKitDownloader(installDir); + devKitDownloader.installSDK(); + }); } public JPanel getMainPanel() { @@ -56,10 +81,55 @@ public String getHome() { return homeTf.getText(); } + @Override + public void setNodeData(RemoteNode node) { + + } + public String getName() { return nameTf.getText(); } + @Override + public String getApiEndpoint() { + return ""; + } + + @Override + public String getAuthKey() { + return ""; + } + + @Override + public NodeType getNodeType() { + return null; + } + + @Override + public String getNetwork() { + return ""; + } + + @Override + public String getNetworkId() { + return ""; + } + + @Override + public String getProtocolMagic() { + return ""; + } + + @Override + public Map getHeaders() { + return Map.of(); + } + + @Override + public int getTimeout() { + return 0; + } + public String getVersion() { return versionTf.getText(); } @@ -89,7 +159,7 @@ private void checkCardanoCLIExecutable() { errorMsgLabel.setText(""); //reset error msg versionTf.setText(""); - if (!new File(homeTf.getText() + File.separator + getCardanoCLICommand()).exists()) { + if (!new File(homeTf.getText() + File.separator + getDevKitScript()).exists()) { versionTf.setText(""); printError("\'cardano-cli\' was not found. Please make sure \'cardano-cli\' file is available under the selected folder."); return; diff --git a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/RemoteNodeConfigPanel.java b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/RemoteNodeConfigPanel.java index 1aefbbd..3f982fc 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/RemoteNodeConfigPanel.java +++ b/src/main/java/com/bloxbean/intelliada/idea/configuration/ui/RemoteNodeConfigPanel.java @@ -73,22 +73,18 @@ public void setNodeData(RemoteNode node) { private void handleNodeTypeSelection() { nodeTypesCB.addActionListener(e -> { - if (NodeType.BLOCKFROST_TESTNET.equals(nodeTypesCB.getSelectedItem()) - || NodeType.BLOCKFROST_MAINNET.equals(nodeTypesCB.getSelectedItem()) - || NodeType.BLOCKFROST_PREPOD.equals(nodeTypesCB.getSelectedItem()) + if (NodeType.BLOCKFROST_MAINNET.equals(nodeTypesCB.getSelectedItem()) + || NodeType.BLOCKFROST_PREPROD.equals(nodeTypesCB.getSelectedItem()) || NodeType.BLOCKFROST_PREVIEW.equals(nodeTypesCB.getSelectedItem()) || NodeType.BLOCKFROST_CUSTOM.equals(nodeTypesCB.getSelectedItem()) ) { apiEndpointTf.setEnabled(false); authKeyLabel.setText("Project Id"); - if (NodeType.BLOCKFROST_TESTNET.equals(nodeTypesCB.getSelectedItem())) { - apiEndpointTf.setText(NetworkUrls.BLOCKFROST_TESTNET_BASEURL); - setNetwork(Networks.testnet()); - } else if (NodeType.BLOCKFROST_MAINNET.equals(nodeTypesCB.getSelectedItem())) { + if (NodeType.BLOCKFROST_MAINNET.equals(nodeTypesCB.getSelectedItem())) { apiEndpointTf.setText(NetworkUrls.BLOCKFROST_MAINNET_BASEURL); setNetwork(Networks.mainnet()); - } else if (NodeType.BLOCKFROST_PREPOD.equals(nodeTypesCB.getSelectedItem())) { + } else if (NodeType.BLOCKFROST_PREPROD.equals(nodeTypesCB.getSelectedItem())) { apiEndpointTf.setText(NetworkUrls.BLOCKFROST_PREPOD_BASEURL); setNetwork(Networks.prepod()); } else if (NodeType.BLOCKFROST_PREVIEW.equals(nodeTypesCB.getSelectedItem())) { @@ -237,9 +233,8 @@ public void warn(String msg) { private void createUIComponents() { // TODO: place custom component creation code here nodeTypesCB = new ComboBox(new NodeType[]{NodeType.EMPTY, - NodeType.BLOCKFROST_PREPOD, + NodeType.BLOCKFROST_PREPROD, NodeType.BLOCKFROST_PREVIEW, - NodeType.BLOCKFROST_TESTNET, NodeType.BLOCKFROST_MAINNET }); } diff --git a/src/main/java/com/bloxbean/intelliada/idea/core/action/BaseTxnAction.java b/src/main/java/com/bloxbean/intelliada/idea/core/action/BaseTxnAction.java index c061f79..f286d8c 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/core/action/BaseTxnAction.java +++ b/src/main/java/com/bloxbean/intelliada/idea/core/action/BaseTxnAction.java @@ -30,7 +30,7 @@ protected String getExportFileLocation(Project project, JComponent parent) { fc.showSaveDialog(parent); File destination = fc.getSelectedFile(); - if (destination.exists()) { + if (destination != null && destination.exists()) { int ret = 0; try { ret = Messages.showYesNoCancelDialog(String.format("Already a file exists with file name %s. Do you want to overwrite?", diff --git a/src/main/java/com/bloxbean/intelliada/idea/core/util/CLIProviderUtil.java b/src/main/java/com/bloxbean/intelliada/idea/core/util/CLIProviderUtil.java index c983f55..abcdd46 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/core/util/CLIProviderUtil.java +++ b/src/main/java/com/bloxbean/intelliada/idea/core/util/CLIProviderUtil.java @@ -19,7 +19,7 @@ public class CLIProviderUtil { public static String getVersionString(String cliFolder) throws Exception { if (cliFolder == null) return null; - String cardanoCLICmd = getCardanoCLICommand(); + String cardanoCLICmd = getDevKitScript(); File file = new File(cliFolder); VirtualFile home = LocalFileSystem.getInstance().findFileByIoFile(file); @@ -39,12 +39,12 @@ public static String getVersionString(String cliFolder) throws Exception { return null; } - public static String getCardanoCLICommand() { - String cardanoCLICmd = "cardano-cli"; + public static String getDevKitScript() { + String devKitScript = "devkit.sh"; if (SystemInfo.isWindows) - cardanoCLICmd = "cardano-cli.exe"; + devKitScript = "devkit.bat"; - return cardanoCLICmd; + return devKitScript; } public static String runProcessAndExit(String program, String command) throws InterruptedException, ExecutionException, IOException { @@ -119,7 +119,7 @@ public static boolean isMacDaedalusHome(String home) { } private static boolean cardanoCLIExists(String path) { - File cli = new File(path, getCardanoCLICommand()); + File cli = new File(path, getDevKitScript()); return cli.exists(); } diff --git a/src/main/java/com/bloxbean/intelliada/idea/core/util/NetworkUrls.java b/src/main/java/com/bloxbean/intelliada/idea/core/util/NetworkUrls.java index 5d2c0a0..8971fcd 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/core/util/NetworkUrls.java +++ b/src/main/java/com/bloxbean/intelliada/idea/core/util/NetworkUrls.java @@ -7,4 +7,6 @@ public class NetworkUrls { public final static String BLOCKFROST_MAINNET_BASEURL = "https://cardano-mainnet.blockfrost.io/api/v0/"; public final static String BLOCKFROST_PREPOD_BASEURL = Constants.BLOCKFROST_PREPROD_URL; public final static String BLOCKFROST_PREVIEW_BASEURL = Constants.BLOCKFROST_PREVIEW_URL; + + public final static String YACI_DEVKIT_BASEURL = "http://localhost:8080/api/v1/"; } diff --git a/src/main/java/com/bloxbean/intelliada/idea/core/util/NodeType.java b/src/main/java/com/bloxbean/intelliada/idea/core/util/NodeType.java index d3b7548..37ab9a6 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/core/util/NodeType.java +++ b/src/main/java/com/bloxbean/intelliada/idea/core/util/NodeType.java @@ -6,16 +6,14 @@ //This node type is actually stored in the configuration public enum NodeType { EMPTY(""), - CARDANO_WALLET("Cardano Wallet"), - CARDANO_GRAPHQL("Cardano GraphQL"), - BLOCKFROST_TESTNET("Blockfrost Testnet"), BLOCKFROST_MAINNET("Blockfrost Mainnet"), - BLOCKFROST_PREPOD("Blockfrost Prepod"), + BLOCKFROST_PREPROD("Blockfrost Preprod"), BLOCKFROST_PREVIEW("Blockfrost Preview"), BLOCKFROST_CUSTOM("Blockfrost Custom"), KOIOS_PREPROD("Koios Preprod"), KOIOS_MAINNET("Koios Mainnet"), - KOIOS_CUSTOM("Koios Custom"); + KOIOS_CUSTOM("Koios Custom"), + YaciDevKit("Yaci DevKit"); private String displayName; diff --git a/src/main/java/com/bloxbean/intelliada/idea/nodeint/devkit/DevKitDownloader.java b/src/main/java/com/bloxbean/intelliada/idea/nodeint/devkit/DevKitDownloader.java new file mode 100644 index 0000000..2a20da0 --- /dev/null +++ b/src/main/java/com/bloxbean/intelliada/idea/nodeint/devkit/DevKitDownloader.java @@ -0,0 +1,204 @@ +package com.bloxbean.intelliada.idea.nodeint.devkit; + +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationType; +import com.intellij.notification.Notifications; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.util.io.FileUtil; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; +import org.jetbrains.annotations.NotNull; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.file.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class DevKitDownloader { + + private final Path installDir; + + public DevKitDownloader(Path installDir) { + this.installDir = installDir; + } + + public void installSDK() { + new Task.Backgroundable(null, "Installing SDK...", true) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + try { + // Fetch latest release info + String zipUrl = fetchLatestReleaseZipUrl(); + if (zipUrl == null) + throw new IllegalStateException("No download URL found for the latest release."); + + // Define paths for download and extraction + Path downloadPath = installDir.resolve("yaci-devkit.zip"); + + // Download the zip file + downloadZip(zipUrl, installDir.toFile().getAbsolutePath()); + + // Extract the zip file + unzip(downloadPath, installDir); +// extractZip(downloadPath.toFile().getAbsolutePath(), installDir.toFile().getAbsolutePath()); + + // Notify user of success + Notifications.Bus.notify(new Notification("Install SDK", "Success", "SDK installed successfully.", NotificationType.INFORMATION)); + } catch (Exception e) { + // Notify user of error + e.printStackTrace(); + Notifications.Bus.notify(new Notification("Install SDK", "Error", "Failed to install SDK: " + e.getMessage(), NotificationType.ERROR)); + } + } + }.queue(); + } + + private String fetchLatestReleaseZipUrl() throws IOException, InterruptedException { + String apiUrl = "https://api.github.com/repos/bloxbean/yaci-devkit/releases/latest"; + + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(apiUrl)) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + String responseBody = response.body(); + + // Parse JSON to extract download URL + JSONObject jsonObject = new JSONObject(responseBody); + JSONArray assets = jsonObject.getJSONArray("assets"); + if (assets.length() > 0) { + String downloadUrl = null; + for (int i = 0; i < assets.length(); i++) { + JSONObject asset = assets.getJSONObject(i); + String name = asset.getString("name"); + if (name != null && + name.startsWith("yaci-devkit") && name.endsWith(".zip")) { + downloadUrl = asset.getString("browser_download_url"); + } + } + return downloadUrl; + } else { + throw new IllegalStateException("No assets found for the latest release."); + } + } + + private Path downloadZip(String downloadUrl, String targetDir) { + + String fileUrl = downloadUrl; // Replace with your file URL + + try { + URL url = new URL(fileUrl); + HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); + long fileSize = httpConn.getContentLengthLong(); + + //Get file name from url + String targetFileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1, fileUrl.length()); + + Path targetPath = Paths.get(targetDir, targetFileName); + Files.createDirectories(targetPath.getParent()); + + try (InputStream inputStream = httpConn.getInputStream(); + FileOutputStream outputStream = new FileOutputStream(targetPath.toFile())) { + + byte[] buffer = new byte[4096]; + int bytesRead; + long totalBytesRead = 0; + int percentCompleted; + + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + totalBytesRead += bytesRead; + percentCompleted = (int) ((totalBytesRead * 100) / fileSize); + //write("\rDownloading: " + percentCompleted + "%"); + } + System.out.println(); + //writeLn(success("Download complete for %s!", component)); + } catch (Exception e) { + e.printStackTrace(); + } + + return targetPath; + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + + public void extractZip(String filePath, String extractDir) throws IOException { + try (FileInputStream fis = new FileInputStream(filePath); + BufferedInputStream bis = new BufferedInputStream(fis); + ZipArchiveInputStream zipIn = new ZipArchiveInputStream(bis)) { + + ArchiveEntry entry; + while ((entry = zipIn.getNextEntry()) != null) { + if (entry.isDirectory()) { + continue; + } + + Path entryDestination = Paths.get(extractDir, entry.getName()); + Files.createDirectories(entryDestination.getParent()); + + try (OutputStream out = new FileOutputStream(entryDestination.toFile())) { + byte[] buffer = new byte[4096]; + int len; + + long bytesRead = 0; + + // writeLn(infoLabel("Extracting", "Extracting %s", clusterConfig.getYaciCliHome() + File.separator + entry.getName())); + while ((len = zipIn.read(buffer)) != -1) { + out.write(buffer, 0, len); + bytesRead += len; + } + } + } + System.out.println(); // Move to next line after extraction completes + } + } + + + private void unzip(Path zipFilePath, Path destDir) throws IOException { + byte[] buffer = new byte[1024]; + File destDirFile = destDir.toFile(); + if (!destDirFile.exists()) { + destDirFile.mkdirs(); + } + + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath.toFile()))) { + ZipEntry zipEntry = zis.getNextEntry(); + while (zipEntry != null) { + File newFile = new File(destDirFile, zipEntry.getName()); + if (zipEntry.isDirectory()) { + newFile.mkdirs(); + } else { + // Create parent directories if needed + new File(newFile.getParent()).mkdirs(); + try (FileOutputStream fos = new FileOutputStream(newFile)) { + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + } + } + zipEntry = zis.getNextEntry(); + } + zis.closeEntry(); + } + + //rename the extracted folder + String fileName = zipFilePath.toFile().getName(); + String folderName = fileName.substring(0, fileName.lastIndexOf(".")); + + FileUtil.moveDirWithContent(new File(destDirFile, folderName), new File(destDirFile, "yaci-devkit")); + } +} diff --git a/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/CardanoServiceFactory.java b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/CardanoServiceFactory.java new file mode 100644 index 0000000..f8417b2 --- /dev/null +++ b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/CardanoServiceFactory.java @@ -0,0 +1,38 @@ +package com.bloxbean.intelliada.idea.nodeint.service; + +import com.bloxbean.intelliada.idea.account.service.AccountService; +import com.bloxbean.intelliada.idea.configuration.model.RemoteNode; +import com.bloxbean.intelliada.idea.core.util.NodeType; +import com.bloxbean.intelliada.idea.nodeint.CardanoNodeConfigurationHelper; +import com.bloxbean.intelliada.idea.nodeint.exception.TargetNodeNotConfigured; +import com.bloxbean.intelliada.idea.nodeint.service.api.CardanoAccountService; +import com.bloxbean.intelliada.idea.nodeint.service.api.LogListener; +import com.bloxbean.intelliada.idea.nodeint.service.impl.AccountServiceImpl; +import com.bloxbean.intelliada.idea.nodeint.service.impl.NetworkServiceImpl; +import com.bloxbean.intelliada.idea.nodeint.service.impl.yaciprovider.YaciAccountServiceImpl; +import com.intellij.openapi.project.Project; + +public class CardanoServiceFactory { + + public static CardanoAccountService getAccountService(Project project, LogListener logListener) throws TargetNodeNotConfigured { + RemoteNode remoteNode = CardanoNodeConfigurationHelper.getTargetRemoteNode(project); + if (remoteNode == null) { + throw new TargetNodeNotConfigured("Please select a default node first"); + } + + if (remoteNode.getNodeType() == NodeType.YaciDevKit) { + return new YaciAccountServiceImpl(remoteNode, logListener); + } else { + return new AccountServiceImpl(project, logListener); + } + } + + public static NetworkServiceImpl getNetworkService(Project project, LogListener logListener) throws TargetNodeNotConfigured { + RemoteNode remoteNode = CardanoNodeConfigurationHelper.getTargetRemoteNode(project); + if (remoteNode == null) { + throw new TargetNodeNotConfigured("Please select a default node first"); + } + + return new NetworkServiceImpl(remoteNode, logListener); + } +} diff --git a/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/NodeServiceFactory.java b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/NodeServiceFactory.java index 2bb7928..de7e740 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/NodeServiceFactory.java +++ b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/NodeServiceFactory.java @@ -36,9 +36,15 @@ public BackendService getBackendService(RemoteNode remoteNode) { if (backendService != null) return backendService; - if (NodeType.BLOCKFROST_TESTNET.equals(remoteNode.getNodeType()) || - NodeType.BLOCKFROST_MAINNET.equals(remoteNode.getNodeType()) || - NodeType.BLOCKFROST_PREPOD.equals(remoteNode.getNodeType()) || + if (NodeType.YaciDevKit.equals(remoteNode.getNodeType())) { + backendService + = new BFBackendService(remoteNode.getApiEndpoint(), "Some dummy key"); + backendServiceMap.put(remoteNode.getId(), backendService); + if (LOG.isDebugEnabled()) { + LOG.debug("Backend service created for the node : " + remoteNode); + } + } else if (NodeType.BLOCKFROST_MAINNET.equals(remoteNode.getNodeType()) || + NodeType.BLOCKFROST_PREPROD.equals(remoteNode.getNodeType()) || NodeType.BLOCKFROST_PREVIEW.equals(remoteNode.getNodeType()) || NodeType.BLOCKFROST_CUSTOM.equals(remoteNode.getNodeType()) ) { diff --git a/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/api/CardanoAccountService.java b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/api/CardanoAccountService.java index 0085dff..59234ac 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/api/CardanoAccountService.java +++ b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/api/CardanoAccountService.java @@ -6,10 +6,11 @@ import com.bloxbean.intelliada.idea.nodeint.exception.ApiCallException; import com.bloxbean.intelliada.idea.nodeint.service.api.model.AssetBalance; +import java.net.URISyntaxException; import java.util.List; public interface CardanoAccountService { - public Result getAdaBalance(String address) throws ApiCallException; + public Result getAdaBalance(String address) throws ApiCallException, URISyntaxException; public List getBalance(String address) throws ApiCallException; diff --git a/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/NodeBaseService.java b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/NodeBaseService.java index a524479..31588cc 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/NodeBaseService.java +++ b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/NodeBaseService.java @@ -101,7 +101,7 @@ protected void waitForTransaction(String txnId) throws ApiCallException { Network network = NetworkUtil.getNetworkType(remoteNode); if (network != null) logListener.info("Check transaction details here : " - + NetworkHelper.getInstance().getTxnHashUrl(network.getName(), txnId)); + + NetworkHelper.getInstance().getTxnHashUrl(remoteNode.getNodeType(), network.getName(), txnId)); } catch (Exception e) { //Ignore diff --git a/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciAccountServiceImpl.java b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciAccountServiceImpl.java new file mode 100644 index 0000000..700d295 --- /dev/null +++ b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciAccountServiceImpl.java @@ -0,0 +1,157 @@ +package com.bloxbean.intelliada.idea.nodeint.service.impl.yaciprovider; + +import com.bloxbean.cardano.client.api.common.OrderEnum; +import com.bloxbean.cardano.client.api.exception.ApiException; +import com.bloxbean.cardano.client.api.model.Result; +import com.bloxbean.cardano.client.api.model.Utxo; +import com.bloxbean.cardano.client.api.util.AssetUtil; +import com.bloxbean.cardano.client.transaction.spec.Asset; +import com.bloxbean.cardano.client.util.HexUtil; +import com.bloxbean.intelliada.idea.configuration.model.RemoteNode; +import com.bloxbean.intelliada.idea.nodeint.exception.ApiCallException; +import com.bloxbean.intelliada.idea.nodeint.exception.TargetNodeNotConfigured; +import com.bloxbean.intelliada.idea.nodeint.service.api.CardanoAccountService; +import com.bloxbean.intelliada.idea.nodeint.service.api.LogListener; +import com.bloxbean.intelliada.idea.nodeint.service.api.model.AssetBalance; +import com.bloxbean.intelliada.idea.nodeint.service.impl.yaciprovider.model.AddressBalanceDto; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.intellij.openapi.project.Project; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.bloxbean.intelliada.idea.util.AdaConversionUtil.LOVELACE; + +public class YaciAccountServiceImpl extends YaciBaseService implements CardanoAccountService { + + public YaciAccountServiceImpl(Project project) throws TargetNodeNotConfigured { + super(project); + } + + public YaciAccountServiceImpl(RemoteNode remoteNode, LogListener logListener) throws TargetNodeNotConfigured { + super(remoteNode, logListener); + } + + @Override + public Result getAdaBalance(String address) { + try { + String apiUrl = baseUrl + "addresses/" + address + "/balance"; + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI(apiUrl)) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() == 200) { + ObjectMapper mapper = new ObjectMapper(); + AddressBalanceDto addressBalanceDto = mapper.readValue(response.body(), AddressBalanceDto.class); + if (addressBalanceDto != null) + return addressBalanceDto.getAmounts().stream() + .filter(amt -> amt.getUnit().equals(LOVELACE)) + .map(amt -> amt.getQuantity()) + .findFirst() + .map(amount -> Result.success(amount.toString()).withValue(amount).code(200)) + .orElse(Result.error("No lovelace amount found")); + else + return Result.error("No lovelace amount found"); + } else { + return Result.error("GET request failed with status code: " + response.statusCode()); + } + } catch (Exception e) { + return Result.error("Error getting account balance : " + e.getMessage()); + } + + } + + @Override + public List getBalance(String address) throws ApiCallException { + try { + String apiUrl = baseUrl + "addresses/" + address + "/balance"; + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI(apiUrl)) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() == 200) { + ObjectMapper mapper = new ObjectMapper(); + AddressBalanceDto addressBalanceDto = mapper.readValue(response.body(), AddressBalanceDto.class); + if (addressBalanceDto != null) { + var amts = addressBalanceDto.getAmounts(); + List assetBalances = new ArrayList<>(); + if (amts != null) { + for (var amt : amts) { + if (!amt.getUnit().equals(LOVELACE)) { + AssetBalance assetBalance = new AssetBalance(); + assetBalance.setUnit(amt.getUnit()); + + var policyAssetName = AssetUtil.getPolicyIdAndAssetName(amt.getUnit()); + assetBalance.setPolicy(policyAssetName._1); + assetBalance.setAssetName(new String(HexUtil.decodeHexString(policyAssetName._2))); + assetBalance.setQuantity(amt.getQuantity()); + + Asset asset = new Asset(assetBalance.getAssetName(), amt.getQuantity()); + assetBalance.setAssetName(asset.getName()); + assetBalance.setFingerPrint(AssetUtil.calculateFingerPrint(assetBalance.getPolicy(), asset.getNameAsHex())); + assetBalances.add(assetBalance); + } else { + AssetBalance assetBalance = new AssetBalance(); + assetBalance.setUnit(LOVELACE); + assetBalance.setQuantity(amt.getQuantity()); + assetBalances.add(0, assetBalance); + } + } + } + + return assetBalances; + + } else + return Collections.emptyList(); + } else { + return Collections.emptyList(); + } + } catch (Exception e) { + return Collections.emptyList(); + } + } + + @Override + public List getUtxos(String address, int count, int page, OrderEnum order) throws ApiCallException { + try { + var result = backendService.getUtxoService().getUtxos(address, count, page, order); + if (result.isSuccessful()) { + return result.getValue(); + } else { + throw new ApiCallException(result.getResponse()); + } + } catch (ApiException e) { + throw new ApiCallException("Error getting UTXOs : " + e.getMessage()); + } + } + + @Override + public List getRecentTransactions(String address, int count, int page, OrderEnum order) throws ApiCallException { + try { + var result = backendService.getAddressService().getTransactions(address, count, page, order); + if (result.isSuccessful()) { + return result.getValue() + .stream() + .map(tx -> tx.getTxHash()) + .toList(); + } else { + throw new ApiCallException(result.getResponse()); + } + } catch (Exception e) { + return Collections.emptyList(); + } + } +} diff --git a/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciBaseService.java b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciBaseService.java new file mode 100644 index 0000000..13ddd66 --- /dev/null +++ b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciBaseService.java @@ -0,0 +1,144 @@ +package com.bloxbean.intelliada.idea.nodeint.service.impl.yaciprovider; + +import com.bloxbean.cardano.client.api.model.Result; +import com.bloxbean.cardano.client.backend.api.BackendService; +import com.bloxbean.cardano.client.backend.blockfrost.service.BFBackendService; +import com.bloxbean.cardano.client.backend.model.TransactionContent; +import com.bloxbean.cardano.client.util.JsonUtil; +import com.bloxbean.intelliada.idea.configuration.model.RemoteNode; +import com.bloxbean.intelliada.idea.core.util.Network; +import com.bloxbean.intelliada.idea.core.util.NetworkUtil; +import com.bloxbean.intelliada.idea.nodeint.CardanoNodeConfigurationHelper; +import com.bloxbean.intelliada.idea.nodeint.exception.ApiCallException; +import com.bloxbean.intelliada.idea.nodeint.exception.TargetNodeNotConfigured; +import com.bloxbean.intelliada.idea.nodeint.service.NodeServiceFactory; +import com.bloxbean.intelliada.idea.nodeint.service.api.LogListener; +import com.bloxbean.intelliada.idea.nodeint.util.NetworkHelper; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; + +public class YaciBaseService { + private static Logger LOG = Logger.getInstance(YaciBaseService.class); + + protected LogListener logListener; + protected BackendService backendService; + private RemoteNode remoteNode; + protected String baseUrl; + + public YaciBaseService(Project project) throws TargetNodeNotConfigured { + this(project, new LogListener() { + @Override + public void info(String msg) { + LOG.info(msg); + } + + @Override + public void error(String msg) { + LOG.info(msg); + } + + @Override + public void warn(String msg) { + LOG.info(msg); + } + + @Override + public void error(String msg, Throwable t) { + LOG.error(msg, t); + } + + @Override + public void warn(String msg, Throwable t) { + LOG.warn(msg, t); + } + }); + } + + public YaciBaseService(Project project, LogListener logListener) throws TargetNodeNotConfigured { + RemoteNode remoteNode = CardanoNodeConfigurationHelper.getTargetRemoteNode(project); + if (remoteNode == null) { + throw new TargetNodeNotConfigured("Please select a default node first"); + } + + this.logListener = logListener; + this.remoteNode = remoteNode; + this.baseUrl = this.remoteNode.getApiEndpoint(); + + this.backendService = new BFBackendService(remoteNode.getApiEndpoint(), "Dummy Key"); + } + + public YaciBaseService(RemoteNode node, LogListener logListener) throws TargetNodeNotConfigured { + if (node == null) + throw new TargetNodeNotConfigured("Target node cannot be null. Please select a valid remote node"); + + this.remoteNode = node; + this.logListener = logListener; + this.baseUrl = this.remoteNode.getApiEndpoint(); + + this.backendService = new BFBackendService(remoteNode.getApiEndpoint(), "Dummy Key"); + } + + protected void printRemoteNodeDetails() { + this.logListener.info("Connecting to " + remoteNode.getApiEndpoint()); + this.logListener.info("Node Name : " + remoteNode.getName()); + this.logListener.info("Node type : " + remoteNode.getNodeType()); + this.logListener.info("\n"); + } + + protected void waitForTransaction(String txnId) throws ApiCallException { + if (StringUtil.isEmpty(txnId)) { + logListener.error("Transaction id cannot be null"); + throw new ApiCallException("Transaction id cannot be null"); + } + + try { + //logListener.info("Waiting for transaction to be mined ...."); + int count = 0; + while (count < 60) { + Result txnResult = backendService.getTransactionService() + .getTransaction(txnId); + if (txnResult.isSuccessful()) { + logListener.info(""); + logListener.info("Txn content :"); + logListener.info(JsonUtil.getPrettyJson(txnResult.getValue())); + + try { + Network network = NetworkUtil.getNetworkType(remoteNode); + if (network != null) + logListener.info("Check transaction details here : " + + NetworkHelper.getInstance().getTxnHashUrl(remoteNode.getNodeType(), network.getName(), txnId)); + + } catch (Exception e) { + //Ignore + } + return; + } else { + logListener.printWait(count + " sec - " + " Waiting for transaction to be mined."); + } + + count++; + Thread.currentThread().sleep(1000); + } + logListener.info(""); + logListener.warn("Taking too long to mine the transaction. " + + "Please check transaction status in the Cardano explorer." + + "\nTransaction Id : " + txnId); + + } catch (Exception e) { + logListener.error("Error getting transaction status", e); + throw new ApiCallException("Error getting transaction status", e); + } + } + + protected RemoteNode getRemoteNode() { + return remoteNode; + } + + /** + * Required only for test connection call. + */ + protected void clearCachedBackendService() { + NodeServiceFactory.getInstance().nodeRemoved(remoteNode); + } +} diff --git a/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/model/AddressBalanceDto.java b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/model/AddressBalanceDto.java new file mode 100644 index 0000000..885e8bb --- /dev/null +++ b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/model/AddressBalanceDto.java @@ -0,0 +1,25 @@ +package com.bloxbean.intelliada.idea.nodeint.service.impl.yaciprovider.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder(toBuilder = true) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class AddressBalanceDto { + private String address; + private List amounts; + private Long slot; + + private Long lastBalanceCalculationBlock; +} diff --git a/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/model/Amt.java b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/model/Amt.java new file mode 100644 index 0000000..aabc0c9 --- /dev/null +++ b/src/main/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/model/Amt.java @@ -0,0 +1,25 @@ +package com.bloxbean.intelliada.idea.nodeint.service.impl.yaciprovider.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigInteger; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder(toBuilder = true) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class Amt implements Serializable { + private String unit; + private String policyId; + private String assetName; + private BigInteger quantity; +} diff --git a/src/main/java/com/bloxbean/intelliada/idea/nodeint/util/NetworkHelper.java b/src/main/java/com/bloxbean/intelliada/idea/nodeint/util/NetworkHelper.java index 5d4adca..4b49976 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/nodeint/util/NetworkHelper.java +++ b/src/main/java/com/bloxbean/intelliada/idea/nodeint/util/NetworkHelper.java @@ -1,6 +1,7 @@ package com.bloxbean.intelliada.idea.nodeint.util; import com.bloxbean.intelliada.idea.core.util.Networks; +import com.bloxbean.intelliada.idea.core.util.NodeType; import com.intellij.openapi.util.text.StringUtil; import java.util.HashMap; @@ -12,10 +13,10 @@ public class NetworkHelper { private NetworkHelper() { networkTOExplorerUrlMap = new HashMap<>(); - networkTOExplorerUrlMap.put(Networks.mainnet().getName(), "https://cexplorer.io"); - networkTOExplorerUrlMap.put(Networks.testnet().getName(), "https://testnet.cexplorer.io"); - networkTOExplorerUrlMap.put(Networks.prepod().getName(), "https://preprod.cexplorer.io"); - networkTOExplorerUrlMap.put(Networks.preview().getName(), "https://preview.cexplorer.io"); + networkTOExplorerUrlMap.put(Networks.mainnet().getName(), "https://cardanoscan.io"); + networkTOExplorerUrlMap.put(Networks.testnet().getName(), "https://testnet.cardanoscan.io"); + networkTOExplorerUrlMap.put(Networks.prepod().getName(), "https://preprod.cardanoscan.io"); + networkTOExplorerUrlMap.put(Networks.preview().getName(), "https://preview.cardanoscan.io"); } public static NetworkHelper getInstance() { @@ -32,10 +33,13 @@ public String getExplorerBaseUrl(String network) { return networkTOExplorerUrlMap.get(network); } - public String getTxnHashUrl(String network, String txnHash) { + public String getTxnHashUrl(NodeType nodeType, String network, String txnHash) { + if (NodeType.YaciDevKit.equals(nodeType)) { + return "http://localhost:5173/transactions/" + txnHash; + } + String url = getExplorerBaseUrl(network); if (StringUtil.isEmpty(url)) return null; - return url + "/tx/" + txnHash; } diff --git a/src/main/java/com/bloxbean/intelliada/idea/toolwindow/ui/CardanoExplorer.java b/src/main/java/com/bloxbean/intelliada/idea/toolwindow/ui/CardanoExplorer.java index 236537a..02eec94 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/toolwindow/ui/CardanoExplorer.java +++ b/src/main/java/com/bloxbean/intelliada/idea/toolwindow/ui/CardanoExplorer.java @@ -191,6 +191,7 @@ private JPanel createToolbarPanel() { group.add(scriptGroup); final ActionToolbar actionToolBar = ActionManager.getInstance().createActionToolbar(ActionPlaces.ANT_EXPLORER_TOOLBAR, group, true); + actionToolBar.setTargetComponent(this); return JBUI.Panels.simplePanel(actionToolBar.getComponent()); } diff --git a/src/main/java/com/bloxbean/intelliada/idea/transaction/ui/TransactionEntryForm.java b/src/main/java/com/bloxbean/intelliada/idea/transaction/ui/TransactionEntryForm.java index d3fdc19..e602811 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/transaction/ui/TransactionEntryForm.java +++ b/src/main/java/com/bloxbean/intelliada/idea/transaction/ui/TransactionEntryForm.java @@ -10,11 +10,11 @@ import com.bloxbean.intelliada.idea.core.util.NetworkUtil; import com.bloxbean.intelliada.idea.core.util.Networks; import com.bloxbean.intelliada.idea.nodeint.CardanoNodeConfigurationHelper; +import com.bloxbean.intelliada.idea.nodeint.service.CardanoServiceFactory; import com.bloxbean.intelliada.idea.nodeint.service.api.CardanoAccountService; import com.bloxbean.intelliada.idea.nodeint.service.api.LogListenerAdapter; import com.bloxbean.intelliada.idea.nodeint.service.api.TransactionService; import com.bloxbean.intelliada.idea.nodeint.service.api.model.AssetBalance; -import com.bloxbean.intelliada.idea.nodeint.service.impl.AccountServiceImpl; import com.bloxbean.intelliada.idea.nodeint.service.impl.TransactionServiceImpl; import com.bloxbean.intelliada.idea.toolwindow.CardanoConsole; import com.bloxbean.intelliada.idea.transaction.TransactionEntryListener; @@ -171,7 +171,7 @@ public void run() { return; try { - CardanoAccountService accountService = new AccountServiceImpl(project, new LogListenerAdapter(console)); + CardanoAccountService accountService = CardanoServiceFactory.getAccountService(project, new LogListenerAdapter(console)); List assetBalanceList = accountService.getBalance(senderTf.getText()); availableBalanceComboBoxModel.removeAllElements(); diff --git a/src/main/java/com/bloxbean/intelliada/idea/utxos/ui/ListUtxosDialog.java b/src/main/java/com/bloxbean/intelliada/idea/utxos/ui/ListUtxosDialog.java index d2d9563..066304e 100644 --- a/src/main/java/com/bloxbean/intelliada/idea/utxos/ui/ListUtxosDialog.java +++ b/src/main/java/com/bloxbean/intelliada/idea/utxos/ui/ListUtxosDialog.java @@ -3,9 +3,9 @@ import com.bloxbean.cardano.client.api.common.OrderEnum; import com.bloxbean.cardano.client.api.model.Utxo; import com.bloxbean.intelliada.idea.nodeint.exception.TargetNodeNotConfigured; +import com.bloxbean.intelliada.idea.nodeint.service.CardanoServiceFactory; import com.bloxbean.intelliada.idea.nodeint.service.api.CardanoAccountService; import com.bloxbean.intelliada.idea.nodeint.service.api.LogListenerAdapter; -import com.bloxbean.intelliada.idea.nodeint.service.impl.AccountServiceImpl; import com.bloxbean.intelliada.idea.toolwindow.CardanoConsole; import com.bloxbean.intelliada.idea.util.IdeaUtil; import com.bloxbean.intelliada.idea.util.JsonUtil; @@ -180,7 +180,7 @@ public void run() { CardanoAccountService cardanoAccountService = null; try { - cardanoAccountService = new AccountServiceImpl(project, new LogListenerAdapter(console)); + cardanoAccountService = CardanoServiceFactory.getAccountService(project, new LogListenerAdapter(console)); } catch (TargetNodeNotConfigured targetNodeNotConfigured) { console.showErrorMessage(targetNodeNotConfigured.getMessage()); IdeaUtil.showNotification(project, "Node Configuration", diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 8ae1e99..7a40267 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -10,8 +10,7 @@

Documentation ]]> - - + @@ -68,54 +67,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -128,10 +83,10 @@ implementationClass="com.bloxbean.intelliada.idea.aiken.comment.AikenCommenter"/> + implementationClass="com.bloxbean.intelliada.idea.aiken.folding.AikenFolderingBuilder"/> + implementationClass="com.bloxbean.intelliada.idea.aiken.folding.AikenPairedBraceMatcher"/> @@ -147,11 +102,8 @@ - - - - - - - - - - - - - - - - - - + diff --git a/src/test/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciAccountServiceImplTest.java b/src/test/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciAccountServiceImplTest.java new file mode 100644 index 0000000..fa2824a --- /dev/null +++ b/src/test/java/com/bloxbean/intelliada/idea/nodeint/service/impl/yaciprovider/YaciAccountServiceImplTest.java @@ -0,0 +1,52 @@ +package com.bloxbean.intelliada.idea.nodeint.service.impl.yaciprovider; + +import com.bloxbean.intelliada.idea.configuration.model.RemoteNode; +import com.bloxbean.intelliada.idea.nodeint.exception.ApiCallException; +import com.bloxbean.intelliada.idea.nodeint.exception.TargetNodeNotConfigured; +import com.bloxbean.intelliada.idea.nodeint.service.api.LogListener; +import com.bloxbean.intelliada.idea.nodeint.service.api.model.AssetBalance; +import junit.framework.TestCase; + +import java.util.List; + +public class YaciAccountServiceImplTest extends TestCase { + + public static final String BASE_URL = "http://localhost:8080/api/v1/"; + + public void testGetAdaBalance() throws TargetNodeNotConfigured { + var remoteNode = new RemoteNode(); + remoteNode.setApiEndpoint(BASE_URL); + YaciAccountServiceImpl yaciAccountServiceImpl = new YaciAccountServiceImpl(remoteNode, new DummyLogListener()); + + var result = yaciAccountServiceImpl.getAdaBalance("addr_test1qrh3nrahcd0pj6ps3g9htnlw2jjxuylgdhfn2s5rxqyrr43yzewr2766qsfeq6stl65t546cwvclpqm2rpkkxtksgxuq90xn5f"); + System.out.println(result); + assertTrue(result.isSuccessful()); + } + + public void testAssets() throws TargetNodeNotConfigured, ApiCallException { + var remoteNode = new RemoteNode(); + remoteNode.setApiEndpoint("http://localhost:8080/api/v1/"); + YaciAccountServiceImpl yaciAccountServiceImpl = new YaciAccountServiceImpl(remoteNode, new DummyLogListener()); + + List assetBalances = yaciAccountServiceImpl.getBalance("addr_test1qrh3nrahcd0pj6ps3g9htnlw2jjxuylgdhfn2s5rxqyrr43yzewr2766qsfeq6stl65t546cwvclpqm2rpkkxtksgxuq90xn5f"); + System.out.println(assetBalances); + } + + class DummyLogListener implements LogListener { + + @Override + public void info(String msg) { + + } + + @Override + public void error(String msg) { + + } + + @Override + public void warn(String msg) { + + } + } +}