Initial import of openwhisk-intellij-plugin donation from NAVER (#5)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..312b385
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+gradle.properties
+.idea
+.gradle
+build
+out
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..809ba07
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,203 @@
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2020-present NAVER Corp.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f59a67a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,53 @@
+# OpenWhisk Intellij Plugin
+**OpenWhisk-intellij-support** is an open source Intellij Plugin for [Apache OpenWhisk](https://github.com/apache/openwhisk). It assists users to develop/deploy/manage OpenWhisk functions in Intellij.
+
+## Prerequisites
+Install the dependencies below to use full features:
+* Intellij >= 2018.1.8
+* [wskdeploy](https://github.com/apache/openwhisk-wskdeploy/releases)
+
+This extension finds the `.wskprops` configuration file located in the home path and connects to the Openwhisk server automatically. Set up your configuration referred to the [cli docs](https://github.com/apache/openwhisk/blob/master/docs/cli.md#openwhisk-cli).
+
+## Feature
+### OpenWhisk Explorer
+* Explore all entities in your endpoints/namespaces.
+ * The `.wskprops` file is automatically registered.
+ * You can add the API host manually.
+ * You can add namespace manually by API auth key.
+* Show the action code with syntax highlighting.
+* [Soon] Edit the action code on the remote server.
+* Invoke the action remotely and get the activation result.
+* Show a list of actions related to the sequence action.
+* Show information about the trigger and related rules.
+* Show activations related to the action (Same as `wsk activation list <action>`).
+* Show detailed information of the activation (Same `as wsk activation get <activation_id>`).
+* Update parameters of the action, package, and trigger.
+
+### Manifest View
+* List up manifest YAML files in the workspace.
+* Deploy/Undeploy OpenWhisk packages with manifest (via wskdeploy).
+ * Deploy with the deployment file.
+ * Deploy with multiple credentials.
+
+## How to debug in your local
+```bash
+./gradlew runIde
+```
+
+## License
+
+```
+Copyright 2020-present NAVER Corp.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+```
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..c823c69
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,61 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'java'
+ id 'org.jetbrains.intellij' version '0.4.18'
+ id 'checkstyle'
+}
+
+group 'com.navercorp.openwhisk.intellij'
+version '1.1.4'
+
+sourceCompatibility = 11
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8'
+ testImplementation group: 'junit', name: 'junit', version: '4.12'
+}
+
+intellij {
+ version '2020.1.2'
+}
+
+patchPluginXml {
+ sinceBuild '181.*'
+ untilBuild '203.*'
+}
+
+publishPlugin {
+ token = System.getenv("ORG_GRADLE_PROJECT_intellijPublishToken")
+ channels 'beta'
+}
+
+checkstyle {
+ configFile = file("${rootDir}/config/checkstyle/checkstyle.xml")
+ toolVersion = 8.35
+}
+
+tasks.withType(Checkstyle) {
+ reports {
+ xml.enabled false
+ html.enabled true
+ }
+}
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000..51b20b3
--- /dev/null
+++ b/config/checkstyle/checkstyle.xml
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+ "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
+ "https://checkstyle.org/dtds/configuration_1_3.dtd">
+
+<!--
+ Checkstyle configuration that checks the sun coding conventions from:
+ - the Java Language Specification at
+ https://docs.oracle.com/javase/specs/jls/se11/html/index.html
+ - the Sun Code Conventions at https://www.oracle.com/java/technologies/javase/codeconventions-contents.html
+ - the Javadoc guidelines at
+ https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html
+ - the JDK Api documentation https://docs.oracle.com/en/java/javase/11/
+ - some best practices
+ Checkstyle is very configurable. Be sure to read the documentation at
+ https://checkstyle.org (or in your downloaded distribution).
+ Most Checks are configurable, be sure to consult the documentation.
+ To completely disable a check, just comment it out or delete it from the file.
+ To suppress certain violations please review suppression filters.
+ Finally, it is worth reading the documentation.
+-->
+
+<module name="Checker">
+ <!--
+ If you set the basedir property below, then all reported file
+ names will be relative to the specified directory. See
+ https://checkstyle.org/config.html#Checker
+ <property name="basedir" value="${basedir}"/>
+ -->
+ <property name="severity" value="error"/>
+
+ <property name="fileExtensions" value="java, properties, xml"/>
+
+ <!-- Excludes all 'module-info.java' files -->
+ <!-- See https://checkstyle.org/config_filefilters.html -->
+ <module name="BeforeExecutionExclusionFileFilter">
+ <property name="fileNamePattern" value="module\-info\.java$"/>
+ </module>
+
+ <!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
+ <module name="SuppressionFilter">
+ <property name="file" value="${org.checkstyle.sun.suppressionfilter.config}"
+ default="checkstyle-suppressions.xml" />
+ <property name="optional" value="true"/>
+ </module>
+
+ <!-- Checks that a package-info.java file exists for each package. -->
+ <!-- See https://checkstyle.org/config_javadoc.html#JavadocPackage -->
+ <!--<module name="JavadocPackage"/>-->
+
+ <!-- Checks whether files end with a new line. -->
+ <!-- See https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
+ <module name="NewlineAtEndOfFile"/>
+
+ <!-- Checks that property files contain the same keys. -->
+ <!-- See https://checkstyle.org/config_misc.html#Translation -->
+ <module name="Translation"/>
+
+ <!-- Checks for Size Violations. -->
+ <!-- See https://checkstyle.org/config_sizes.html -->
+ <module name="FileLength"/>
+ <module name="LineLength">
+ <property name="fileExtensions" value="java"/>
+ <property name="max" value="160"/>
+ </module>
+
+ <!-- Checks for whitespace -->
+ <!-- See https://checkstyle.org/config_whitespace.html -->
+ <module name="FileTabCharacter"/>
+
+ <!-- Miscellaneous other checks. -->
+ <!-- See https://checkstyle.org/config_misc.html -->
+ <module name="RegexpSingleline">
+ <property name="format" value="\s+$"/>
+ <property name="minimum" value="0"/>
+ <property name="maximum" value="0"/>
+ <property name="message" value="Line has trailing spaces."/>
+ </module>
+
+ <!-- Checks for Headers -->
+ <!-- See https://checkstyle.org/config_header.html -->
+ <!-- <module name="Header"> -->
+ <!-- <property name="headerFile" value="${checkstyle.header.file}"/> -->
+ <!-- <property name="fileExtensions" value="java"/> -->
+ <!-- </module> -->
+
+ <module name="TreeWalker">
+
+ <!-- Checks for Javadoc comments. -->
+ <!-- See https://checkstyle.org/config_javadoc.html -->
+ <!--<module name="InvalidJavadocPosition"/>-->
+ <!--<module name="JavadocMethod"/>-->
+ <module name="JavadocType"/>
+ <!--<module name="JavadocVariable"/>-->
+ <module name="JavadocStyle"/>
+ <!--<module name="MissingJavadocMethod"/>-->
+
+ <!-- Checks for Naming Conventions. -->
+ <!-- See https://checkstyle.org/config_naming.html -->
+ <module name="ConstantName"/>
+ <module name="LocalFinalVariableName"/>
+ <module name="LocalVariableName"/>
+ <module name="MemberName"/>
+ <module name="MethodName"/>
+ <module name="PackageName"/>
+ <module name="ParameterName"/>
+ <module name="StaticVariableName"/>
+ <module name="TypeName"/>
+
+ <!-- Checks for imports -->
+ <!-- See https://checkstyle.org/config_imports.html -->
+ <module name="AvoidStarImport">
+ <property name="allowClassImports" value="true"/>
+ <property name="allowStaticMemberImports" value="true"/>
+ </module>
+ <module name="IllegalImport"/> <!-- defaults to sun.* packages -->
+ <module name="RedundantImport"/>
+ <module name="UnusedImports">
+ <property name="processJavadoc" value="false"/>
+ </module>
+
+ <!-- Checks for Size Violations. -->
+ <!-- See https://checkstyle.org/config_sizes.html -->
+ <module name="MethodLength">
+ <property name="max" value="300"/>
+ </module>
+ <module name="ParameterNumber">
+ <property name="max" value="20"/>
+ </module>
+
+ <!-- Checks for whitespace -->
+ <!-- See https://checkstyle.org/config_whitespace.html -->
+ <module name="EmptyForIteratorPad"/>
+ <module name="GenericWhitespace"/>
+ <module name="MethodParamPad"/>
+ <module name="NoWhitespaceAfter"/>
+ <module name="NoWhitespaceBefore"/>
+ <module name="OperatorWrap"/>
+ <module name="ParenPad"/>
+ <module name="TypecastParenPad"/>
+ <module name="WhitespaceAfter"/>
+ <module name="WhitespaceAround"/>
+
+ <!-- Modifier Checks -->
+ <!-- See https://checkstyle.org/config_modifiers.html -->
+ <module name="ModifierOrder"/>
+ <module name="RedundantModifier"/>
+
+ <!-- Checks for blocks. You know, those {}'s -->
+ <!-- See https://checkstyle.org/config_blocks.html -->
+ <module name="AvoidNestedBlocks"/>
+ <module name="EmptyBlock"/>
+ <module name="LeftCurly"/>
+ <module name="NeedBraces">
+ <property name="allowSingleLineStatement" value="true"/>
+ </module>
+ <module name="RightCurly"/>
+
+ <!-- Checks for common coding problems -->
+ <!-- See https://checkstyle.org/config_coding.html -->
+ <module name="EmptyStatement"/>
+ <module name="EqualsHashCode"/>
+ <module name="HiddenField">
+ <property name="ignoreConstructorParameter" value="true"/>
+ <property name="ignoreSetter" value="true"/>
+ </module>
+ <module name="IllegalInstantiation"/>
+ <module name="InnerAssignment"/>
+ <!--<module name="MagicNumber"/>-->
+ <module name="MissingSwitchDefault"/>
+ <module name="MultipleVariableDeclarations"/>
+ <module name="SimplifyBooleanExpression"/>
+ <module name="SimplifyBooleanReturn"/>
+
+ <!-- Checks for class design -->
+ <!-- See https://checkstyle.org/config_design.html -->
+ <!--<module name="DesignForExtension"/>-->
+ <!--<module name="FinalClass"/>-->
+ <module name="HideUtilityClassConstructor"/>
+ <module name="InterfaceIsType"/>
+ <module name="VisibilityModifier">
+ <property name="protectedAllowed" value="true"/>
+ <property name="allowPublicFinalFields" value="true"/>
+ </module>
+
+ <!-- Miscellaneous other checks. -->
+ <!-- See https://checkstyle.org/config_misc.html -->
+ <module name="ArrayTypeStyle"/>
+ <!--<module name="FinalParameters">-->
+ <module name="TodoComment"/>
+ <module name="UpperEll"/>
+
+ <!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
+ <module name="SuppressionXpathFilter">
+ <property name="file" value="${org.checkstyle.sun.suppressionxpathfilter.config}"
+ default="checkstyle-xpath-suppressions.xml" />
+ <property name="optional" value="true"/>
+ </module>
+
+ </module>
+
+</module>
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..28861d2
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0517884
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,19 @@
+# Copyright 2020-present NAVER Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..d9508bb
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,186 @@
+#!/usr/bin/env sh
+
+# Copyright 2020-present NAVER Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..d88ae3b
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,98 @@
+@rem Copyright 2020-present NAVER Corp.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem http://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..7ff0013
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,18 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+rootProject.name = 'intellij-plugin'
+
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/DialogWrapperWithApply.java b/src/main/java/com/navercorp/openwhisk/intellij/common/DialogWrapperWithApply.java
new file mode 100644
index 0000000..7d56d5a
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/DialogWrapperWithApply.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+public abstract class DialogWrapperWithApply extends DialogWrapper {
+
+ protected ApplyAction myApplyAction;
+
+ protected DialogWrapperWithApply(@Nullable Project project, boolean canBeParent) {
+ super(project, canBeParent);
+ myApplyAction = new DialogWrapperWithApply.ApplyAction("Apply");
+ }
+
+ /**
+ * This method is invoked by default implementation of "Apply" action.
+ * This is convenient place to override functionality of "Apply" action.
+ */
+ protected void doApplyAction() {
+ // do noting
+ }
+
+ @NotNull
+ @Override
+ protected Action[] createActions() {
+ return new Action[]{
+ myCancelAction, myOKAction, myApplyAction
+ };
+ }
+
+ protected class ApplyAction extends DialogWrapperAction {
+
+ private boolean isApplied;
+
+ protected ApplyAction(@NotNull String name) {
+ super(name);
+ isApplied = false;
+ }
+
+ @Override
+ protected void doAction(ActionEvent e) {
+ doApplyAction();
+ isApplied = true;
+ }
+
+ public boolean isApplied() {
+ return isApplied;
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/Icons.java b/src/main/java/com/navercorp/openwhisk/intellij/common/Icons.java
new file mode 100644
index 0000000..eaa869e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/Icons.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.util.IconLoader;
+import gnu.trove.THashMap;
+
+import javax.swing.*;
+import java.util.Map;
+
+public class Icons {
+
+ protected Icons() {
+ throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+ }
+
+ private static final Map<String, Icon> REGISTERED_ICONS = new THashMap<>();
+
+ public static final Icon ENTITY_TRIGGER = load("/icons/event.svg");
+ public static final Icon ENTITY_TRIGGER_ROOT = load("/icons/eventGroup.svg");
+
+ public static final Icon ARROW_UP = load("/icons/arrowUp.svg");
+ public static final Icon ARROW_DOWN = load("/icons/arrowDown.svg");
+
+ public static final Icon OPEN_DISK_HOVER = load("/icons/openDiskHover.svg");
+ public static final Icon RUN_ANYTHING = load("/icons/run_anything.svg");
+ public static final Icon YAML = load("/icons/yaml.svg");
+
+ public static final Icon KIND_JAVA = load("/icons/language/java/java.svg");
+ public static final Icon KIND_JS = load("/icons/language/javascript/javascript.svg");
+ public static final Icon KIND_GO = load("/icons/language/go/go.svg");
+ public static final Icon KIND_PYTHON = load("/icons/language/python/py.svg");
+ public static final Icon KIND_PHP = load("/icons/language/php/php.svg");
+ public static final Icon KIND_RUBY = load("/icons/language/ruby/rb.svg");
+ public static final Icon KIND_SWIFT = load("/icons/language/swift/sw.svg");
+ public static final Icon KIND_SEQUENCE = load("/icons/seq.svg");
+ public static final Icon KIND_DOCKER = load("/icons/language/docker/DockerCompose.svg");
+
+ private static Icon load(String path) {
+ try {
+ return IconLoader.getIcon(path);
+ } catch (Throwable t) {
+ return AllIcons.General.Warning;
+ }
+ }
+
+ private static Icon load(String key, String path) {
+ Icon icon = load(path);
+ REGISTERED_ICONS.put(key, icon);
+ return icon;
+ }
+
+ public static Icon getIcon(String key) {
+ return REGISTERED_ICONS.get(key);
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/error/NotExistFileException.java b/src/main/java/com/navercorp/openwhisk/intellij/common/error/NotExistFileException.java
new file mode 100644
index 0000000..04c94f4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/error/NotExistFileException.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.error;
+
+public class NotExistFileException extends RuntimeException {
+
+ public NotExistFileException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/notification/SimpleNotifier.java b/src/main/java/com/navercorp/openwhisk/intellij/common/notification/SimpleNotifier.java
new file mode 100644
index 0000000..a0cc5e4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/notification/SimpleNotifier.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.notification;
+
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationDisplayType;
+import com.intellij.notification.NotificationGroup;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+public class SimpleNotifier {
+
+ private SimpleNotifier() {
+ }
+
+ private static class LazyHolder {
+ private static final SimpleNotifier INSTANCE = new SimpleNotifier();
+ }
+
+ public static SimpleNotifier getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ private static final NotificationGroup NOTIFICATION_GROUP =
+ new NotificationGroup("Simple Notification Group", NotificationDisplayType.STICKY_BALLOON, true);
+
+ public Notification notify(@NotNull String content, @NotNull final NotificationType type) {
+ return notify(null, content, type);
+ }
+
+ public Notification notify(Project project, @NotNull String content, @NotNull final NotificationType type) {
+ final Notification notification = NOTIFICATION_GROUP.createNotification(content, type);
+ notification.notify(project);
+ return notification;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/service/WhiskService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/service/WhiskService.java
new file mode 100644
index 0000000..cd58f06
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/service/WhiskService.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.service;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+@State(
+ name = "whisk",
+ storages = {
+ @Storage("whisk.xml")
+ }
+)
+public class WhiskService implements PersistentStateComponent<WhiskService> {
+ private static final Logger LOG = Logger.getInstance(WhiskService.class);
+
+ private String endpoints;
+
+ @Nullable
+ @Override
+ public WhiskService getState() {
+ return this;
+ }
+
+ @Override
+ public void loadState(@NotNull WhiskService state) {
+ LOG.info("Save state");
+ XmlSerializerUtil.copyBean(state, this);
+ }
+
+ public String getEndpoints() {
+ return endpoints;
+ }
+
+ public void setEndpoints(String endpoints) {
+ this.endpoints = endpoints;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/service/WskDeployService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/service/WskDeployService.java
new file mode 100644
index 0000000..9627678
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/service/WskDeployService.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.service;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+@State(
+ name = "wskdeploy",
+ storages = {
+ @Storage("wskdeploy.xml")
+ }
+)
+public class WskDeployService implements PersistentStateComponent<WskDeployService> {
+ private static final Logger LOG = Logger.getInstance(WskDeployService.class);
+
+ private String wskdeployPath;
+ private String wskdeployName;
+
+ @Nullable
+ @Override
+ public WskDeployService getState() {
+ return this;
+ }
+
+ @Override
+ public void loadState(@NotNull WskDeployService state) {
+ LOG.info("Save wskdeploy");
+ XmlSerializerUtil.copyBean(state, this);
+ }
+
+ public String getWskdeployPath() {
+ return wskdeployPath;
+ }
+
+ public void setWskdeployPath(String wskdeployPath) {
+ this.wskdeployPath = wskdeployPath;
+ }
+
+ public String getWskdeployName() {
+ return wskdeployName;
+ }
+
+ public void setWskdeployName(String wskdeployName) {
+ this.wskdeployName = wskdeployName;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/CommandUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/CommandUtils.java
new file mode 100644
index 0000000..4f35ec5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/CommandUtils.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.WskDeployCmdResponse;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class CommandUtils {
+
+ private static final Logger LOG = Logger.getInstance(CommandUtils.class);
+
+ protected CommandUtils() {
+ throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+ }
+
+ public static WskDeployCmdResponse runCommand(String[] command)
+ throws IOException, InterruptedException {
+ ProcessBuilder builder = new ProcessBuilder(command);
+ Process process = builder.start();
+
+ // read std output
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(process.getInputStream()));
+ StringBuilder output = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ output.append(line + "\n");
+ }
+
+ // read error output
+ BufferedReader errReader =
+ new BufferedReader(new InputStreamReader(process.getErrorStream()));
+ StringBuilder errOutput = new StringBuilder();
+ String errLine;
+ while ((errLine = errReader.readLine()) != null) {
+ errOutput.append(errLine + "\n");
+ }
+
+ int exitCode = process.waitFor();
+ return new WskDeployCmdResponse(exitCode, output.toString(), errOutput.toString());
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/EventUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/EventUtils.java
new file mode 100644
index 0000000..df936da
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/EventUtils.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.messages.MessageBus;
+import com.intellij.util.messages.MessageBusConnection;
+import com.intellij.util.messages.Topic;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class EventUtils {
+
+ protected EventUtils() {
+ throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+ }
+
+ @NotNull
+ private static MessageBusConnection connect(@NotNull Project project, @Nullable Disposable parentDisposable) {
+ MessageBus messageBus = project.getMessageBus();
+ return parentDisposable == null ? messageBus.connect(project) : messageBus.connect(parentDisposable);
+ }
+
+ public static <T> void subscribe(Project project, @Nullable Disposable parentDisposable, Topic<T> topic, T handler) {
+ MessageBusConnection messageBusConnection = connect(project, parentDisposable);
+ messageBusConnection.subscribe(topic, handler);
+
+ }
+
+ public static <T> void publish(@Nullable Project project, Topic<T> topic, ParametricRunnable.Basic<T> callback) {
+ if (project != null) {
+ try {
+ MessageBus messageBus = project.getMessageBus();
+ T publisher = messageBus.syncPublisher(topic);
+ callback.run(publisher);
+ } catch (ProcessCanceledException ignore) {
+ }
+ }
+
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/FileUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/FileUtils.java
new file mode 100644
index 0000000..e396c7d
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/FileUtils.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Optional;
+
+import static com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils.writeWhiskActivationToJson;
+
+public class FileUtils {
+
+ private static final Logger LOG = Logger.getInstance(FileUtils.class);
+
+ protected FileUtils() {
+ throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+ }
+
+ /**
+ * It accumulates in the .gradle/caches subfolder and files are not automatically cleared unless you delete the cache directory.
+ * TODO Delete the cache file globally.
+ * TODO Use LightVirtualFile instead of local file.
+ */
+ public static VirtualFile writeActionToFile(String basePath, ExecutableWhiskAction action) throws IOException {
+ File dir = new File(basePath);
+ if (!dir.exists()) {
+ dir.mkdir();
+ }
+
+ final String path = basePath + "/" + action.getName() + action.getKindExtension();
+ File f = new File(path);
+ LOG.info("Open action file: " + f.getAbsolutePath());
+
+ FileWriter fw = new FileWriter(f);
+
+ if (action.getExec().isBinary()) {
+ fw.write("This action was created as a zip file, and you can't see the code here.");
+ } else {
+ String code = Optional.ofNullable(action.getExec().getCode()).orElse("This action is empty");
+ fw.write(code);
+ }
+ fw.close();
+
+ return VfsUtil.findFileByIoFile(f, true);
+ }
+
+ /**
+ * It accumulates in the .gradle/caches subfolder and files are not automatically cleared unless you delete the cache directory.
+ * TODO Delete the cache file globally.
+ * TODO Use LightVirtualFile instead of local file.
+ */
+ public static VirtualFile writeActivationToFile(String basePath, WhiskActivationWithLogs activation) throws IOException {
+ File dir = new File(basePath);
+ if (!dir.exists()) {
+ dir.mkdir();
+ }
+
+ File f = new File(basePath + "/" + activation.getName() + "-" + activation.getActivationId() + ".json");
+ LOG.info("Open activation file: " + f.getAbsolutePath());
+
+ FileWriter fw = new FileWriter(f);
+ fw.write(writeWhiskActivationToJson(activation));
+ fw.close();
+
+ return VfsUtil.findFileByIoFile(f, true);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/JsonParserUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/JsonParserUtils.java
new file mode 100644
index 0000000..656482c
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/JsonParserUtils.java
@@ -0,0 +1,214 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import org.apache.commons.lang.StringUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.CodeExec;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.CodeExecSerializer;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskRule;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+
+import java.io.IOException;
+import java.util.*;
+
+public class JsonParserUtils {
+ private static ObjectMapper mapper = new ObjectMapper();
+ private static SimpleModule simpleModule = new SimpleModule();
+
+ protected JsonParserUtils() {
+ throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+ }
+
+ static {
+ simpleModule.addSerializer(CodeExec.class, new CodeExecSerializer());
+ mapper.registerModule(simpleModule);
+ }
+
+ public static List<WhiskActionMetaData> parseWhiskActions(String actions) throws IOException {
+ if (StringUtils.isNotEmpty(actions)) {
+ return Arrays.asList(mapper.readValue(actions, WhiskActionMetaData[].class));
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+ public static Optional<ExecutableWhiskAction> parseWhiskAction(String action) throws IOException {
+ if (StringUtils.isNotEmpty(action)) {
+ return Optional.of(mapper.readValue(action, ExecutableWhiskAction.class));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public static List<WhiskPackage> parseWhiskPackages(String packages) throws IOException {
+ if (StringUtils.isNotEmpty(packages)) {
+ return Arrays.asList(mapper.readValue(packages, WhiskPackage[].class));
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+ public static Optional<WhiskPackageWithActions> parseWhiskPackage(String pkg) throws IOException {
+ if (StringUtils.isNotEmpty(pkg)) {
+ return Optional.of(mapper.readValue(pkg, WhiskPackageWithActions.class));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ // TODO test
+ public static List<WhiskEndpoint> parseWhiskEndpoints(String endpoints) throws IOException {
+ if (StringUtils.isNotEmpty(endpoints)) {
+ return Arrays.asList(mapper.readValue(endpoints, WhiskEndpoint[].class));
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+ // TODO test
+ public static String writeEndpointsToJson(List<WhiskEndpoint> ep) throws JsonProcessingException {
+ return mapper.writeValueAsString(ep);
+ }
+
+ // TODO test
+ public static String[] parseWhiskNamespace(String namespaces) throws IOException {
+ if (StringUtils.isNotEmpty(namespaces)) {
+ return mapper.readValue(namespaces, String[].class);
+ } else {
+ return new String[]{};
+ }
+ }
+
+ // TODO test
+ public static List<WhiskActivationMetaData> parseWhiskActivations(String actions) throws IOException {
+ if (StringUtils.isNotEmpty(actions)) {
+ return Arrays.asList(mapper.readValue(actions, WhiskActivationMetaData[].class));
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+ // TODO test
+ public static Optional<WhiskActivationWithLogs> parseWhiskActivation(String actions) throws IOException {
+ if (StringUtils.isNotEmpty(actions)) {
+ return Optional.of(mapper.readValue(actions, WhiskActivationWithLogs.class));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ // TODO test
+ public static String writeWhiskActivationToJson(WhiskActivationWithLogs activation) throws JsonProcessingException {
+ return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(activation);
+ }
+
+ public static List<WhiskTriggerMetaData> parseWhiskTriggers(String triggers) throws IOException {
+ if (StringUtils.isNotEmpty(triggers)) {
+ return Arrays.asList(mapper.readValue(triggers, WhiskTriggerMetaData[].class));
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+ public static Optional<ExecutableWhiskTrigger> parseWhiskTrigger(String triggers) throws IOException {
+ if (StringUtils.isNotEmpty(triggers)) {
+ return Optional.of(mapper.readValue(triggers, ExecutableWhiskTrigger.class));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ // TODO test
+ public static Optional<WhiskRule> parseWhiskRule(String rule) throws IOException {
+ if (StringUtils.isNotEmpty(rule)) {
+ return Optional.of(mapper.readValue(rule, WhiskRule.class));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ // TODO test
+ public static String writeParameterToJson(List<Map<String, Object>> params) throws JsonProcessingException {
+ return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(ParameterUtils.listMapToMap(params));
+ }
+
+ // TODO test
+ public static List<Map<String, Object>> parseListMap(String json) throws IOException {
+ if (StringUtils.isNotEmpty(json)) {
+ return mapper.readValue(json, new TypeReference<List<Map<String, Object>>>() {
+ });
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+ // TODO test
+ public static String writeListMapToJson(List<Map<String, Object>> maps) throws JsonProcessingException {
+ return mapper.writer().writeValueAsString(maps);
+ }
+
+ // TODO test
+ public static Map<String, Object> parseMap(String json) {
+ if (StringUtils.isNotEmpty(json)) {
+ try {
+ return mapper.readValue(json, Map.class);
+ } catch (IOException e) {
+ return new LinkedHashMap<>();
+ }
+ } else {
+ return new LinkedHashMap<>();
+ }
+ }
+
+ // TODO test
+ public static String writeMapToJson(Map<String, Object> map) throws JsonProcessingException {
+ return mapper.writeValueAsString(map);
+ }
+
+ // TODO test
+ public static boolean isValidJson(final String json) throws IOException {
+ boolean valid = true;
+ try {
+ mapper.readTree(json);
+ } catch (JsonProcessingException e) {
+ valid = false;
+ }
+ return valid;
+ }
+
+ // TODO test
+ public static String beautifyJson(String json) throws IOException {
+ if (StringUtils.isNotEmpty(json)) {
+ Map<String, Object> j = mapper.readValue(json, Map.class);
+ return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(j);
+ } else {
+ return "";
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParameterUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParameterUtils.java
new file mode 100644
index 0000000..c6de207
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParameterUtils.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.io.IOException;
+import java.util.*;
+
+public class ParameterUtils {
+
+ protected ParameterUtils() {
+ throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+ }
+
+ // TODO test
+ public static Map<String, Object> listMapToMap(List<Map<String, Object>> params) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ for (Map<String, Object> p : params) {
+ map.put((String) p.get("key"), p.get("value"));
+ }
+ return map;
+ }
+
+ public static List<Map<String, Object>> mapToListMap(Map<String, Object> params) {
+ List<Map<String, Object>> list = new ArrayList<>();
+ for (String key : params.keySet()) {
+ Object v = params.get(key);
+ Map<String, Object> m = new LinkedHashMap<>();
+ m.put("key", key);
+ m.put("value", v);
+ list.add(m);
+ }
+ return list;
+ }
+
+ public static Optional<String> validateParams(String params) {
+ if (StringUtils.isEmpty(params)) {
+ return Optional.of("{}");
+ } else {
+ try {
+ if (JsonParserUtils.isValidJson(params)) {
+ return Optional.of(params);
+ } else {
+ return Optional.empty();
+ }
+ } catch (IOException e) {
+ return Optional.empty();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParametricRunnable.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParametricRunnable.java
new file mode 100644
index 0000000..5964db8
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParametricRunnable.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+@FunctionalInterface
+public interface ParametricRunnable<P, E extends Throwable> {
+ void run(P parameter) throws E;
+
+ interface Basic<P> extends ParametricRunnable<P, RuntimeException> {
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ValidationUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ValidationUtils.java
new file mode 100644
index 0000000..e243226
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ValidationUtils.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.WskDeployCmdResponse;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import static com.navercorp.openwhisk.intellij.common.utils.CommandUtils.runCommand;
+
+public class ValidationUtils {
+ private static final Logger LOG = Logger.getInstance(ValidationUtils.class);
+
+ protected ValidationUtils() {
+ throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+ }
+
+ public static Optional<VirtualFile> validateWskDeploy(Optional<VirtualFile> wskdeploy) {
+ return wskdeploy.flatMap(wskdeployFile -> {
+ String[] cmd = new String[]{wskdeployFile.getPath(), "version"};
+ try {
+ WskDeployCmdResponse response = runCommand(cmd);
+ if (response.getExistCode() == 0) {
+ return Optional.of(wskdeployFile);
+ } else {
+ LOG.error(response.getErrorOutput());
+ return Optional.empty();
+ }
+ } catch (IOException | InterruptedException e) {
+ LOG.error(e);
+ return Optional.empty();
+ }
+ });
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/WhiskUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/WhiskUtils.java
new file mode 100644
index 0000000..d35c368
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/WhiskUtils.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskUtils {
+ private static final Logger LOG = Logger.getInstance(WhiskUtils.class);
+
+ protected WhiskUtils() {
+ throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+ }
+
+ public static Optional<WhiskAuth> findWhiskAuth(List<WhiskEndpoint> endpoints, WhiskNamespace namespace) {
+ for (WhiskEndpoint ep : endpoints) {
+ for (WhiskNamespace np : ep.getNamespaces()) {
+ if (np.getAuth().equals(namespace.getAuth()) && np.getPath().equals(namespace.getPath())) {
+ return Optional.of(new WhiskAuth(np.getAuth(), ep.getApihost()));
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ public static Optional<WhiskNamespace> findWhiskNamespace(List<WhiskEndpoint> endpoints, String auth) {
+ for (WhiskEndpoint ep : endpoints) {
+ for (WhiskNamespace np : ep.getNamespaces()) {
+ if (np.getAuth().equals(auth)) {
+ return Optional.of(np);
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ public static String getApihHostWithProtocol(String apiHost) {
+ if (!apiHost.toLowerCase().matches("^\\w+://.*")) {
+ apiHost = "https://" + apiHost;
+ }
+ return apiHost;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Binding.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Binding.java
new file mode 100644
index 0000000..3baef86
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Binding.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import java.util.Objects;
+
+public class Binding {
+
+ private String namespace;
+ private String name;
+
+ public Binding(String namespace, String name) {
+ this.namespace = namespace;
+ this.name = name;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Binding binding = (Binding) o;
+ return Objects.equals(namespace, binding.namespace)
+ && Objects.equals(name, binding.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(namespace, name);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Limits.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Limits.java
new file mode 100644
index 0000000..7f82578
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Limits.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+public class Limits {
+ private int concurrency;
+ private int logs;
+ private int memory;
+ private int timeout;
+
+ public Limits() {
+ }
+
+ public Limits(int concurrency, int logs, int memory, int timeout) {
+ this.concurrency = concurrency;
+ this.logs = logs;
+ this.memory = memory;
+ this.timeout = timeout;
+ }
+
+ public int getConcurrency() {
+ return concurrency;
+ }
+
+ public void setConcurrency(int concurrency) {
+ this.concurrency = concurrency;
+ }
+
+ public int getLogs() {
+ return logs;
+ }
+
+ public void setLogs(int logs) {
+ this.logs = logs;
+ }
+
+ public int getMemory() {
+ return memory;
+ }
+
+ public void setMemory(int memory) {
+ this.memory = memory;
+ }
+
+ public int getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Limits limits = (Limits) o;
+
+ if (concurrency != limits.concurrency) return false;
+ if (logs != limits.logs) return false;
+ if (memory != limits.memory) return false;
+ return timeout == limits.timeout;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = concurrency;
+ result = 31 * result + logs;
+ result = 31 * result + memory;
+ result = 31 * result + timeout;
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Runtime.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Runtime.java
new file mode 100644
index 0000000..8de09df
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Runtime.java
@@ -0,0 +1,188 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+/**
+ * Set the type of runtime based on upstream
+ * - https://github.com/apache/openwhisk/blob/master/ansible/files/runtimes.json
+ *
+ * The runtime used for each endpoint may be different, so use it according to the policy of the endpoint users are using.
+ */
+public enum Runtime {
+ NODE_6("nodejs", ".js", "6", false, true),
+ NODE_8("nodejs", ".js", "8", false, true),
+ NODE_10("nodejs", ".js", "10", true, false),
+ NODE_12("nodejs", ".js", "12", false, false),
+ NODE_14("nodejs", ".js", "14", false, false),
+ PYTHON_2("python", ".py", "2", false, false),
+ PYTHON_3("python", ".py", "3", true, false),
+ JAVA("java", ".java", "8", true, false),
+ SWIFT_3_1_1("swift", ".swift", "3.1.1", false, true),
+ SWIFT_4_2("swift", ".swift", "4.2", true, false),
+ SWIFT_5_1("swift", ".swift", "5.1", false, false),
+ PHP_7_3("php", ".php", "7.3", false, false),
+ PHP_7_4("php", ".php", "7.4", true, false),
+ GO_1_11("go", ".go", "1.11", true, false),
+ RUBY_2_5("ruby", ".rb", "2.5", true, false),
+ SEQUENCE("sequence", "", "", true, false),
+ DOCKER("blackbox", "", "", true, false);
+
+ private String name;
+ private String extension;
+ private String version;
+ private boolean defaultRuntime;
+ private boolean deprecated;
+
+ Runtime(String name, String extension, String version, boolean defaultRuntime, boolean deprecated) {
+ this.name = name;
+ this.extension = extension;
+ this.version = version;
+ this.defaultRuntime = defaultRuntime;
+ this.deprecated = deprecated;
+ }
+
+ @Override
+ public String toString() {
+ switch (name) {
+ case "java":
+ return name;
+ default:
+ String runtime = name;
+ if (version.length() > 0) {
+ runtime += ":" + version;
+ }
+
+ if (deprecated) {
+ runtime += " (Deprecated)";
+ }
+ return runtime;
+ }
+ }
+
+ public static Runtime toRuntime(String kind) {
+ switch (kind) {
+ case "nodejs:6":
+ return NODE_6;
+ case "nodejs:8":
+ return NODE_8;
+ case "nodejs:10":
+ return NODE_10;
+ case "nodejs:12":
+ return NODE_12;
+ case "nodejs:14":
+ return NODE_14;
+ case "python:2":
+ return PYTHON_2;
+ case "python:3":
+ return PYTHON_3;
+ case "java":
+ return JAVA;
+ case "swift:3.1.1":
+ return SWIFT_3_1_1;
+ case "swift:4.2":
+ return SWIFT_4_2;
+ case "swift:5.1":
+ return SWIFT_5_1;
+ case "php:7.3":
+ return PHP_7_3;
+ case "php:7.4":
+ return PHP_7_4;
+ case "go:1.11":
+ return GO_1_11;
+ case "ruby:2.5":
+ return RUBY_2_5;
+ case "sequence":
+ return SEQUENCE;
+ default:
+ return DOCKER;
+ }
+ }
+
+ public static Runtime toRuntime(int index) {
+ switch (index) {
+ case 0:
+ return NODE_6;
+ case 1:
+ return NODE_8;
+ case 2:
+ return NODE_10;
+ case 3:
+ return NODE_12;
+ case 4:
+ return NODE_14;
+ case 5:
+ return PYTHON_2;
+ case 6:
+ return PYTHON_3;
+ case 7:
+ return JAVA;
+ case 8:
+ return SWIFT_3_1_1;
+ case 9:
+ return SWIFT_4_2;
+ case 10:
+ return SWIFT_5_1;
+ case 11:
+ return PHP_7_3;
+ case 12:
+ return PHP_7_4;
+ case 13:
+ return GO_1_11;
+ case 14:
+ return RUBY_2_5;
+ case 15:
+ return SEQUENCE;
+ default:
+ return DOCKER;
+ }
+ }
+
+ public static Runtime toCodeType(int index) {
+ switch (index) {
+ case 0:
+ return NODE_6;
+ case 1:
+ return NODE_8;
+ case 2:
+ return NODE_10;
+ case 3:
+ return NODE_12;
+ case 4:
+ return NODE_14;
+ case 5:
+ return PYTHON_2;
+ case 6:
+ return PYTHON_3;
+ case 7:
+ return JAVA;
+ case 8:
+ return SWIFT_3_1_1;
+ case 9:
+ return SWIFT_4_2;
+ case 10:
+ return SWIFT_5_1;
+ case 11:
+ return PHP_7_3;
+ case 12:
+ return PHP_7_4;
+ case 13:
+ return GO_1_11;
+ default:
+ return RUBY_2_5;
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuth.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuth.java
new file mode 100644
index 0000000..a044de8
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuth.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.navercorp.openwhisk.intellij.common.utils.WhiskUtils;
+
+import java.util.Base64;
+
+public class WhiskAuth {
+ private String auth;
+ private String apihost;
+
+ public WhiskAuth() {
+ }
+
+ public WhiskAuth(String auth, String apihost) {
+ this.auth = auth;
+ this.apihost = WhiskUtils.getApihHostWithProtocol(apihost);
+ }
+
+ public String getAuth() {
+ return auth;
+ }
+
+ public void setAuth(String auth) {
+ this.auth = auth;
+ }
+
+ public String getApihost() {
+ return apihost;
+ }
+
+ public void setApihost(String apihost) {
+ this.apihost = WhiskUtils.getApihHostWithProtocol(apihost);
+ }
+
+ public String getBasicAuthHeader() {
+ return "Basic " + Base64.getEncoder().encodeToString(auth.getBytes());
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthWithName.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthWithName.java
new file mode 100644
index 0000000..72b176e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthWithName.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+public class WhiskAuthWithName {
+ private WhiskAuth auth;
+ private String name;
+
+ public WhiskAuthWithName(WhiskAuth auth, String name) {
+ this.auth = auth;
+ this.name = name;
+ }
+
+ public WhiskAuth getAuth() {
+ return auth;
+ }
+
+ public void setAuth(WhiskAuth auth) {
+ this.auth = auth;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name + " (" + auth.getApihost() + ")";
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpoint.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpoint.java
new file mode 100644
index 0000000..d36fb12
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpoint.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.navercorp.openwhisk.intellij.common.utils.WhiskUtils;
+
+import java.util.List;
+
+public class WhiskEndpoint {
+ private String alias;
+ private String apihost;
+ private List<WhiskNamespace> namespaces;
+
+ public WhiskEndpoint() {
+ }
+
+ public WhiskEndpoint(String alias, String apihost, List<WhiskNamespace> namespaces) {
+ this.alias = alias;
+ this.apihost = WhiskUtils.getApihHostWithProtocol(apihost);
+ this.namespaces = namespaces;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+
+ public String getApihost() {
+ return apihost;
+ }
+
+ public void setApihost(String apihost) {
+ this.apihost = WhiskUtils.getApihHostWithProtocol(apihost);
+ }
+
+ public List<WhiskNamespace> getNamespaces() {
+ return namespaces;
+ }
+
+ public void setNamespaces(List<WhiskNamespace> namespaces) {
+ this.namespaces = namespaces;
+ }
+
+ public void addNamespaces(WhiskNamespace namespace) {
+ this.namespaces.add(namespace);
+ }
+
+ public String displayName() {
+ return this.alias + " (" + this.apihost + ")";
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskNamespace.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskNamespace.java
new file mode 100644
index 0000000..46c9376
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskNamespace.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@JsonIgnoreProperties(value = {"packages", "actions"})
+public class WhiskNamespace {
+ private String auth;
+ private String path;
+ private List<WhiskPackage> packages = new ArrayList<>();
+ private List<WhiskActionMetaData> actions = new ArrayList<>();
+ private List<WhiskTriggerMetaData> triggers = new ArrayList<>();
+
+ public WhiskNamespace() {
+ }
+
+ public WhiskNamespace(String auth, String path) {
+ this.auth = auth;
+ this.path = path;
+ }
+
+ public WhiskNamespace(String auth, String path, List<WhiskPackage> packages, List<WhiskActionMetaData> actions, List<WhiskTriggerMetaData> triggers) {
+ this.auth = auth;
+ this.path = path;
+ this.packages = packages;
+ this.actions = actions;
+ this.triggers = triggers;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public String getAuth() {
+ return auth;
+ }
+
+ public void setAuth(String auth) {
+ this.auth = auth;
+ }
+
+ public List<WhiskPackage> getPackages() {
+ return packages;
+ }
+
+ public void setPackages(List<WhiskPackage> packages) {
+ this.packages = packages;
+ }
+
+ public List<WhiskActionMetaData> getActions() {
+ return actions;
+ }
+
+ public void setActions(List<WhiskActionMetaData> actions) {
+ this.actions = actions;
+ }
+
+ public List<WhiskTriggerMetaData> getTriggers() {
+ return triggers;
+ }
+
+ public void setTriggers(List<WhiskTriggerMetaData> triggers) {
+ this.triggers = triggers;
+ }
+
+ @Override
+ public String toString() {
+ return path;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ WhiskNamespace that = (WhiskNamespace) o;
+ return Objects.equals(auth, that.auth)
+ && Objects.equals(path, that.path)
+ && Objects.equals(packages, that.packages)
+ && Objects.equals(actions, that.actions)
+ && Objects.equals(triggers, that.triggers);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(auth, path, packages, actions, triggers);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/CompactWhiskAction.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/CompactWhiskAction.java
new file mode 100644
index 0000000..e0b2434
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/CompactWhiskAction.java
@@ -0,0 +1,136 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.action;
+
+import com.navercorp.openwhisk.intellij.common.Icons;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class CompactWhiskAction {
+ private String name;
+ private String version;
+ private List<Map<String, Object>> annotations = new ArrayList<>();
+
+ public CompactWhiskAction() {
+ }
+
+ public CompactWhiskAction(String name, String version, List<Map<String, Object>> annotations) {
+ this.name = name;
+ this.version = version;
+ this.annotations = annotations;
+ }
+
+ public String getKind() {
+ for (Map<String, Object> a : this.annotations) {
+ if ("exec".equals(a.get("key"))) {
+ return (String) a.get("value");
+ }
+ }
+ return "";
+ }
+
+ public String getKindExtension() {
+ String kind = getKind();
+ if (kind.contains("java")) {
+ return ".java";
+ } else if (kind.contains("nodejs")) {
+ return ".js";
+ } else if (kind.contains("python")) {
+ return ".py";
+ } else if (kind.contains("swift")) {
+ return ".swift";
+ } else if (kind.contains("php")) {
+ return ".php";
+ } else if (kind.contains("go")) {
+ return ".go";
+ } else if (kind.contains("ruby")) {
+ return ".rb";
+ } else {
+ return "";
+ }
+ }
+
+ public Icon getKindIcon() {
+ String kind = getKind();
+ if (kind.contains("java")) {
+ return Icons.KIND_JAVA;
+ } else if (kind.contains("nodejs")) {
+ return Icons.KIND_JS;
+ } else if (kind.contains("python")) {
+ return Icons.KIND_PYTHON;
+ } else if (kind.contains("swift")) {
+ return Icons.KIND_SWIFT;
+ } else if (kind.contains("php")) {
+ return Icons.KIND_PHP;
+ } else if (kind.contains("go")) {
+ return Icons.KIND_GO;
+ } else if (kind.contains("ruby")) {
+ return Icons.KIND_RUBY;
+ } else if (kind.contains("sequence")) {
+ return Icons.KIND_SEQUENCE;
+ } else {
+ return Icons.KIND_DOCKER;
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public List<Map<String, Object>> getAnnotations() {
+ return annotations;
+ }
+
+ public void setAnnotations(List<Map<String, Object>> annotations) {
+ this.annotations = annotations;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CompactWhiskAction that = (CompactWhiskAction) o;
+
+ if (name != null ? !name.equals(that.name) : that.name != null) return false;
+ if (version != null ? !version.equals(that.version) : that.version != null) return false;
+ return annotations != null ? annotations.equals(that.annotations) : that.annotations == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (version != null ? version.hashCode() : 0);
+ result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/ExecutableWhiskAction.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/ExecutableWhiskAction.java
new file mode 100644
index 0000000..6c6a41e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/ExecutableWhiskAction.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.action;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.Limits;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.CodeExec;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class ExecutableWhiskAction extends WhiskAction<CodeExec> {
+ private List<Map<String, Object>> parameters = new ArrayList<>();
+
+ public ExecutableWhiskAction() {
+ }
+
+ public ExecutableWhiskAction(List<Map<String, Object>> parameters) {
+ this.parameters = parameters;
+ }
+
+ public ExecutableWhiskAction(String name, String namespace, String version, long updated, boolean publish,
+ List<Map<String, Object>> annotations,
+ Limits limits,
+ CodeExec exec,
+ List<Map<String, Object>> parameters) {
+ super(name, namespace, version, updated, publish, annotations, limits, exec);
+ this.parameters = parameters;
+ }
+
+ public List<Map<String, Object>> getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(List<Map<String, Object>> parameters) {
+ this.parameters = parameters;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+ ExecutableWhiskAction that = (ExecutableWhiskAction) o;
+ return Objects.equals(parameters, that.parameters);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), parameters);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskAction.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskAction.java
new file mode 100644
index 0000000..feff4e2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskAction.java
@@ -0,0 +1,292 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.action;
+
+import com.navercorp.openwhisk.intellij.common.Icons;
+import com.navercorp.openwhisk.intellij.common.whisk.model.Limits;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.Exec;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public abstract class WhiskAction<E extends Exec> {
+ private String name;
+ private String namespace;
+ private String version;
+ private long updated;
+ private boolean publish;
+ private List<Map<String, Object>> annotations = new ArrayList<>();
+ private Limits limits;
+ private E exec;
+
+ public WhiskAction() {
+ }
+
+ public WhiskAction(String name, String namespace, String version, long updated, boolean publish,
+ List<Map<String, Object>> annotations,
+ Limits limits,
+ E exec) {
+ this.name = name;
+ this.namespace = namespace;
+ this.version = version;
+ this.updated = updated;
+ this.publish = publish;
+ this.annotations = annotations;
+ this.limits = limits;
+ this.exec = exec;
+ }
+
+ public Optional<String> getWhiskPackage() {
+ if (namespace == null) {
+ return Optional.empty();
+ } else {
+ String[] ns = namespace.split("/");
+ if (ns.length == 1) {
+ return Optional.empty();
+ } else {
+ return Optional.of(ns[1]);
+ }
+ }
+ }
+
+ public String getNamespacePath() {
+ return namespace.split("/")[0];
+ }
+
+ public String getFullyQualifiedName() {
+ return namespace + "/" + name;
+ }
+
+ public String getKind() {
+ for (Map<String, Object> a : this.annotations) {
+ if ("exec".equals(a.get("key"))) {
+ return (String) a.get("value");
+ }
+ }
+ return "";
+ }
+
+ public Optional<String> getCodeType() {
+ for (Map<String, Object> a : this.annotations) {
+ if ("code-type".equals(a.get("key"))) {
+ return Optional.ofNullable((String) a.get("value"));
+ }
+ }
+ return Optional.empty();
+ }
+
+ public String getKindExtension() {
+ String kind = getKind();
+ if (kind.contains("java")) {
+ return ".java";
+ } else if (kind.contains("nodejs")) {
+ return ".js";
+ } else if (kind.contains("python")) {
+ return ".py";
+ } else if (kind.contains("swift")) {
+ return ".swift";
+ } else if (kind.contains("php")) {
+ return ".php";
+ } else if (kind.contains("go")) {
+ return ".go";
+ } else if (kind.contains("ruby")) {
+ return ".rb";
+ } else if (kind.contains("blackbox")) {
+ return getCodeType().map(codeType -> {
+ if (codeType.contains("java")) {
+ return ".java";
+ } else if (codeType.contains("nodejs")) {
+ return ".js";
+ } else if (codeType.contains("python")) {
+ return ".py";
+ } else if (codeType.contains("swift")) {
+ return ".swift";
+ } else if (codeType.contains("php")) {
+ return ".php";
+ } else if (codeType.contains("go")) {
+ return ".go";
+ } else if (codeType.contains("ruby")) {
+ return ".rb";
+ }
+ return "";
+ }).orElse("");
+ }
+ return "";
+ }
+
+ public Icon getKindIcon() {
+ String kind = getKind();
+ if (kind.contains("java")) {
+ return Icons.KIND_JAVA;
+ } else if (kind.contains("nodejs")) {
+ return Icons.KIND_JS;
+ } else if (kind.contains("python")) {
+ return Icons.KIND_PYTHON;
+ } else if (kind.contains("swift")) {
+ return Icons.KIND_SWIFT;
+ } else if (kind.contains("php")) {
+ return Icons.KIND_PHP;
+ } else if (kind.contains("go")) {
+ return Icons.KIND_GO;
+ } else if (kind.contains("ruby")) {
+ return Icons.KIND_RUBY;
+ } else if (kind.contains("sequence")) {
+ return Icons.KIND_SEQUENCE;
+ } else {
+ return Icons.KIND_DOCKER;
+ }
+ }
+
+
+ public boolean isSequenceAction() {
+ String kind = getKind();
+ return kind.equals("sequence");
+ }
+
+ public boolean isWebAction() {
+ for (Map<String, Object> a : this.annotations) {
+ if ("web-export".equals(a.get("key"))) {
+ return (Boolean) a.get("value");
+ }
+ }
+ return false;
+ }
+
+ public boolean isCustomOptions() {
+ for (Map<String, Object> a : this.annotations) {
+ if ("web-custom-options".equals(a.get("key"))) {
+ return (Boolean) a.get("value");
+ }
+ }
+ return false;
+ }
+
+ public boolean isRawHttp() {
+ for (Map<String, Object> a : this.annotations) {
+ if ("raw-http".equals(a.get("key"))) {
+ return (Boolean) a.get("value");
+ }
+ }
+ return false;
+ }
+
+ public boolean isFinalDefaultParameter() {
+ for (Map<String, Object> a : this.annotations) {
+ if ("final".equals(a.get("key"))) {
+ return (Boolean) a.get("value");
+ }
+ }
+ return false;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public long getUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(long updated) {
+ this.updated = updated;
+ }
+
+ public boolean isPublish() {
+ return publish;
+ }
+
+ public void setPublish(boolean publish) {
+ this.publish = publish;
+ }
+
+ public List<Map<String, Object>> getAnnotations() {
+ return annotations;
+ }
+
+ public void setAnnotations(List<Map<String, Object>> annotations) {
+ this.annotations = annotations;
+ }
+
+ public Limits getLimits() {
+ return limits;
+ }
+
+ public void setLimits(Limits limits) {
+ this.limits = limits;
+ }
+
+ public E getExec() {
+ return exec;
+ }
+
+ public void setExec(E exec) {
+ this.exec = exec;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ WhiskAction<?> that = (WhiskAction<?>) o;
+
+ if (updated != that.updated) return false;
+ if (publish != that.publish) return false;
+ if (name != null ? !name.equals(that.name) : that.name != null) return false;
+ if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
+ if (version != null ? !version.equals(that.version) : that.version != null) return false;
+ if (annotations != null ? !annotations.equals(that.annotations) : that.annotations != null) return false;
+ if (limits != null ? !limits.equals(that.limits) : that.limits != null) return false;
+ return exec != null ? exec.equals(that.exec) : that.exec == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
+ result = 31 * result + (version != null ? version.hashCode() : 0);
+ result = 31 * result + (int) (updated ^ (updated >>> 32));
+ result = 31 * result + (publish ? 1 : 0);
+ result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+ result = 31 * result + (limits != null ? limits.hashCode() : 0);
+ result = 31 * result + (exec != null ? exec.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskActionMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskActionMetaData.java
new file mode 100644
index 0000000..674995e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskActionMetaData.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.action;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.Limits;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.ExecMetaData;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityEntry;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityType;
+
+import java.util.List;
+import java.util.Map;
+
+public class WhiskActionMetaData extends WhiskAction<ExecMetaData> {
+ public WhiskActionMetaData() {
+ }
+
+ public WhiskActionMetaData(String name, String namespace, String version, long updated, boolean publish,
+ List<Map<String, Object>> annotations,
+ Limits limits,
+ ExecMetaData exec) {
+ super(name, namespace, version, updated, publish, annotations, limits, exec);
+ }
+
+ @Override
+ public String toString() {
+ return getWhiskPackage().map(pkg -> pkg + "/" + getName()).orElse(getName());
+ }
+
+ public ComboBoxEntityEntry toCombBoxEntityEntry() {
+ return new ComboBoxEntityEntry(toString(), ComboBoxEntityType.ACTION);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivation.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivation.java
new file mode 100644
index 0000000..4c0be99
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivation.java
@@ -0,0 +1,183 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.activation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public abstract class WhiskActivation {
+
+ private String activationId;
+ private String name;
+ private String namespace;
+ private String version;
+ private String cause;
+ private long start;
+ private long end;
+ private long duration;
+ private boolean publish;
+ private List<Map<String, Object>> annotations = new ArrayList<>();
+
+ public WhiskActivation() {
+ }
+
+ public WhiskActivation(String activationId, String name, String namespace, String version, String cause,
+ long start, long end, long duration, boolean publish,
+ List<Map<String, Object>> annotations) {
+ this.activationId = activationId;
+ this.name = name;
+ this.namespace = namespace;
+ this.version = version;
+ this.cause = cause;
+ this.start = start;
+ this.end = end;
+ this.duration = duration;
+ this.publish = publish;
+ this.annotations = annotations;
+ }
+
+ public String getActivationId() {
+ return activationId;
+ }
+
+ public void setActivationId(String activationId) {
+ this.activationId = activationId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public String getCause() {
+ return cause;
+ }
+
+ public void setCause(String cause) {
+ this.cause = cause;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public long getStart() {
+ return start;
+ }
+
+ public void setStart(long start) {
+ this.start = start;
+ }
+
+ public long getEnd() {
+ return end;
+ }
+
+ public void setEnd(long end) {
+ this.end = end;
+ }
+
+ public long getDuration() {
+ return duration;
+ }
+
+ public void setDuration(long duration) {
+ this.duration = duration;
+ }
+
+ public boolean isPublish() {
+ return publish;
+ }
+
+ public void setPublish(boolean publish) {
+ this.publish = publish;
+ }
+
+ public List<Map<String, Object>> getAnnotations() {
+ return annotations;
+ }
+
+ public void setAnnotations(List<Map<String, Object>> annotations) {
+ this.annotations = annotations;
+ }
+
+ public String getKind() {
+ for (Map<String, Object> a : annotations) {
+ if (a.get("key").equals("kind")) {
+ return (String) a.get("value");
+ }
+ }
+ return "unknown";
+ }
+
+ public String getStartType() {
+ for (Map<String, Object> a : annotations) {
+ if (a.get("key").equals("initTime")) {
+ return "cold";
+ }
+ }
+ return "warm";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ WhiskActivation that = (WhiskActivation) o;
+
+ if (start != that.start) return false;
+ if (end != that.end) return false;
+ if (duration != that.duration) return false;
+ if (publish != that.publish) return false;
+ if (activationId != null ? !activationId.equals(that.activationId) : that.activationId != null) return false;
+ if (name != null ? !name.equals(that.name) : that.name != null) return false;
+ if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
+ if (version != null ? !version.equals(that.version) : that.version != null) return false;
+ return annotations != null ? annotations.equals(that.annotations) : that.annotations == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = activationId != null ? activationId.hashCode() : 0;
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
+ result = 31 * result + (version != null ? version.hashCode() : 0);
+ result = 31 * result + (int) (start ^ (start >>> 32));
+ result = 31 * result + (int) (end ^ (end >>> 32));
+ result = 31 * result + (int) (duration ^ (duration >>> 32));
+ result = 31 * result + (publish ? 1 : 0);
+ result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationMetaData.java
new file mode 100644
index 0000000..9c210a1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationMetaData.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.activation;
+
+import java.util.List;
+import java.util.Map;
+
+public class WhiskActivationMetaData extends WhiskActivation {
+
+ private int statusCode;
+
+ public WhiskActivationMetaData() {
+ }
+
+ public WhiskActivationMetaData(String activationId, String name, String namespace, String version, String cause,
+ long start, long end, long duration, boolean publish,
+ List<Map<String, Object>> annotations,
+ int statusCode) {
+ super(activationId, name, namespace, version, cause, start, end, duration, publish, annotations);
+ this.statusCode = statusCode;
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ public void setStatusCode(int statusCode) {
+ this.statusCode = statusCode;
+ }
+
+ public String getStatus() {
+ switch (this.statusCode) {
+ case 0:
+ return "success";
+ case 1:
+ return "application error";
+ case 2:
+ return "developer error";
+ default:
+ return "internal error";
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ WhiskActivationMetaData that = (WhiskActivationMetaData) o;
+
+ return statusCode == that.statusCode;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + statusCode;
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationWithLogs.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationWithLogs.java
new file mode 100644
index 0000000..b780b6f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationWithLogs.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.activation;
+
+import org.codehaus.groovy.util.ListHashMap;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class WhiskActivationWithLogs extends WhiskActivation {
+ private String subject;
+ private List<String> logs = new ArrayList<>();
+ private Map<String, Object> response = new ListHashMap<>();
+
+ public WhiskActivationWithLogs() {
+ }
+
+ public WhiskActivationWithLogs(String activationId, String name, String namespace, String version, String cause,
+ long start, long end, long duration, boolean publish,
+ List<Map<String, Object>> annotations,
+ String subject,
+ List<String> logs,
+ Map<String, Object> response) {
+ super(activationId, name, namespace, version, cause, start, end, duration, publish, annotations);
+ this.subject = subject;
+ this.logs = logs;
+ this.response = response;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public List<String> getLogs() {
+ return logs;
+ }
+
+ public void setLogs(List<String> logs) {
+ this.logs = logs;
+ }
+
+ public Map<String, Object> getResponse() {
+ return response;
+ }
+
+ public void setResponse(Map<String, Object> response) {
+ this.response = response;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ WhiskActivationWithLogs that = (WhiskActivationWithLogs) o;
+
+ if (subject != null ? !subject.equals(that.subject) : that.subject != null) return false;
+ if (logs != null ? !logs.equals(that.logs) : that.logs != null) return false;
+ return response != null ? response.equals(that.response) : that.response == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (subject != null ? subject.hashCode() : 0);
+ result = 31 * result + (logs != null ? logs.hashCode() : 0);
+ result = 31 * result + (response != null ? response.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExec.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExec.java
new file mode 100644
index 0000000..1d4788b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExec.java
@@ -0,0 +1,121 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.exec;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CodeExec extends Exec {
+ private String kind;
+ @Nullable
+ private String main;
+ private String code;
+ private String image; // for docker image
+ private List<String> components = new ArrayList<>(); // for sequence action
+
+ public CodeExec() {
+ }
+
+ public CodeExec(String kind, @Nullable String main, String code, String image, List<String> components) {
+ this.kind = kind;
+ this.main = main;
+ this.code = code;
+ this.image = image;
+ this.components = components;
+ }
+
+ public CodeExec(boolean binary, String kind, @Nullable String main, String code, String image, List<String> components) {
+ super(binary);
+ this.kind = kind;
+ this.main = main;
+ this.code = code;
+ this.image = image;
+ this.components = components;
+ }
+
+ public String getKind() {
+ return kind;
+ }
+
+ public void setKind(String kind) {
+ this.kind = kind;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getMain() {
+ return main;
+ }
+
+ public void setMain(String main) {
+ this.main = main;
+ }
+
+ public String getImage() {
+ return image;
+ }
+
+ public void setImage(String image) {
+ this.image = image;
+ }
+
+ public List<String> getComponents() {
+ return components;
+ }
+
+ public void setComponents(List<String> components) {
+ this.components = components;
+ }
+
+ public ExecMetaData toExecMetaData() {
+ return new ExecMetaData(isBinary());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ CodeExec codeExec = (CodeExec) o;
+
+ if (kind != null ? !kind.equals(codeExec.kind) : codeExec.kind != null) return false;
+ if (main != null ? !main.equals(codeExec.main) : codeExec.main != null) return false;
+ if (code != null ? !code.equals(codeExec.code) : codeExec.code != null) return false;
+ if (image != null ? !image.equals(codeExec.image) : codeExec.image != null) return false;
+ return components != null ? components.equals(codeExec.components) : codeExec.components == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (kind != null ? kind.hashCode() : 0);
+ result = 31 * result + (main != null ? main.hashCode() : 0);
+ result = 31 * result + (code != null ? code.hashCode() : 0);
+ result = 31 * result + (image != null ? image.hashCode() : 0);
+ result = 31 * result + (components != null ? components.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExecSerializer.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExecSerializer.java
new file mode 100644
index 0000000..c7072ec
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExecSerializer.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.exec;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import java.io.IOException;
+import java.util.Optional;
+
+public class CodeExecSerializer extends JsonSerializer<CodeExec> {
+ @Override
+ public void serialize(CodeExec codeExec, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+ jsonGenerator.writeStartObject();
+
+ jsonGenerator.writeFieldName("binary");
+ jsonGenerator.writeBoolean(codeExec.isBinary());
+
+ jsonGenerator.writeFieldName("kind");
+ jsonGenerator.writeString(codeExec.getKind());
+
+ if (Optional.ofNullable(codeExec.getCode()).isPresent()) {
+ jsonGenerator.writeFieldName("code");
+ jsonGenerator.writeString(codeExec.getCode());
+ }
+
+ if (Optional.ofNullable(codeExec.getMain()).isPresent()) {
+ jsonGenerator.writeFieldName("main");
+ jsonGenerator.writeString(codeExec.getMain());
+ }
+
+ if (Optional.ofNullable(codeExec.getImage()).isPresent()) {
+ jsonGenerator.writeFieldName("image");
+ jsonGenerator.writeString(codeExec.getImage());
+ }
+
+ if (!codeExec.getComponents().isEmpty()) {
+ jsonGenerator.writeFieldName("components");
+ jsonGenerator.writeStartArray();
+ for (String c : codeExec.getComponents()) {
+ jsonGenerator.writeString(c);
+ }
+ jsonGenerator.writeEndArray();
+ }
+
+ jsonGenerator.writeEndObject();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/Exec.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/Exec.java
new file mode 100644
index 0000000..2e48fb5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/Exec.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.exec;
+
+public abstract class Exec {
+ private boolean binary;
+
+ public Exec() {
+ }
+
+ public Exec(boolean binary) {
+ this.binary = binary;
+ }
+
+
+ public boolean isBinary() {
+ return binary;
+ }
+
+ public void setBinary(boolean binary) {
+ this.binary = binary;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Exec exec = (Exec) o;
+
+ return binary == exec.binary;
+ }
+
+ @Override
+ public int hashCode() {
+ return (binary ? 1 : 0);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/ExecMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/ExecMetaData.java
new file mode 100644
index 0000000..6a11506
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/ExecMetaData.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.exec;
+
+public class ExecMetaData extends Exec {
+ public ExecMetaData() {
+ }
+
+ public ExecMetaData(boolean binary) {
+ super(binary);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackage.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackage.java
new file mode 100644
index 0000000..41a7680
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackage.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.pkg;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.Binding;
+
+import java.util.*;
+
+public class WhiskPackage {
+ private String name;
+ private String namespace;
+ private boolean publish;
+ private long updated;
+ private String version;
+ private List<Map<String, Object>> annotations = new ArrayList<>();
+ private Object binding;
+
+ public WhiskPackage() {
+ }
+
+ public WhiskPackage(String name, String namespace, boolean publish, long updated, String version, List<Map<String, Object>> annotations, Object binding) {
+ this.name = name;
+ this.namespace = namespace;
+ this.publish = publish;
+ this.updated = updated;
+ this.version = version;
+ this.annotations = annotations;
+ this.binding = binding;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public boolean isPublish() {
+ return publish;
+ }
+
+ public void setPublish(boolean publish) {
+ this.publish = publish;
+ }
+
+ public long getUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(long updated) {
+ this.updated = updated;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public List<Map<String, Object>> getAnnotations() {
+ return annotations;
+ }
+
+ public void setAnnotations(List<Map<String, Object>> annotations) {
+ this.annotations = annotations;
+ }
+
+ public Optional<Binding> getBinding() {
+ if (binding instanceof Map) {
+ Map<String, String> b = (LinkedHashMap) binding;
+ if (b.containsKey("namespace") && b.containsKey("name")) {
+ return Optional.of(new Binding(b.get("namespace"), b.get("name")));
+ } else {
+ return Optional.empty();
+ }
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public void setBinding(Object binding) {
+ this.binding = binding;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ WhiskPackage that = (WhiskPackage) o;
+
+ if (publish != that.publish) return false;
+ if (updated != that.updated) return false;
+ if (name != null ? !name.equals(that.name) : that.name != null) return false;
+ if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
+ if (version != null ? !version.equals(that.version) : that.version != null) return false;
+ if (annotations != null ? !annotations.equals(that.annotations) : that.annotations != null) return false;
+ return binding != null ? binding.equals(that.binding) : that.binding == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
+ result = 31 * result + (publish ? 1 : 0);
+ result = 31 * result + (int) (updated ^ (updated >>> 32));
+ result = 31 * result + (version != null ? version.hashCode() : 0);
+ result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+ result = 31 * result + (binding != null ? binding.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackageWithActions.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackageWithActions.java
new file mode 100644
index 0000000..5b547da
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackageWithActions.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.pkg;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class WhiskPackageWithActions extends WhiskPackage {
+
+ private List<Map<String, Object>> parameters = new ArrayList<>();
+ private List<CompactWhiskAction> actions = new ArrayList<>();
+ private List<Object> feeds = new ArrayList<>();
+
+ public WhiskPackageWithActions() {
+ }
+
+ public WhiskPackageWithActions(String name, String namespace, boolean publish, long updated, String version,
+ List<Map<String, Object>> annotations,
+ Object binding,
+ List<Map<String, Object>> parameters,
+ List<CompactWhiskAction> actions,
+ List<Object> feeds) {
+ super(name, namespace, publish, updated, version, annotations, binding);
+ this.parameters = parameters;
+ this.actions = actions;
+ this.feeds = feeds;
+ }
+
+ public List<CompactWhiskAction> getActions() {
+ return actions;
+ }
+
+ public void setActions(List<CompactWhiskAction> actions) {
+ this.actions = actions;
+ }
+
+ public List<Map<String, Object>> getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(List<Map<String, Object>> parameters) {
+ this.parameters = parameters;
+ }
+
+
+ public List<Object> getFeeds() {
+ return feeds;
+ }
+
+ public void setFeeds(List<Object> feeds) {
+ this.feeds = feeds;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ WhiskPackageWithActions that = (WhiskPackageWithActions) o;
+
+ if (parameters != null ? !parameters.equals(that.parameters) : that.parameters != null) return false;
+ if (actions != null ? !actions.equals(that.actions) : that.actions != null) return false;
+ return feeds != null ? feeds.equals(that.feeds) : that.feeds == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (parameters != null ? parameters.hashCode() : 0);
+ result = 31 * result + (actions != null ? actions.hashCode() : 0);
+ result = 31 * result + (feeds != null ? feeds.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/ExecutableWhiskTrigger.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/ExecutableWhiskTrigger.java
new file mode 100644
index 0000000..e9c9fae
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/ExecutableWhiskTrigger.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExecutableWhiskTrigger extends WhiskTrigger {
+
+ private List<Map<String, Object>> parameters = new ArrayList<>();
+ private Map<String, SimplifiedWhiskRule> rules = new LinkedHashMap<>();
+
+ /**
+ * Limits on a specific trigger. None yet.
+ */
+ private Map<String, Object> limits;
+
+ public ExecutableWhiskTrigger() {
+ }
+
+ public ExecutableWhiskTrigger(String name, String namespace, String version, long updated, boolean publish,
+ List<Map<String, Object>> annotations,
+ List<Map<String, Object>> parameters,
+ Map<String, SimplifiedWhiskRule> rules,
+ Map<String, Object> limits) {
+ super(name, namespace, version, updated, publish, annotations);
+ this.parameters = parameters;
+ this.rules = rules;
+ this.limits = limits;
+ }
+
+ public List<Map<String, Object>> getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(List<Map<String, Object>> parameters) {
+ this.parameters = parameters;
+ }
+
+ public Map<String, SimplifiedWhiskRule> getRules() {
+ return rules;
+ }
+
+ public void setRules(Map<String, SimplifiedWhiskRule> rules) {
+ this.rules = rules;
+ }
+
+ public Map<String, Object> getLimits() {
+ return limits;
+ }
+
+ public void setLimits(Map<String, Object> limits) {
+ this.limits = limits;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ ExecutableWhiskTrigger that = (ExecutableWhiskTrigger) o;
+
+ if (parameters != null ? !parameters.equals(that.parameters) : that.parameters != null) return false;
+ if (rules != null ? !rules.equals(that.rules) : that.rules != null) return false;
+ return limits != null ? limits.equals(that.limits) : that.limits == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (parameters != null ? parameters.hashCode() : 0);
+ result = 31 * result + (rules != null ? rules.hashCode() : 0);
+ result = 31 * result + (limits != null ? limits.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedEntityMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedEntityMetaData.java
new file mode 100644
index 0000000..6ce3ed5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedEntityMetaData.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+public class SimplifiedEntityMetaData {
+ private String path;
+ private String name;
+
+ public SimplifiedEntityMetaData() {
+ }
+
+ public SimplifiedEntityMetaData(String path, String name) {
+ this.path = path;
+ this.name = name;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getPkgActionName() {
+ String pkg = "";
+ if (path.contains("/")) {
+ pkg = path.split("/")[1] + "/";
+ }
+ return pkg + name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ SimplifiedEntityMetaData that = (SimplifiedEntityMetaData) o;
+
+ if (path != null ? !path.equals(that.path) : that.path != null) return false;
+ return name != null ? name.equals(that.name) : that.name == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = path != null ? path.hashCode() : 0;
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedWhiskRule.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedWhiskRule.java
new file mode 100644
index 0000000..cb3a420
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedWhiskRule.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+public class SimplifiedWhiskRule {
+
+ private SimplifiedEntityMetaData action;
+ private String status;
+
+ public SimplifiedWhiskRule() {
+ }
+
+ public SimplifiedWhiskRule(SimplifiedEntityMetaData action, String status) {
+ this.action = action;
+ this.status = status;
+ }
+
+ public SimplifiedEntityMetaData getAction() {
+ return action;
+ }
+
+ public void setAction(SimplifiedEntityMetaData action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ SimplifiedWhiskRule that = (SimplifiedWhiskRule) o;
+
+ if (action != null ? !action.equals(that.action) : that.action != null) return false;
+ return status != null ? status.equals(that.status) : that.status == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = action != null ? action.hashCode() : 0;
+ result = 31 * result + (status != null ? status.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRule.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRule.java
new file mode 100644
index 0000000..5b57f94
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRule.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class WhiskRule extends WhiskRuleMetaData {
+
+ private String status;
+ private List<Map<String, Object>> annotations = new ArrayList<>();
+
+ public WhiskRule() {
+ }
+
+ public WhiskRule(String name, String namespace, String version, long updated, boolean publish,
+ SimplifiedEntityMetaData action, SimplifiedEntityMetaData trigger) {
+ super(name, namespace, version, updated, publish, action, trigger);
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public List<Map<String, Object>> getAnnotations() {
+ return annotations;
+ }
+
+ public void setAnnotations(List<Map<String, Object>> annotations) {
+ this.annotations = annotations;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ WhiskRule whiskRule = (WhiskRule) o;
+
+ if (status != null ? !status.equals(whiskRule.status) : whiskRule.status != null) return false;
+ return annotations != null ? annotations.equals(whiskRule.annotations) : whiskRule.annotations == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (status != null ? status.hashCode() : 0);
+ result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRuleMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRuleMetaData.java
new file mode 100644
index 0000000..6c92016
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRuleMetaData.java
@@ -0,0 +1,126 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+public class WhiskRuleMetaData {
+
+ private String name;
+ private String namespace;
+ private String version;
+ private long updated;
+ private boolean publish;
+ private SimplifiedEntityMetaData action;
+ private SimplifiedEntityMetaData trigger;
+
+ public WhiskRuleMetaData() {
+ }
+
+ public WhiskRuleMetaData(String name, String namespace, String version, long updated, boolean publish,
+ SimplifiedEntityMetaData action, SimplifiedEntityMetaData trigger) {
+ this.name = name;
+ this.namespace = namespace;
+ this.version = version;
+ this.updated = updated;
+ this.publish = publish;
+ this.action = action;
+ this.trigger = trigger;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public long getUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(long updated) {
+ this.updated = updated;
+ }
+
+ public boolean isPublish() {
+ return publish;
+ }
+
+ public void setPublish(boolean publish) {
+ this.publish = publish;
+ }
+
+ public SimplifiedEntityMetaData getAction() {
+ return action;
+ }
+
+ public void setAction(SimplifiedEntityMetaData action) {
+ this.action = action;
+ }
+
+ public SimplifiedEntityMetaData getTrigger() {
+ return trigger;
+ }
+
+ public void setTrigger(SimplifiedEntityMetaData trigger) {
+ this.trigger = trigger;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ WhiskRuleMetaData whiskRuleMetaData = (WhiskRuleMetaData) o;
+
+ if (updated != whiskRuleMetaData.updated) return false;
+ if (publish != whiskRuleMetaData.publish) return false;
+ if (name != null ? !name.equals(whiskRuleMetaData.name) : whiskRuleMetaData.name != null) return false;
+ if (namespace != null ? !namespace.equals(whiskRuleMetaData.namespace) : whiskRuleMetaData.namespace != null) return false;
+ if (version != null ? !version.equals(whiskRuleMetaData.version) : whiskRuleMetaData.version != null) return false;
+ if (action != null ? !action.equals(whiskRuleMetaData.action) : whiskRuleMetaData.action != null) return false;
+ return trigger != null ? trigger.equals(whiskRuleMetaData.trigger) : whiskRuleMetaData.trigger == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
+ result = 31 * result + (version != null ? version.hashCode() : 0);
+ result = 31 * result + (int) (updated ^ (updated >>> 32));
+ result = 31 * result + (publish ? 1 : 0);
+ result = 31 * result + (action != null ? action.hashCode() : 0);
+ result = 31 * result + (trigger != null ? trigger.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTrigger.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTrigger.java
new file mode 100644
index 0000000..8063521
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTrigger.java
@@ -0,0 +1,117 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public abstract class WhiskTrigger {
+
+ private String name;
+ private String namespace;
+ private String version;
+ private long updated;
+ private boolean publish;
+ private List<Map<String, Object>> annotations = new ArrayList<>();
+
+ public WhiskTrigger() {
+ }
+
+ public WhiskTrigger(String name, String namespace, String version, long updated, boolean publish, List<Map<String, Object>> annotations) {
+ this.name = name;
+ this.namespace = namespace;
+ this.version = version;
+ this.updated = updated;
+ this.publish = publish;
+ this.annotations = annotations;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public long getUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(long updated) {
+ this.updated = updated;
+ }
+
+ public boolean isPublish() {
+ return publish;
+ }
+
+ public void setPublish(boolean publish) {
+ this.publish = publish;
+ }
+
+ public List<Map<String, Object>> getAnnotations() {
+ return annotations;
+ }
+
+ public void setAnnotations(List<Map<String, Object>> annotations) {
+ this.annotations = annotations;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ WhiskTrigger that = (WhiskTrigger) o;
+
+ if (updated != that.updated) return false;
+ if (publish != that.publish) return false;
+ if (name != null ? !name.equals(that.name) : that.name != null) return false;
+ if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
+ if (version != null ? !version.equals(that.version) : that.version != null) return false;
+ return annotations != null ? annotations.equals(that.annotations) : that.annotations == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
+ result = 31 * result + (version != null ? version.hashCode() : 0);
+ result = 31 * result + (int) (updated ^ (updated >>> 32));
+ result = 31 * result + (publish ? 1 : 0);
+ result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerMetaData.java
new file mode 100644
index 0000000..a5250b4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerMetaData.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityEntry;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityType;
+
+import java.util.List;
+import java.util.Map;
+
+public class WhiskTriggerMetaData extends WhiskTrigger {
+
+ public WhiskTriggerMetaData() {
+ }
+
+ public WhiskTriggerMetaData(String name, String namespace, String version, long updated, boolean publish, List<Map<String, Object>> annotations) {
+ super(name, namespace, version, updated, publish, annotations);
+ }
+
+ public ComboBoxEntityEntry toCombBoxEntityEntry() {
+ return new ComboBoxEntityEntry(getName(), ComboBoxEntityType.TRIGGER);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerRoot.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerRoot.java
new file mode 100644
index 0000000..be28a78
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerRoot.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+public class WhiskTriggerRoot {
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/NullWskDeployBinary.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/NullWskDeployBinary.java
new file mode 100644
index 0000000..32bd8e1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/NullWskDeployBinary.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+public class NullWskDeployBinary implements WskDeployFile {
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployBinary.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployBinary.java
new file mode 100644
index 0000000..86062d5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployBinary.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+public class WskDeployBinary implements WskDeployFile {
+ private String fullPath;
+ private String name;
+
+ public WskDeployBinary(String fullPath, String name) {
+ this.fullPath = fullPath;
+ this.name = name;
+ }
+
+ public String getFullPath() {
+ return fullPath;
+ }
+
+ public void setFullPath(String fullPath) {
+ this.fullPath = fullPath;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmd.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmd.java
new file mode 100644
index 0000000..8c3db12
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmd.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+
+public interface WskDeployCmd {
+
+ String[] toCmd(WhiskAuth auth);
+
+ String toCmdString();
+
+ String getCmdName();
+
+ WskDeployManifest getManifest();
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdDeploy.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdDeploy.java
new file mode 100644
index 0000000..daf0463
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdDeploy.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+import com.navercorp.openwhisk.intellij.common.error.NotExistFileException;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+
+import java.util.Optional;
+
+public class WskDeployCmdDeploy implements WskDeployCmd {
+
+ private Optional<WskDeployBinary> wskDeployBinary;
+ private WskDeployManifest manifest;
+
+ private WskDeployCmdDeploy() {
+ }
+
+ public WskDeployCmdDeploy(Optional<WskDeployBinary> wskDeployBinary, WskDeployManifest manifest) {
+ this.wskDeployBinary = wskDeployBinary;
+ this.manifest = manifest;
+ }
+
+ @Override
+ public String[] toCmd(WhiskAuth auth) {
+ return wskDeployBinary
+ .map(bin -> new String[]{
+ bin.getFullPath(),
+ "--manifest", manifest.getFullPath(),
+ "--auth", auth.getAuth(),
+ "--apihost", auth.getApihost()
+ })
+ .orElseThrow(() -> new NotExistFileException("The wskdeploy binary file does not exist."));
+ }
+
+ @Override
+ public String toCmdString() {
+ String[] cmd = wskDeployBinary
+ .map(bin -> new String[]{
+ bin.getFullPath(),
+ "--manifest", manifest.getFullPath(),
+ })
+ .orElseThrow(() -> new NotExistFileException("The wskdeploy binary file does not exist."));
+ StringBuilder str = new StringBuilder();
+ for (String c : cmd) {
+ str.append(c + " ");
+ }
+ return str.toString();
+ }
+
+ @Override
+ public String getCmdName() {
+ return "deploy";
+ }
+
+ @Override
+ public WskDeployManifest getManifest() {
+ return manifest;
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdResponse.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdResponse.java
new file mode 100644
index 0000000..6ccdb3b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdResponse.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+public class WskDeployCmdResponse {
+ private int existCode;
+ private String successOutput;
+ private String errorOutput;
+
+ public WskDeployCmdResponse(int existCode, String successOutput, String errorOutput) {
+ this.existCode = existCode;
+ this.successOutput = successOutput;
+ this.errorOutput = errorOutput;
+ }
+
+ public int getExistCode() {
+ return existCode;
+ }
+
+ public void setExistCode(int existCode) {
+ this.existCode = existCode;
+ }
+
+ public String getSuccessOutput() {
+ return successOutput;
+ }
+
+ public void setSuccessOutput(String successOutput) {
+ this.successOutput = successOutput;
+ }
+
+ public String getErrorOutput() {
+ return errorOutput;
+ }
+
+ public void setErrorOutput(String errorOutput) {
+ this.errorOutput = errorOutput;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdUndeploy.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdUndeploy.java
new file mode 100644
index 0000000..d7a2d2b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdUndeploy.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+import com.navercorp.openwhisk.intellij.common.error.NotExistFileException;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+
+import java.util.Optional;
+
+public class WskDeployCmdUndeploy implements WskDeployCmd {
+
+ private Optional<WskDeployBinary> wskDeployBinary;
+ private WskDeployManifest manifest;
+
+ private WskDeployCmdUndeploy() {
+ }
+
+ public WskDeployCmdUndeploy(Optional<WskDeployBinary> wskDeployBinary, WskDeployManifest manifest) {
+ this.wskDeployBinary = wskDeployBinary;
+ this.manifest = manifest;
+ }
+
+ @Override
+ public String[] toCmd(WhiskAuth auth) {
+ return wskDeployBinary
+ .map(bin -> new String[]{
+ bin.getFullPath(), "undeploy",
+ "--manifest", manifest.getFullPath(),
+ "--auth", auth.getAuth(),
+ "--apihost", auth.getApihost()
+ })
+ .orElseThrow(() -> new NotExistFileException("The wskdeploy binary file does not exist."));
+ }
+
+ @Override
+ public String toCmdString() throws NotExistFileException {
+ String[] cmd = wskDeployBinary
+ .map(bin -> new String[]{
+ bin.getFullPath(), "undeploy",
+ "--manifest", manifest.getFullPath(),
+ })
+ .orElseThrow(() -> new NotExistFileException("The wskdeploy binary file does not exist."));
+ StringBuilder str = new StringBuilder();
+ for (String c : cmd) {
+ str.append(c + " ");
+ }
+ return str.toString();
+ }
+
+ @Override
+ public String getCmdName() {
+ return "undeploy";
+ }
+
+ @Override
+ public WskDeployManifest getManifest() {
+ return manifest;
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployFile.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployFile.java
new file mode 100644
index 0000000..4b010c1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployFile.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+public interface WskDeployFile {
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployManifest.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployManifest.java
new file mode 100644
index 0000000..8bc4b1f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployManifest.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+public class WskDeployManifest {
+
+ private String path;
+ private String fullPath;
+ private String name;
+
+ public WskDeployManifest() {
+ }
+
+ public WskDeployManifest(String filePath, String fullFilePath, String fileName) {
+ this.path = filePath;
+ this.fullPath = fullFilePath;
+ this.name = fileName;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public String getFullPath() {
+ return fullPath;
+ }
+
+ public void setFullPath(String fullPath) {
+ this.fullPath = fullPath;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ WskDeployManifest manifest = (WskDeployManifest) o;
+
+ if (path != null ? !path.equals(manifest.path) : manifest.path != null) return false;
+ if (fullPath != null ? !fullPath.equals(manifest.fullPath) : manifest.fullPath != null) return false;
+ return name != null ? name.equals(manifest.name) : manifest.name == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = path != null ? path.hashCode() : 0;
+ result = 31 * result + (fullPath != null ? fullPath.hashCode() : 0);
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActionService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActionService.java
new file mode 100644
index 0000000..1bdfda4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActionService.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.entity.ContentType;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class WhiskActionService {
+ private static final Logger LOG = Logger.getInstance(WhiskActionService.class);
+
+ private WhiskActionService() {
+
+ }
+
+ private static class LazyHolder {
+ private static final WhiskActionService INSTANCE = new WhiskActionService();
+ }
+
+ public static WhiskActionService getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ public List<WhiskActionMetaData> getWhiskActions(WhiskAuth whiskAuth) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/actions?limit=200&skip=0";
+ String authorization = whiskAuth.getBasicAuthHeader();
+
+ String result = Request.Get(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskActions(result);
+ }
+
+ public Optional<ExecutableWhiskAction> getWhiskAction(WhiskAuth whiskAuth,
+ Optional<String> namespaceName,
+ Optional<String> pkgName,
+ String actionName) throws IOException {
+ String namespace = namespaceName.orElse("_");
+ String name = pkgName.map(p -> p + "/" + actionName).orElse(actionName);
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/" + namespace + "/actions/" + name + "?code=true";
+ String authorization = whiskAuth.getBasicAuthHeader();
+
+ String result = Request.Get(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskAction(result);
+ }
+
+ public String invokeWhiskAction(WhiskAuth whiskAuth,
+ Optional<String> namespaceName,
+ Optional<String> pkgName,
+ String actionName,
+ String params) throws IOException {
+ String namespace = namespaceName.orElse("_");
+ String name = pkgName.map(p -> p + "/" + actionName).orElse(actionName);
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/" + namespace + "/actions/" + name + "?blocking=true&result=true";
+ String authorization = whiskAuth.getBasicAuthHeader();
+ String result = Request.Post(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .bodyString(params, ContentType.APPLICATION_JSON)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.beautifyJson(result);
+ }
+
+ public Optional<ExecutableWhiskAction> updateWhiskAction(WhiskAuth whiskAuth,
+ ExecutableWhiskAction updatedAction,
+ Map<String, Object> payload) throws IOException {
+ String namespace = updatedAction.getNamespacePath();
+ String name = updatedAction.getWhiskPackage().map(p -> p + "/" + updatedAction.getName()).orElse(updatedAction.getName());
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/" + namespace + "/actions/" + name + "?overwrite=true";
+ String authorization = whiskAuth.getBasicAuthHeader();
+ String body = JsonParserUtils.writeMapToJson(payload);
+ LOG.info("Action updated: " + body);
+ String result = Request.Put(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .bodyString(body, ContentType.APPLICATION_JSON)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskAction(result);
+ }
+
+ public Optional<ExecutableWhiskAction> deleteWhiskActions(WhiskAuth whiskAuth, Optional<String> pkgName, String actionName) throws IOException {
+ String name = pkgName.map(p -> p + "/" + actionName).orElse(actionName);
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/actions/" + name;
+ String authorization = whiskAuth.getBasicAuthHeader();
+
+ String result = Request.Delete(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskAction(result);
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActivationService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActivationService.java
new file mode 100644
index 0000000..c3b1ac5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActivationService.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.fluent.Request;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class WhiskActivationService {
+
+ private WhiskActivationService() {
+ }
+
+ private static class LazyHolder {
+ private static final WhiskActivationService INSTANCE = new WhiskActivationService();
+ }
+
+ public static WhiskActivationService getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ public List<WhiskActivationMetaData> getWhiskActivations(WhiskAuth whiskAuth, Optional<String> name, int limit, int skip) throws IOException {
+ String endpoint = whiskAuth.getApihost()
+ + "/api/v1/namespaces/_/activations"
+ + "?limit=" + limit + "&skip=" + skip + "" + name.map(n -> "&name=" + n).orElse("");
+ String authorization = whiskAuth.getBasicAuthHeader();
+
+ String result = Request.Get(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskActivations(result);
+ }
+
+ public Optional<WhiskActivationWithLogs> getWhiskActivation(WhiskAuth whiskAuth, String activationId) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/activations/" + activationId;
+ String authorization = whiskAuth.getBasicAuthHeader();
+
+ String result = Request.Get(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskActivation(result);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskNamespaceService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskNamespaceService.java
new file mode 100644
index 0000000..9621aa3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskNamespaceService.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.fluent.Request;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+
+import java.io.IOException;
+import java.util.Optional;
+
+public class WhiskNamespaceService {
+ private static final Logger LOG = Logger.getInstance(WhiskNamespaceService.class);
+
+ private WhiskNamespaceService() {
+
+ }
+
+ private static class LazyHolder {
+ private static final WhiskNamespaceService INSTANCE = new WhiskNamespaceService();
+ }
+
+ public static WhiskNamespaceService getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ public Optional<WhiskNamespace> validateNamespace(WhiskAuth whiskAuth) {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces";
+ String authorization = whiskAuth.getBasicAuthHeader();
+ try {
+ String result = Request.Get(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString();
+
+ String[] namespaces = JsonParserUtils.parseWhiskNamespace(result);
+ if (namespaces.length > 0) {
+ return Optional.of(new WhiskNamespace(whiskAuth.getAuth(), namespaces[0]));
+ } else {
+ return Optional.empty();
+ }
+ } catch (IOException e) {
+ LOG.warn("Invalid namespace", e);
+ return Optional.empty();
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskPackageService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskPackageService.java
new file mode 100644
index 0000000..f6702b5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskPackageService.java
@@ -0,0 +1,140 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.entity.ContentType;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class WhiskPackageService {
+ private static final Logger LOG = Logger.getInstance(WhiskPackageService.class);
+
+ private WhiskPackageService() {
+
+ }
+
+ private static class LazyHolder {
+ private static final WhiskPackageService INSTANCE = new WhiskPackageService();
+ }
+
+ public static WhiskPackageService getInstance() {
+ return WhiskPackageService.LazyHolder.INSTANCE;
+ }
+
+ public List<WhiskPackage> getWhiskPackages(WhiskAuth whiskAuth) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/packages?limit=200&skip=0";
+ String authorization = whiskAuth.getBasicAuthHeader();
+
+ String result = Request.Get(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskPackages(result);
+ }
+
+ public Optional<WhiskPackageWithActions> getWhiskPackage(WhiskAuth whiskAuth, String namespace, String name) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/" + namespace + "/packages/" + name;
+ String authorization = whiskAuth.getBasicAuthHeader();
+
+ String result = Request.Get(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskPackage(result);
+ }
+
+ public Optional<WhiskPackageWithActions> deleteWhiskPackage(WhiskAuth whiskAuth, String name) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/packages/" + name;
+ String authorization = whiskAuth.getBasicAuthHeader();
+ HttpResponse response = Request.Delete(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnResponse();
+
+ if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CONFLICT) {
+ return Optional.empty();
+ } else {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ response.getEntity().getContent()));
+ String inputLine;
+ StringBuffer result = new StringBuffer();
+ while ((inputLine = reader.readLine()) != null) {
+ result.append(inputLine);
+ }
+ reader.close();
+ return JsonParserUtils.parseWhiskPackage(result.toString());
+ }
+ }
+
+ public Optional<WhiskPackageWithActions> updateWhiskPackage(WhiskAuth whiskAuth, String name, Map<String, Object> payload) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/packages/" + name + "?overwrite=true";
+ String authorization = whiskAuth.getBasicAuthHeader();
+ String body = JsonParserUtils.writeMapToJson(payload);
+ LOG.info("Package updated: " + body);
+ String result = Request.Put(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .bodyString(body, ContentType.APPLICATION_JSON)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskPackage(result);
+ }
+
+ public Optional<WhiskPackageWithActions> createWhiskPackage(WhiskAuth whiskAuth, String name, Map<String, Object> payload) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/packages/" + name + "?overwrite=false";
+ String authorization = whiskAuth.getBasicAuthHeader();
+ String body = JsonParserUtils.writeMapToJson(payload);
+ LOG.info("Package craeted: " + body);
+ HttpResponse response = Request.Put(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .bodyString(body, ContentType.APPLICATION_JSON)
+ .execute()
+ .returnResponse();
+
+ if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CONFLICT) {
+ return Optional.empty();
+ } else {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ response.getEntity().getContent()));
+ String inputLine;
+ StringBuffer result = new StringBuffer();
+ while ((inputLine = reader.readLine()) != null) {
+ result.append(inputLine);
+ }
+ reader.close();
+ return JsonParserUtils.parseWhiskPackage(result.toString());
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskRuleService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskRuleService.java
new file mode 100644
index 0000000..bc93ae5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskRuleService.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.entity.ContentType;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskRule;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class WhiskRuleService {
+ private static final Logger LOG = Logger.getInstance(WhiskRuleService.class);
+
+ private WhiskRuleService() {
+ }
+
+ private static class LazyHolder {
+ private static final WhiskRuleService INSTANCE = new WhiskRuleService();
+ }
+
+ public static WhiskRuleService getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ public Optional<WhiskRule> updateWhiskRule(WhiskAuth whiskAuth, String ruleName, Map<String, Object> payload) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/rules/" + ruleName + "?overwrite=true";
+ String authorization = whiskAuth.getBasicAuthHeader();
+ String body = JsonParserUtils.writeMapToJson(payload);
+ LOG.info("Rule updated: " + body);
+ String result = Request.Put(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .bodyString(body, ContentType.APPLICATION_JSON)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskRule(result);
+ }
+
+ public Optional<WhiskRule> deleteWhiskRule(WhiskAuth whiskAuth, String ruleName) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/rules/" + ruleName;
+ String authorization = whiskAuth.getBasicAuthHeader();
+ String result = Request.Delete(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskRule(result);
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskTriggerService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskTriggerService.java
new file mode 100644
index 0000000..4c3e095
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskTriggerService.java
@@ -0,0 +1,129 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.fluent.Content;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.entity.ContentType;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class WhiskTriggerService {
+ private static final Logger LOG = Logger.getInstance(WhiskTriggerService.class);
+
+ private WhiskTriggerService() {
+ }
+
+ private static class LazyHolder {
+ private static final WhiskTriggerService INSTANCE = new WhiskTriggerService();
+ }
+
+ public static WhiskTriggerService getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ public List<WhiskTriggerMetaData> getWhiskTriggers(WhiskAuth whiskAuth) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers?limit=50&skip=0";
+ String authorization = whiskAuth.getBasicAuthHeader();
+
+ String result = Request.Get(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskTriggers(result);
+ }
+
+ public Optional<ExecutableWhiskTrigger> getWhiskTrigger(WhiskAuth whiskAuth, String triggerName) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers/" + triggerName;
+ String authorization = whiskAuth.getBasicAuthHeader();
+
+ String result = Request.Get(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskTrigger(result);
+ }
+
+ public Optional<String> fireWhiskTrigger(WhiskAuth whiskAuth, String triggerName, String params) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers/" + triggerName;
+ String authorization = whiskAuth.getBasicAuthHeader();
+ Content content = Request.Post(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .bodyString(params, ContentType.APPLICATION_JSON)
+ .execute()
+ .returnContent();
+ if (content != null) {
+ return Optional.ofNullable(JsonParserUtils.beautifyJson(content.asString(UTF_8)));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public Optional<ExecutableWhiskTrigger> deleteWhiskTrigger(WhiskAuth whiskAuth, String triggerName) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers/" + triggerName;
+ String authorization = whiskAuth.getBasicAuthHeader();
+
+ String result = Request.Delete(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskTrigger(result);
+ }
+
+ public Optional<ExecutableWhiskTrigger> createWhiskTrigger(WhiskAuth whiskAuth, String triggerName, Map<String, Object> payload) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers/" + triggerName + "?overwrite=false";
+ String authorization = whiskAuth.getBasicAuthHeader();
+ String body = JsonParserUtils.writeMapToJson(payload);
+ LOG.info("Trigger updated: " + body);
+ String result = Request.Put(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .bodyString(body, ContentType.APPLICATION_JSON)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskTrigger(result);
+ }
+
+ public Optional<ExecutableWhiskTrigger> updateWhiskTrigger(WhiskAuth whiskAuth, String triggerName, Map<String, Object> payload) throws IOException {
+ String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers/" + triggerName + "?overwrite=true";
+ String authorization = whiskAuth.getBasicAuthHeader();
+ String body = JsonParserUtils.writeMapToJson(payload);
+ LOG.info("Trigger updated: " + body);
+ String result = Request.Put(endpoint)
+ .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+ .bodyString(body, ContentType.APPLICATION_JSON)
+ .execute()
+ .returnContent()
+ .asString(UTF_8);
+ return JsonParserUtils.parseWhiskTrigger(result);
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ActionManagerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ActionManagerDialog.java
new file mode 100644
index 0000000..7a10e4b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ActionManagerDialog.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.ActionManagerDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+public class ActionManagerDialog extends DialogWrapperWithApply {
+
+ private ActionManagerDialogForm actionManagerDialogForm;
+
+ public ActionManagerDialog(Project project, WhiskAuth auth, ExecutableWhiskAction action, List<WhiskActionMetaData> actions) {
+ super(project, true); // use current window as parent
+ actionManagerDialogForm = new ActionManagerDialogForm(project, auth, action, actions);
+ setTitle("Manage Action");
+ setResizable(false);
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ if (actionManagerDialogForm != null) {
+ return this.actionManagerDialogForm.getContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void doApplyAction() {
+ if (actionManagerDialogForm != null) {
+ actionManagerDialogForm.updateAction();
+ }
+ super.doApplyAction();
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (actionManagerDialogForm != null && !myApplyAction.isApplied()) {
+ actionManagerDialogForm.updateAction();
+ }
+ super.doOKAction();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/DeleteActionDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/DeleteActionDialog.java
new file mode 100644
index 0000000..bb510bb
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/DeleteActionDialog.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DeleteActionDialog extends DialogWrapper {
+
+ public DeleteActionDialog(Project project) {
+ super(true); // use current window as parent
+ setTitle("Delete Action");
+ setResizable(false);
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ JPanel dialogPanel = new JPanel(new BorderLayout());
+
+ JLabel label = new JLabel("Are you sure you want to delete your action?");
+ label.setPreferredSize(new Dimension(100, 30));
+ dialogPanel.add(label, BorderLayout.CENTER);
+
+ return dialogPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/DownActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/DownActionListener.java
new file mode 100644
index 0000000..c3ca8ed
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/DownActionListener.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.listener;
+
+public interface DownActionListener {
+ void downAction(int index);
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/RemoveActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/RemoveActionListener.java
new file mode 100644
index 0000000..c211853
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/RemoveActionListener.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.listener;
+
+public interface RemoveActionListener {
+ void removeAction(int index);
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/UpActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/UpActionListener.java
new file mode 100644
index 0000000..32d88bc
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/UpActionListener.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.listener;
+
+public interface UpActionListener {
+ void upAction(int index);
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.form
new file mode 100644
index 0000000..9ecf0e0
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.form
@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.ActionManagerDialogForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="14" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="936" height="525"/>
+ </constraints>
+ <properties>
+ <preferredSize width="800" height="300"/>
+ </properties>
+ <border type="none"/>
+ <children>
+ <grid id="519e8" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="800" height="30"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="27de0" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Runtime"/>
+ </properties>
+ </component>
+ <component id="e5ddb" class="javax.swing.JComboBox" binding="runtimeJComboBox">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="700" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="4cbec" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="fd8b" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="800" height="30"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="10d33" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="47667" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Timeout (ms)"/>
+ </properties>
+ </component>
+ <component id="8ac53" class="javax.swing.JSpinner" binding="timeoutJSpinner">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="b5d2b" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="dd67a" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="866bd" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Memory (MB)"/>
+ </properties>
+ </component>
+ <component id="43f8a" class="javax.swing.JSpinner" binding="memoryJSpinner">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="6f7cf" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <component id="19f4a" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="2e6c5" layout-manager="GridLayoutManager" row-count="1" column-count="4" same-size-horizontally="false" same-size-vertically="true" hgap="-1" vgap="0">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="1" use-parent-layout="false">
+ <minimum-size width="-1" height="30"/>
+ <preferred-size width="800" height="30"/>
+ <maximum-size width="-1" height="30"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="17e79" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="77c80" class="javax.swing.JCheckBox" binding="webactionJCheckBox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="300" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Web Action"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="30b99" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="61458" class="javax.swing.JCheckBox" binding="rawHttpJCheckBox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Raw HTTP"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="660f" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="d7b90" class="javax.swing.JCheckBox" binding="customOptionHeaderJCheckBox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Custom Options Header"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="96f9d" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="ed717" class="javax.swing.JCheckBox" binding="finalOptionJCheckBox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Protect Default Parameter"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <grid id="4506f" binding="defaultParameterJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="12" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <component id="bc149" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="-1" height="5"/>
+ <maximum-size width="-1" height="5"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="f54d9" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="-1" height="5"/>
+ <maximum-size width="-1" height="5"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="ee953" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="-1" height="5"/>
+ <maximum-size width="-1" height="5"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="1ae9" class="javax.swing.JSeparator" binding="defaultParameterJSeparator">
+ <constraints>
+ <grid row="13" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="-1" height="5"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <grid id="82b18" binding="dockerImageJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="8" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <component id="7ff6e" class="javax.swing.JSeparator" binding="dockerImageJSeparator">
+ <constraints>
+ <grid row="9" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="-1" height="5"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <grid id="33dc2" binding="linkedActionsJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="-1" height="150"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <component id="c5701" class="javax.swing.JSeparator" binding="linkedActionsJSeparator">
+ <constraints>
+ <grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <grid id="96812" binding="codeTypeJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="10" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <component id="4bebf" class="javax.swing.JSeparator" binding="codeTypeJSeparator">
+ <constraints>
+ <grid row="11" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.java
new file mode 100644
index 0000000..6f68c21
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.java
@@ -0,0 +1,413 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.utils.ParameterUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.Limits;
+import com.navercorp.openwhisk.intellij.common.whisk.model.Runtime;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.CodeExec;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.RefreshActionOrTriggerListener;
+
+import javax.swing.*;
+import javax.swing.event.ListDataListener;
+import java.awt.*;
+import java.awt.event.ItemEvent;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.intellij.uiDesigner.core.GridConstraints.*;
+
+public class ActionManagerDialogForm {
+ private static final Logger LOG = Logger.getInstance(ActionManagerDialogForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private JPanel mainJPanel;
+ private JComboBox runtimeJComboBox;
+ private JCheckBox webactionJCheckBox;
+ private JCheckBox rawHttpJCheckBox;
+ private JCheckBox customOptionHeaderJCheckBox;
+ private JCheckBox finalOptionJCheckBox;
+ private JSpinner timeoutJSpinner;
+ private JSpinner memoryJSpinner;
+ private JPanel defaultParameterJPanel;
+ private JSeparator defaultParameterJSeparator;
+ private JPanel dockerImageJPanel;
+ private JSeparator dockerImageJSeparator;
+ private JPanel linkedActionsJPanel;
+ private JSeparator linkedActionsJSeparator;
+ private JSeparator codeTypeJSeparator;
+ private JPanel codeTypeJPanel;
+
+ private DefaultParameterForm defaultParameterForm;
+ private DockerImageForm dockerImageForm;
+ private CodeTypeForm codeTypeForm;
+ private LinkedActionsForm linkedActionsForm;
+
+ private Project project;
+ private WhiskAuth whiskAuth;
+ private ExecutableWhiskAction action;
+
+ private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+
+ public ActionManagerDialogForm(Project project, WhiskAuth auth, ExecutableWhiskAction action, List<WhiskActionMetaData> actions) {
+ this.project = project;
+ this.whiskAuth = auth;
+ this.action = action;
+
+ // add linked actions panel
+ linkedActionsForm = new LinkedActionsForm(project, action.getNamespace().split("/")[0], actions, action.getExec().getComponents());
+ linkedActionsJPanel.add(linkedActionsForm.getContent(), BorderLayout.CENTER);
+
+ // add docker image panel
+ dockerImageForm = new DockerImageForm();
+ dockerImageJPanel.add(dockerImageForm.getContent(), BorderLayout.CENTER);
+
+ // add code type panel
+ codeTypeForm = new CodeTypeForm();
+ codeTypeJPanel.add(codeTypeForm.getContent(), BorderLayout.CENTER);
+
+ // add default parameter panel
+ defaultParameterForm = new DefaultParameterForm();
+ defaultParameterJPanel.add(defaultParameterForm.getContent(), BorderLayout.CENTER);
+
+ switch (action.getKind()) {
+ case "sequence":
+ // remove docker image panel
+ mainJPanel.remove(dockerImageJPanel);
+ mainJPanel.remove(dockerImageJSeparator);
+
+ // remove code type panel
+ mainJPanel.remove(codeTypeJPanel);
+ mainJPanel.remove(codeTypeJSeparator);
+
+ // remove default parameter panel
+ mainJPanel.remove(defaultParameterJPanel);
+ mainJPanel.remove(defaultParameterJSeparator);
+ break;
+ case "blackbox":
+ // remove linked actions panel
+ mainJPanel.remove(linkedActionsJPanel);
+ mainJPanel.remove(linkedActionsJSeparator);
+ break;
+ default: // normal
+ // remove linked actions panel
+ mainJPanel.remove(linkedActionsJPanel);
+ mainJPanel.remove(linkedActionsJSeparator);
+
+ // remove docker image panel
+ mainJPanel.remove(dockerImageJPanel);
+ mainJPanel.remove(dockerImageJSeparator);
+
+ // remove code type panel
+ mainJPanel.remove(codeTypeJPanel);
+ mainJPanel.remove(codeTypeJSeparator);
+ }
+
+ runtimeJComboBox.setModel(new ComboBoxModel<Runtime>() {
+ private Runtime selected;
+
+ @Override
+ public void setSelectedItem(Object anItem) {
+ selected = (Runtime) anItem;
+ }
+
+ @Override
+ public Runtime getSelectedItem() {
+ return this.selected;
+ }
+
+ @Override
+ public int getSize() {
+ return Runtime.values().length;
+ }
+
+ @Override
+ public Runtime getElementAt(int index) {
+ return Runtime.toRuntime(index);
+ }
+
+ @Override
+ public void addListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+
+ @Override
+ public void removeListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+ });
+
+ if (!webactionJCheckBox.isSelected()) {
+ // raw http
+ rawHttpJCheckBox.setEnabled(false);
+ // custom option header
+ customOptionHeaderJCheckBox.setEnabled(false);
+ // final option
+ finalOptionJCheckBox.setEnabled(false);
+ }
+
+ webactionJCheckBox.addChangeListener(e -> {
+ AbstractButton abstractButton = (AbstractButton) e.getSource();
+ ButtonModel buttonModel = abstractButton.getModel();
+
+ if (buttonModel.isSelected()) {
+ // raw http
+ rawHttpJCheckBox.setEnabled(true);
+ // custom option header
+ customOptionHeaderJCheckBox.setEnabled(true);
+ // final option
+ finalOptionJCheckBox.setEnabled(true);
+ } else {
+ // raw http
+ rawHttpJCheckBox.setSelected(false);
+ rawHttpJCheckBox.setEnabled(false);
+ // custom option header
+ customOptionHeaderJCheckBox.setSelected(false);
+ customOptionHeaderJCheckBox.setEnabled(false);
+ // final option
+ finalOptionJCheckBox.setSelected(false);
+ finalOptionJCheckBox.setEnabled(false);
+ }
+ });
+
+ /**
+ * Set Value
+ */
+ // runtime
+ runtimeJComboBox.setSelectedItem(Runtime.toRuntime(action.getKind()));
+ // timeout
+ timeoutJSpinner.setValue(action.getLimits().getTimeout());
+ // memory
+ memoryJSpinner.setValue(action.getLimits().getMemory());
+ // webaction
+ webactionJCheckBox.setSelected(action.isWebAction());
+ // raw http
+ rawHttpJCheckBox.setSelected(action.isRawHttp());
+ // custom option header
+ customOptionHeaderJCheckBox.setSelected(action.isCustomOptions());
+ // final option
+ finalOptionJCheckBox.setSelected(action.isFinalDefaultParameter());
+
+ try {
+ // default parameter
+ defaultParameterForm.setDefaultParameter(JsonParserUtils.writeParameterToJson(action.getParameters()));
+ } catch (IOException e) {
+ LOG.error("Failed to parse json: " + action.getFullyQualifiedName(), e);
+ }
+ // docker image url
+ dockerImageForm.setDockerImage(action.getExec().getImage());
+ // code type
+ action.getCodeType().ifPresent(codeType -> codeTypeForm.setCodeType(codeType));
+
+ /**
+ * Set runtime event
+ */
+ runtimeJComboBox.addItemListener(e -> {
+ if (e.getStateChange() != ItemEvent.SELECTED) {
+ return;
+ }
+
+ switch ((Runtime) e.getItem()) {
+ case SEQUENCE:
+ removeAllPanel();
+
+ mainJPanel.add(linkedActionsJPanel, new GridConstraints(7, 0, 1, 1,
+ SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+ new Dimension(-1, -1), new Dimension(-1, 150), new Dimension(-1, -1), 0));
+ mainJPanel.add(linkedActionsJSeparator, new GridConstraints(8, 0, 1, 1,
+ SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+ new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, 5), 0));
+
+ mainJPanel.updateUI();
+ break;
+ case DOCKER:
+ removeAllPanel();
+
+ mainJPanel.add(dockerImageJPanel, new GridConstraints(7, 0, 1, 1,
+ SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+ new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, -1), 0));
+ mainJPanel.add(dockerImageJSeparator, new GridConstraints(8, 0, 1, 1,
+ SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+ new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, 5), 0));
+
+ mainJPanel.add(codeTypeJPanel, new GridConstraints(9, 0, 1, 1,
+ SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+ new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, -1), 0));
+ mainJPanel.add(codeTypeJSeparator, new GridConstraints(10, 0, 1, 1,
+ SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+ new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, 5), 0));
+
+ mainJPanel.add(defaultParameterJPanel, new GridConstraints(11, 0, 1, 1,
+ SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+ new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, -1), 0));
+ mainJPanel.add(defaultParameterJSeparator, new GridConstraints(12, 0, 1, 1,
+ SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+ new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, 5), 0));
+
+ mainJPanel.updateUI();
+ break;
+ default: // normal
+ removeAllPanel();
+
+ mainJPanel.add(defaultParameterJPanel, new GridConstraints(7, 0, 1, 1,
+ SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+ new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, -1), 0));
+ mainJPanel.add(defaultParameterJSeparator, new GridConstraints(8, 0, 1, 1,
+ SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+ new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, 5), 0));
+
+ mainJPanel.updateUI();
+ }
+ });
+ }
+
+ private void removeAllPanel() {
+ // remove linked actions panel
+ mainJPanel.remove(linkedActionsJPanel);
+ mainJPanel.remove(linkedActionsJSeparator);
+
+ // remove docker image panel
+ mainJPanel.remove(dockerImageJPanel);
+ mainJPanel.remove(dockerImageJSeparator);
+
+ // remove code type panel
+ mainJPanel.remove(codeTypeJPanel);
+ mainJPanel.remove(codeTypeJSeparator);
+
+ // remove default parameter panel
+ mainJPanel.remove(defaultParameterJPanel);
+ mainJPanel.remove(defaultParameterJSeparator);
+ }
+
+ public void updateAction() {
+ try {
+ // parameters
+ Optional<String> params = getParameter();
+ if (!params.isPresent()) {
+ NOTIFIER.notify(project, "The json format of the parameter is incorrect.", NotificationType.ERROR);
+ return;
+ }
+ List<Map<String, Object>> validParams = ParameterUtils.mapToListMap(JsonParserUtils.parseMap(params.get()));
+ // timeout
+ int timeout = (Integer) timeoutJSpinner.getValue();
+ // memory
+ int memory = (Integer) memoryJSpinner.getValue();
+ Limits limits = action.getLimits();
+ limits.setTimeout(timeout);
+ limits.setMemory(memory);
+ // runtime
+ Runtime runtime = (Runtime) runtimeJComboBox.getSelectedItem();
+ CodeExec exec = createExec(action, runtime);
+ // web action
+ boolean web = webactionJCheckBox.isSelected();
+ boolean rawHttp = rawHttpJCheckBox.isSelected();
+ boolean customOption = customOptionHeaderJCheckBox.isSelected();
+ boolean finalDefaultParameter = finalOptionJCheckBox.isSelected();
+ // code type
+ Optional<String> codeType = getCodeType(runtime);
+ // annotations
+ List<Map<String, Object>> annotations = annotationToCollection(action, web, rawHttp, customOption, finalDefaultParameter, codeType);
+
+ // payload
+ Map<String, Object> payload = new LinkedHashMap<>();
+ payload.put("namespace", action.getNamespacePath());
+ payload.put("name", action.getWhiskPackage().map(pkg -> pkg + "/" + action.getName()).orElse(action.getName()));
+ payload.put("parameters", validParams);
+ payload.put("annotations", annotations);
+ payload.put("limits", limits);
+ payload.put("exec", exec);
+ whiskActionService.updateWhiskAction(whiskAuth, action, payload).ifPresent(updated -> {
+ NOTIFIER.notify(project, "Action update succeeded: " + updated.getFullyQualifiedName(), NotificationType.INFORMATION);
+ EventUtils.publish(project, RefreshActionOrTriggerListener.TOPIC, RefreshActionOrTriggerListener::fetchActionMetadata);
+ EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+ });
+ } catch (IOException e) {
+ String msg = "Failed to update action: " + action.getFullyQualifiedName();
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }
+
+ private Optional<String> getCodeType(Runtime runtime) {
+ switch (runtime) {
+ case DOCKER:
+ Runtime codeType = codeTypeForm.getSelectedCodeType();
+ return Optional.ofNullable(codeType.toString());
+ default:
+ return Optional.empty();
+ }
+ }
+
+ private Optional<String> getParameter() {
+ if (defaultParameterForm == null) {
+ return Optional.of("{}");
+ }
+ return ParameterUtils.validateParams(defaultParameterForm.getDefaultParameter());
+ }
+
+ private CodeExec createExec(ExecutableWhiskAction executableWhiskAction, Runtime runtime) {
+ CodeExec codeExec = executableWhiskAction.getExec();
+ codeExec.setKind(runtime.toString());
+
+ switch (runtime) {
+ case SEQUENCE:
+ codeExec.setComponents(linkedActionsForm.getComponents());
+ break;
+ case DOCKER:
+ codeExec.setImage(dockerImageForm.getDockerImage());
+ break;
+ default: // normal
+ }
+
+ return codeExec;
+ }
+
+ private List<Map<String, Object>> annotationToCollection(ExecutableWhiskAction executableWhiskAction,
+ boolean web,
+ boolean rawHttp,
+ boolean customOption,
+ boolean finalDefaultParameter,
+ Optional<String> codeType) {
+ Map<String, Object> annotations = ParameterUtils.listMapToMap(executableWhiskAction.getAnnotations());
+ annotations.put("web-export", web);
+ annotations.put("raw-http", rawHttp);
+ annotations.put("web-custom-options", customOption);
+ annotations.put("final", finalDefaultParameter);
+ codeType.map(ct -> annotations.put("code-type", ct));
+ return ParameterUtils.mapToListMap(annotations);
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.form
new file mode 100644
index 0000000..e4c6cab
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.form
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.CodeTypeForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="811" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="16beb" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Code Type"/>
+ </properties>
+ </component>
+ <component id="2ec8b" class="javax.swing.JComboBox" binding="codeTypeJComboBox">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="700" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="af638" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.java
new file mode 100644
index 0000000..3314167
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.Runtime;
+
+import javax.swing.*;
+import javax.swing.event.ListDataListener;
+
+public class CodeTypeForm {
+ private JPanel mainJPanel;
+ private JComboBox codeTypeJComboBox;
+
+ public CodeTypeForm() {
+
+ codeTypeJComboBox.setModel(new ComboBoxModel<Runtime>() {
+ private Runtime selected;
+
+ @Override
+ public void setSelectedItem(Object anItem) {
+ selected = (Runtime) anItem;
+ }
+
+ @Override
+ public Object getSelectedItem() {
+ return this.selected;
+ }
+
+ @Override
+ public int getSize() {
+ // remove blackbox, sequence
+ return Runtime.values().length - 2;
+ }
+
+ @Override
+ public Runtime getElementAt(int index) {
+ return Runtime.toCodeType(index);
+ }
+
+ @Override
+ public void addListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+
+ @Override
+ public void removeListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+ });
+ }
+
+ public void setCodeType(String codeType) {
+ Runtime runtime = Runtime.toRuntime(codeType);
+ switch (runtime) {
+ case SEQUENCE:
+ case DOCKER:
+ break;
+ default: // normal
+ codeTypeJComboBox.setSelectedItem(runtime);
+ }
+ }
+
+ public Runtime getSelectedCodeType() {
+ return (Runtime) codeTypeJComboBox.getSelectedItem();
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.form
new file mode 100644
index 0000000..4c08f09
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.form
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.DefaultParameterForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="dc471" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Default Parameter"/>
+ </properties>
+ </component>
+ <scrollpane id="c704a">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="700" height="150"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="e3336" class="javax.swing.JTextArea" binding="defaultParameterJTextArea">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ <component id="146a8" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.java
new file mode 100644
index 0000000..bcdac1e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import javax.swing.*;
+
+public class DefaultParameterForm {
+ private JPanel mainJPanel;
+ private JTextArea defaultParameterJTextArea;
+
+ public DefaultParameterForm() {
+
+ }
+
+ public void setDefaultParameter(String parameter) {
+ defaultParameterJTextArea.setText(parameter);
+ }
+
+ public String getDefaultParameter() {
+ return defaultParameterJTextArea.getText();
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.form
new file mode 100644
index 0000000..45720b1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.form
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.DockerImageForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="724b3" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Image"/>
+ </properties>
+ </component>
+ <component id="8e07" class="javax.swing.JTextField" binding="dockerImageJTextField">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="700" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="c87" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.java
new file mode 100644
index 0000000..c07ecab
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import javax.swing.*;
+
+public class DockerImageForm {
+ private JTextField dockerImageJTextField;
+ private JPanel mainJPanel;
+
+ public DockerImageForm() {
+
+ }
+
+ public void setDockerImage(String dockerImage) {
+ dockerImageJTextField.setText(dockerImage);
+ }
+
+ public String getDockerImage() {
+ return dockerImageJTextField.getText();
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.form
new file mode 100644
index 0000000..3050ceb
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.form
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.LinkedActionsEntryForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="ac3fe" class="javax.swing.JLabel" binding="indexJLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="50" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ <component id="5ae4" class="javax.swing.JTextField" binding="actionJTextField">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="360" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="89105" class="javax.swing.JButton" binding="upJButton">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="30" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ <component id="afc8f" class="javax.swing.JButton" binding="downJButton">
+ <constraints>
+ <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="30" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ <component id="fa2ee" class="javax.swing.JButton" binding="removeJButton">
+ <constraints>
+ <grid row="0" column="4" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="30" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.java
new file mode 100644
index 0000000..9bb5739
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.java
@@ -0,0 +1,98 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.listener.DownActionListener;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.listener.RemoveActionListener;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.listener.UpActionListener;
+
+import javax.swing.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import static com.intellij.icons.AllIcons.General.Remove;
+import static com.navercorp.openwhisk.intellij.common.Icons.ARROW_DOWN;
+import static com.navercorp.openwhisk.intellij.common.Icons.ARROW_UP;
+
+public class LinkedActionsEntryForm {
+ private static final Logger LOG = Logger.getInstance(LinkedActionsEntryForm.class);
+
+ private JPanel mainJPanel;
+ private JTextField actionJTextField;
+ private JButton upJButton;
+ private JButton downJButton;
+ private JButton removeJButton;
+ private JLabel indexJLabel;
+
+ private String namespace;
+
+ public LinkedActionsEntryForm(int index,
+ String namespace,
+ String actionName,
+ UpActionListener upActionListener,
+ DownActionListener downActionListener,
+ RemoveActionListener removeActionListener) {
+ this.namespace = namespace;
+
+ indexJLabel.setText(String.valueOf(index));
+ indexJLabel.setHorizontalAlignment(SwingConstants.CENTER);
+ setActionName(actionName);
+
+ upJButton.setIcon(ARROW_UP);
+ upJButton.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ upActionListener.upAction(Integer.parseInt(indexJLabel.getText()));
+ }
+ });
+
+ downJButton.setIcon(ARROW_DOWN);
+ downJButton.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ downActionListener.downAction(Integer.parseInt(indexJLabel.getText()));
+ }
+ });
+
+ removeJButton.setIcon(Remove);
+ removeJButton.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ removeActionListener.removeAction(Integer.parseInt(indexJLabel.getText()));
+ }
+ });
+ }
+
+ private void setActionName(String actionName) {
+ String name = actionName.replaceAll("/" + namespace + "/", "");
+ actionJTextField.setText(name);
+ }
+
+ public String getActionName() {
+ return "/" + namespace + "/" + actionJTextField.getText();
+ }
+
+ public void setIndex(int index) {
+ indexJLabel.setText(String.valueOf(index));
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.form
new file mode 100644
index 0000000..078e2f7
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.form
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.LinkedActionsForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="960cb" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Linked Actions"/>
+ </properties>
+ </component>
+ <scrollpane id="70411">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="700" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="db8f" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints/>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="5365b" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="50" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Action"/>
+ </properties>
+ </component>
+ <component id="7c1b7" class="javax.swing.JComboBox" binding="actionsJComboBox">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="520" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="56534" class="javax.swing.JButton" binding="addJButton">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="30" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ <grid id="83040" binding="linkedActionsJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ </children>
+ </grid>
+ </children>
+ </scrollpane>
+ <component id="278f2" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.java
new file mode 100644
index 0000000..4d6fc35
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.java
@@ -0,0 +1,180 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import javax.swing.*;
+import javax.swing.event.ListDataListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.intellij.icons.AllIcons.General.Add;
+
+public class LinkedActionsForm {
+ private static final Logger LOG = Logger.getInstance(LinkedActionsForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private JPanel mainJPanel;
+ private JComboBox actionsJComboBox;
+ private JButton addJButton;
+ private JPanel linkedActionsJPanel;
+
+ private Project project;
+ private List<LinkedActionsEntryForm> cachedActions = new ArrayList<>();
+
+ public LinkedActionsForm(Project project, String namespace, List<WhiskActionMetaData> actions, List<String> components) {
+ this.project = project;
+
+ for (int i = 0; i < components.size(); i++) {
+ addLinkedAction(i, namespace, components.get(i));
+ }
+
+ actionsJComboBox.setModel(new ComboBoxModel() {
+ private WhiskActionMetaData selected;
+
+ @Override
+ public void setSelectedItem(Object anItem) {
+ selected = (WhiskActionMetaData) anItem;
+ }
+
+ @Override
+ public Object getSelectedItem() {
+ return selected;
+ }
+
+ @Override
+ public int getSize() {
+ return actions.size();
+ }
+
+ @Override
+ public Object getElementAt(int index) {
+ return actions.get(index);
+ }
+
+ @Override
+ public void addListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+
+ @Override
+ public void removeListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+ });
+
+ addJButton.setIcon(Add);
+ addJButton.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (actionsJComboBox.getSelectedItem() == null) {
+ return;
+ }
+
+ WhiskActionMetaData action = (WhiskActionMetaData) actionsJComboBox.getSelectedItem();
+ String actionName = action.getWhiskPackage().map(pkg -> pkg + "/" + action.getName()).orElse(action.getName());
+ addLinkedAction(cachedActions.size(), namespace, actionName);
+ }
+ });
+ }
+
+ public List<String> getComponents() {
+ return cachedActions.stream().map(LinkedActionsEntryForm::getActionName).collect(Collectors.toList());
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+
+ /**
+ * Helper functions.
+ */
+ private void addLinkedAction(int index, String namespace, String actionName) {
+ LinkedActionsEntryForm entry = new LinkedActionsEntryForm(index, namespace, actionName,
+ this::upLinkedAction,
+ this::downLinkedAction,
+ this::removeLinkedAction);
+ linkedActionsJPanel.setLayout(new BoxLayout(linkedActionsJPanel, BoxLayout.Y_AXIS));
+ linkedActionsJPanel.add(entry.getContent());
+ cachedActions.add(entry);
+ linkedActionsJPanel.updateUI();
+ }
+
+ private void upLinkedAction(int index) {
+ if (index <= 0) {
+ return;
+ }
+
+ for (int i = index - 1; i < cachedActions.size(); i++) {
+ linkedActionsJPanel.remove(cachedActions.get(i).getContent());
+ }
+
+ Collections.swap(cachedActions, index - 1, index);
+
+ for (int i = index - 1; i < cachedActions.size(); i++) {
+ LinkedActionsEntryForm l = cachedActions.get(i);
+ l.setIndex(i);
+ linkedActionsJPanel.add(l.getContent());
+ }
+
+ linkedActionsJPanel.updateUI();
+ }
+
+ private void downLinkedAction(int index) {
+ if (index >= cachedActions.size() - 1) {
+ return;
+ }
+
+ for (int i = index; i < cachedActions.size(); i++) {
+ linkedActionsJPanel.remove(cachedActions.get(i).getContent());
+ }
+
+ Collections.swap(cachedActions, index, index + 1);
+
+ for (int i = index; i < cachedActions.size(); i++) {
+ LinkedActionsEntryForm l = cachedActions.get(i);
+ l.setIndex(i);
+ linkedActionsJPanel.add(l.getContent());
+ }
+
+ linkedActionsJPanel.updateUI();
+ }
+
+ private void removeLinkedAction(int index) {
+ if (cachedActions.size() <= 1) {
+ NOTIFIER.notify(project, "Sequence actions require at least one action.", NotificationType.WARNING);
+ return;
+ }
+
+ linkedActionsJPanel.remove(cachedActions.get(index).getContent());
+ cachedActions.remove(index);
+ for (int i = index; i < cachedActions.size(); i++) {
+ cachedActions.get(i).setIndex(i);
+ }
+
+ linkedActionsJPanel.updateUI();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/AddEndpointDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/AddEndpointDialog.java
new file mode 100644
index 0000000..f3bbf3d
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/AddEndpointDialog.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.endpoint;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui.AddEndpointDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class AddEndpointDialog extends DialogWrapperWithApply {
+
+ private AddEndpointDialogForm addEndpointDialogForm;
+
+ public AddEndpointDialog(Project project) {
+ super(project, true); // use current window as parent
+ addEndpointDialogForm = new AddEndpointDialogForm(project);
+ setTitle("Add Endpoint");
+ setResizable(false);
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ if (addEndpointDialogForm != null) {
+ return addEndpointDialogForm.getContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void doApplyAction() {
+ if (addEndpointDialogForm != null) {
+ addEndpointDialogForm.addEndpoint();
+ }
+ super.doApplyAction();
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (addEndpointDialogForm != null && !myApplyAction.isApplied()) {
+ addEndpointDialogForm.addEndpoint();
+ }
+ super.doOKAction();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/DeleteEndpointDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/DeleteEndpointDialog.java
new file mode 100644
index 0000000..f0bd398
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/DeleteEndpointDialog.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.endpoint;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DeleteEndpointDialog extends DialogWrapper {
+
+ private WhiskEndpoint whiskEndpoint;
+
+ public DeleteEndpointDialog(Project project, WhiskEndpoint whiskEndpoint) {
+ super(true); // use current window as parent
+ setTitle("Delete Endpoint");
+ setResizable(false);
+ this.whiskEndpoint = whiskEndpoint;
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ JPanel dialogPanel = new JPanel(new BorderLayout());
+
+ JLabel label = new JLabel("Are you sure you want to delete " + whiskEndpoint.getAlias() + " endpoint?");
+ label.setPreferredSize(new Dimension(100, 30));
+ dialogPanel.add(label, BorderLayout.CENTER);
+
+ return dialogPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/EditEndpointDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/EditEndpointDialog.java
new file mode 100644
index 0000000..78b8a87
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/EditEndpointDialog.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.endpoint;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui.EditEndpointDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class EditEndpointDialog extends DialogWrapperWithApply {
+
+ private EditEndpointDialogForm editEndpointDialogForm;
+
+ public EditEndpointDialog(Project project, WhiskEndpoint whiskEndpoint) {
+ super(project, true); // use current window as parent
+ editEndpointDialogForm = new EditEndpointDialogForm(project, whiskEndpoint);
+ setTitle("Edit Endpoint");
+ setResizable(false);
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ if (editEndpointDialogForm != null) {
+ return this.editEndpointDialogForm.getContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void doApplyAction() {
+ if (editEndpointDialogForm != null) {
+ editEndpointDialogForm.updateEndpoint();
+ }
+ super.doApplyAction();
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (editEndpointDialogForm != null && !myApplyAction.isApplied()) {
+ editEndpointDialogForm.updateEndpoint();
+ }
+ super.doOKAction();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.form
new file mode 100644
index 0000000..3392fc4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.form
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui.AddEndpointDialogForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="401ea" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="6e866" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="100" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Alias"/>
+ </properties>
+ </component>
+ <component id="330f9" class="javax.swing.JTextField" binding="aliasJTextField">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="366c6" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="11db1" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="34574" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="100" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Apihost"/>
+ </properties>
+ </component>
+ <component id="5bf65" class="javax.swing.JTextField" binding="apihostJTextField">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="f57a8" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <component id="8df6d" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="7794b" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.java
new file mode 100644
index 0000000..669b5b2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class AddEndpointDialogForm {
+ private static final Logger LOG = Logger.getInstance(AddEndpointDialogForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private JPanel mainJPanel;
+ private JTextField aliasJTextField;
+ private JTextField apihostJTextField;
+
+ private WhiskService whiskService;
+ private List<WhiskEndpoint> endpoints;
+
+ private Project project;
+
+ public AddEndpointDialogForm(Project project) {
+ this.project = project;
+
+ /**
+ * Load endpoints
+ */
+ this.whiskService = ServiceManager.getService(project, WhiskService.class);
+ try {
+ endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+ } catch (IOException e) {
+ LOG.error("Endpoint parsing failed", e);
+ }
+
+ }
+
+ public void addEndpoint() {
+ final String alias = aliasJTextField.getText().trim();
+ final String apihost = apihostJTextField.getText().trim(); // TODO validate api host
+
+ if (existAlias(endpoints, alias)) {
+ NOTIFIER.notify(project, "Failed to added endpoint: " + alias + " already exists.", NotificationType.ERROR);
+ } else {
+ endpoints.add(new WhiskEndpoint(alias, apihost, new ArrayList<>()));
+ saveEndpoints(endpoints);
+
+ // Update action tree
+ EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+ NOTIFIER.notify(project, alias + " has been added successfully.", NotificationType.INFORMATION);
+ }
+ }
+
+ private boolean existAlias(List<WhiskEndpoint> whiskEndpoints, String alias) {
+ for (WhiskEndpoint ep : whiskEndpoints) {
+ if (ep.getAlias().equals(alias)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void saveEndpoints(List<WhiskEndpoint> newEndpoints) {
+ try {
+ String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+ whiskService.setEndpoints(eps);
+ whiskService.loadState(whiskService);
+ } catch (JsonProcessingException e) {
+ LOG.error("Endpoint parsing failed", e);
+ }
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.form
new file mode 100644
index 0000000..82f636a
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.form
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui.EditEndpointDialogForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="3b01a" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="4ed82" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="100" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Alias"/>
+ </properties>
+ </component>
+ <component id="e975e" class="javax.swing.JTextField" binding="aliasJTextField">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="3d5e6" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="a4647" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="dc17c" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="100" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Apihost"/>
+ </properties>
+ </component>
+ <component id="f76a8" class="javax.swing.JTextField" binding="apihostJTextField">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="eb8b4" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <component id="320bb" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="78128" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.java
new file mode 100644
index 0000000..8714a4d
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class EditEndpointDialogForm {
+ private static final Logger LOG = Logger.getInstance(EditEndpointDialogForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private JPanel mainJPanel;
+ private JTextField aliasJTextField;
+ private JTextField apihostJTextField;
+
+ private WhiskService whiskService;
+ private List<WhiskEndpoint> endpoints;
+
+ private Project project;
+ private WhiskEndpoint whiskEndpoint;
+
+ public EditEndpointDialogForm(Project project, WhiskEndpoint whiskEndpoint) {
+ this.project = project;
+ this.whiskEndpoint = whiskEndpoint;
+
+ /**
+ * Load endpoints
+ */
+ this.whiskService = ServiceManager.getService(project, WhiskService.class);
+ try {
+ endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+ } catch (IOException e) {
+ LOG.error("Endpoint parsing failed", e);
+ }
+
+ aliasJTextField.setText(whiskEndpoint.getAlias());
+ apihostJTextField.setText(whiskEndpoint.getApihost());
+ }
+
+ public void updateEndpoint() {
+ final String alias = aliasJTextField.getText().trim();
+ final String apihost = apihostJTextField.getText().trim(); // TODO validate api host
+
+ if (existAlias(endpoints, whiskEndpoint.getAlias(), alias)) {
+ NOTIFIER.notify(project, "Failed to update endpoint: " + alias + " already exists.", NotificationType.ERROR);
+ } else {
+ List<WhiskEndpoint> newEndpoints = updateWhiskEndpoint(endpoints,
+ whiskEndpoint.getAlias(),
+ new WhiskEndpoint(alias, apihost, whiskEndpoint.getNamespaces()));
+ saveEndpoints(newEndpoints);
+
+ // Update action tree
+ EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+ NOTIFIER.notify(project, alias + " has been added successfully.", NotificationType.INFORMATION);
+ }
+ }
+
+ private List<WhiskEndpoint> updateWhiskEndpoint(List<WhiskEndpoint> eps, String key, WhiskEndpoint newEndpoint) {
+ for (int i = 0; i < eps.size(); i++) {
+ if (eps.get(i).getAlias().equals(key)) {
+ eps.set(i, newEndpoint);
+ }
+ }
+ return eps;
+ }
+
+ private boolean existAlias(List<WhiskEndpoint> whiskEndpoints, String originAlias, String alias) {
+ return whiskEndpoints.stream()
+ .filter(ep -> !ep.getAlias().equals(originAlias)) // exclude current endpoint
+ .anyMatch(ep -> ep.getAlias().equals(alias)); // find duplicated alias
+ }
+
+ private void saveEndpoints(List<WhiskEndpoint> newEndpoints) {
+ try {
+ String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+ whiskService.setEndpoints(eps);
+ whiskService.loadState(whiskService);
+ } catch (JsonProcessingException e) {
+ LOG.error("Endpoint parsing failed", e);
+ }
+ }
+
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/AddNamespaceDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/AddNamespaceDialog.java
new file mode 100644
index 0000000..bbab99f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/AddNamespaceDialog.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.namespace;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui.AddNamespaceDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class AddNamespaceDialog extends DialogWrapperWithApply {
+
+ private AddNamespaceDialogForm addNamespaceDialogForm;
+
+ public AddNamespaceDialog(Project project, WhiskEndpoint whiskEndpoint) {
+ super(project, true); // use current window as parent
+ addNamespaceDialogForm = new AddNamespaceDialogForm(project, whiskEndpoint);
+ setTitle("Add Namespace");
+ setResizable(false);
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ if (addNamespaceDialogForm != null) {
+ return addNamespaceDialogForm.getContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void doApplyAction() {
+ if (addNamespaceDialogForm != null) {
+ addNamespaceDialogForm.addNamespace();
+ }
+ super.doApplyAction();
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (addNamespaceDialogForm != null && !myApplyAction.isApplied()) {
+ addNamespaceDialogForm.addNamespace();
+ }
+ super.doOKAction();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/DeleteNamespaceDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/DeleteNamespaceDialog.java
new file mode 100644
index 0000000..b12b080
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/DeleteNamespaceDialog.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.namespace;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DeleteNamespaceDialog extends DialogWrapper {
+
+ private WhiskNamespace whiskNamespace;
+
+ public DeleteNamespaceDialog(Project project, WhiskNamespace whiskNamespace) {
+ super(true); // use current window as parent
+ setTitle("Delete Namespace");
+ setResizable(false);
+ this.whiskNamespace = whiskNamespace;
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ JPanel dialogPanel = new JPanel(new BorderLayout());
+
+ JLabel label = new JLabel("Are you sure you want to delete " + whiskNamespace.getPath() + " namespace?");
+ label.setPreferredSize(new Dimension(100, 30));
+ dialogPanel.add(label, BorderLayout.CENTER);
+
+ return dialogPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/EditNamespaceDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/EditNamespaceDialog.java
new file mode 100644
index 0000000..59a49e3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/EditNamespaceDialog.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.namespace;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui.EditNamespaceDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class EditNamespaceDialog extends DialogWrapperWithApply {
+
+ private EditNamespaceDialogForm editNamespaceDialogForm;
+
+ public EditNamespaceDialog(Project project, WhiskAuth whiskAuth, WhiskNamespace whiskNamespace) {
+ super(project, true); // use current window as parent
+ editNamespaceDialogForm = new EditNamespaceDialogForm(project, whiskAuth, whiskNamespace);
+ setTitle("Edit Namespace");
+ setResizable(false);
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ if (editNamespaceDialogForm != null) {
+ return this.editNamespaceDialogForm.getContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void doApplyAction() {
+ if (editNamespaceDialogForm != null) {
+ editNamespaceDialogForm.updateNamespace();
+ }
+ super.doApplyAction();
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (editNamespaceDialogForm != null && !myApplyAction.isApplied()) {
+ editNamespaceDialogForm.updateNamespace();
+ }
+ super.doOKAction();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.form
new file mode 100644
index 0000000..d9cd4bd
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.form
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui.AddNamespaceDialogForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="569" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="48a1a" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="3d4" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Auth Key"/>
+ </properties>
+ </component>
+ <component id="2385f" class="javax.swing.JPasswordField" binding="authKeyJPasswordField">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="700" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="1a120" class="javax.swing.JCheckBox" binding="showAuthKeyJCheckBox">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="100" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Show Key"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="4ab28" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="1094b" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="6c796" class="javax.swing.JButton" binding="verifyAuthKeyJButton">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ <maximum-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Verify AuthKey"/>
+ </properties>
+ </component>
+ <component id="1be9" class="javax.swing.JLabel" binding="verifyResultJLabel">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="d2d81" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ </children>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.java
new file mode 100644
index 0000000..1f2cbc5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.JBColor;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskNamespaceService;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+
+import javax.swing.*;
+import java.awt.event.ItemEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class AddNamespaceDialogForm {
+ private static final Logger LOG = Logger.getInstance(AddNamespaceDialogForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private JPanel mainJPanel;
+ private JPasswordField authKeyJPasswordField;
+ private JButton verifyAuthKeyJButton;
+ private JLabel verifyResultJLabel;
+ private JCheckBox showAuthKeyJCheckBox;
+
+ private WhiskNamespaceService whiskNamespaceService = WhiskNamespaceService.getInstance();
+ private WhiskService whiskService;
+ private List<WhiskEndpoint> endpoints;
+
+ private Project project;
+ private WhiskEndpoint whiskEndpoint;
+
+ public AddNamespaceDialogForm(Project project, WhiskEndpoint whiskEndpoint) {
+ this.project = project;
+ this.whiskEndpoint = whiskEndpoint;
+
+ /**
+ * Load endpoints
+ */
+ this.whiskService = ServiceManager.getService(project, WhiskService.class);
+ try {
+ endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+ } catch (IOException e) {
+ LOG.error("Endpoint parsing failed", e);
+ }
+
+ verifyAuthKeyJButton.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ final String auth = new String(authKeyJPasswordField.getPassword());
+ final String apihost = whiskEndpoint.getApihost();
+ final WhiskAuth newWhiskAuth = new WhiskAuth(auth, apihost);
+ Optional<WhiskNamespace> result = whiskNamespaceService.validateNamespace(newWhiskAuth);
+ if (result.isPresent()) {
+ verifyResultJLabel.setText("Success");
+ verifyResultJLabel.setForeground(JBColor.GREEN);
+ } else {
+ verifyResultJLabel.setText("Invalid authkey");
+ verifyResultJLabel.setForeground(JBColor.RED);
+ }
+ }
+ });
+
+ showAuthKeyJCheckBox.addItemListener(e -> {
+ if (e.getStateChange() == ItemEvent.SELECTED) {
+ authKeyJPasswordField.setEchoChar((char) 0);
+ } else {
+ authKeyJPasswordField.setEchoChar('*');
+ }
+ });
+ }
+
+ public void addNamespace() {
+ final String auth = new String(authKeyJPasswordField.getPassword());
+ final String apihost = whiskEndpoint.getApihost();
+ final WhiskAuth newWhiskAuth = new WhiskAuth(auth, apihost);
+ Optional<WhiskNamespace> result = whiskNamespaceService.validateNamespace(newWhiskAuth);
+ if (result.isPresent()) {
+ WhiskNamespace whiskNamespace = result.get();
+ List<WhiskEndpoint> newEndpoints = addNamespace(whiskEndpoint, whiskNamespace);
+ saveEndpoints(newEndpoints);
+
+ // Update action tree
+ EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+ NOTIFIER.notify(project, whiskNamespace.getPath() + " has been added successfully.", NotificationType.INFORMATION);
+ } else {
+ NOTIFIER.notify(project, "Invalid authkey", NotificationType.ERROR);
+ }
+ }
+
+ private List<WhiskEndpoint> addNamespace(WhiskEndpoint endpoint, WhiskNamespace whiskNamespace) {
+ for (int i = 0; i < endpoints.size(); i++) {
+ WhiskEndpoint ep = endpoints.get(i);
+ if (ep.getAlias().equals(endpoint.getAlias()) && ep.getApihost().equals(endpoint.getApihost())) {
+ List<WhiskNamespace> namespaces = ep.getNamespaces();
+ namespaces.add(whiskNamespace);
+ ep.setNamespaces(namespaces);
+ }
+ endpoints.set(i, ep);
+ }
+ return endpoints;
+ }
+
+ private void saveEndpoints(List<WhiskEndpoint> newEndpoints) {
+ try {
+ String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+ whiskService.setEndpoints(eps);
+ whiskService.loadState(whiskService);
+ } catch (JsonProcessingException e) {
+ LOG.error("Endpoint parsing failed", e);
+ }
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.form
new file mode 100644
index 0000000..94d394c
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.form
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui.EditNamespaceDialogForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="942" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="ac30b" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false">
+ <preferred-size width="500" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="ac8d2" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="100" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Name"/>
+ </properties>
+ </component>
+ <component id="4bc0d" class="javax.swing.JTextField" binding="nameJTextField">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="700" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <editable value="false"/>
+ </properties>
+ </component>
+ <component id="c2d6a" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="5" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="259ba" layout-manager="GridLayoutManager" row-count="1" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false">
+ <preferred-size width="500" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="a750" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="100" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Auth Key"/>
+ </properties>
+ </component>
+ <component id="cc357" class="javax.swing.JPasswordField" binding="authKeyJPasswordField">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="700" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <echoChar value="*"/>
+ </properties>
+ </component>
+ <component id="ee04d" class="javax.swing.JCheckBox" binding="showAuthKeyJCheckbox">
+ <constraints>
+ <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="100" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Show Key"/>
+ </properties>
+ </component>
+ <component id="8c925" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="5" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="44877" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="true" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="500" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="db084" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="ae99f" class="javax.swing.JButton" binding="verifyAuthKeyJButton">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ <maximum-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Verify AuthKey"/>
+ </properties>
+ </component>
+ <component id="a7218" class="javax.swing.JLabel" binding="verifyResultJLabel">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <horizontalAlignment value="10"/>
+ <text value=""/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="8e824" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ </children>
+ </grid>
+ <component id="6a07a" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="ce2ed" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <hspacer id="2c3b2">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.java
new file mode 100644
index 0000000..9a363c4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.JBColor;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskNamespaceService;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+
+import javax.swing.*;
+import java.awt.event.ItemEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class EditNamespaceDialogForm {
+ private static final Logger LOG = Logger.getInstance(EditNamespaceDialogForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private JPanel mainJPanel;
+
+ private JTextField nameJTextField;
+ private JPasswordField authKeyJPasswordField;
+
+ private JButton verifyAuthKeyJButton;
+ private JLabel verifyResultJLabel;
+ private JCheckBox showAuthKeyJCheckbox;
+
+ private WhiskNamespaceService whiskNamespaceService = WhiskNamespaceService.getInstance();
+ private WhiskService whiskService;
+ private List<WhiskEndpoint> endpoints;
+
+ private Project project;
+ private WhiskAuth whiskAuth;
+ private WhiskNamespace whiskNamespace;
+
+ public EditNamespaceDialogForm(Project project, WhiskAuth whiskAuth, WhiskNamespace whiskNamespace) {
+ this.project = project;
+ this.whiskAuth = whiskAuth;
+ this.whiskNamespace = whiskNamespace;
+
+ /**
+ * Load endpoints
+ */
+ this.whiskService = ServiceManager.getService(project, WhiskService.class);
+ try {
+ endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+ } catch (IOException e) {
+ LOG.error("Endpoint parsing failed", e);
+ }
+
+ nameJTextField.setText(whiskNamespace.getPath());
+ authKeyJPasswordField.setText(whiskNamespace.getAuth());
+
+ verifyAuthKeyJButton.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ final String auth = new String(authKeyJPasswordField.getPassword());
+ final String apihost = whiskAuth.getApihost();
+ final WhiskAuth newWhiskAuth = new WhiskAuth(auth, apihost);
+ Optional<WhiskNamespace> result = whiskNamespaceService.validateNamespace(newWhiskAuth);
+ if (result.isPresent()) {
+ verifyResultJLabel.setText("Success");
+ verifyResultJLabel.setForeground(JBColor.GREEN);
+ } else {
+ verifyResultJLabel.setText("Invalid authkey");
+ verifyResultJLabel.setForeground(JBColor.RED);
+ }
+ }
+ });
+
+ showAuthKeyJCheckbox.addItemListener(e -> {
+ if (e.getStateChange() == ItemEvent.SELECTED) {
+ authKeyJPasswordField.setEchoChar((char) 0);
+ } else {
+ authKeyJPasswordField.setEchoChar('*');
+ }
+ });
+ }
+
+ public void updateNamespace() {
+ final String auth = new String(authKeyJPasswordField.getPassword());
+ final String apihost = whiskAuth.getApihost();
+ final WhiskAuth newWhiskAuth = new WhiskAuth(auth, apihost);
+ Optional<WhiskNamespace> result = whiskNamespaceService.validateNamespace(newWhiskAuth);
+ if (result.isPresent()) {
+ WhiskNamespace newNamespace = result.get();
+ List<WhiskEndpoint> newEndpoints = updateNamespace(whiskNamespace, newNamespace);
+ saveEndpoints(newEndpoints);
+ nameJTextField.setText(newNamespace.getPath());
+
+ // Update action tree
+ EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+ NOTIFIER.notify(project, whiskNamespace.getPath() + " modification was successful.", NotificationType.INFORMATION);
+ } else {
+ NOTIFIER.notify(project, "Invalid authkey", NotificationType.ERROR);
+ }
+ }
+
+ private List<WhiskEndpoint> updateNamespace(WhiskNamespace originNamespace, WhiskNamespace newNamespace) {
+ for (int i = 0; i < endpoints.size(); i++) {
+ WhiskEndpoint endpoint = endpoints.get(i);
+ List<WhiskNamespace> namespaces = endpoint.getNamespaces();
+ for (int j = 0; j < namespaces.size(); j++) {
+ if (namespaces.get(j).getPath().equals(originNamespace.getPath())) {
+ namespaces.set(j, newNamespace);
+ }
+ }
+ endpoint.setNamespaces(namespaces);
+ endpoints.set(i, endpoint);
+ }
+ return endpoints;
+ }
+
+ private void saveEndpoints(List<WhiskEndpoint> newEndpoints) {
+ try {
+ String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+ whiskService.setEndpoints(eps);
+ whiskService.loadState(whiskService);
+ } catch (JsonProcessingException e) {
+ LOG.error("Endpoint parsing failed", e);
+ }
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/DeletePackageDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/DeletePackageDialog.java
new file mode 100644
index 0000000..e2784ed
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/DeletePackageDialog.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.pkg;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DeletePackageDialog extends DialogWrapper {
+
+ private WhiskPackageWithActions whiskPackage;
+
+ public DeletePackageDialog(Project project, WhiskPackageWithActions whiskPackage) {
+ super(true); // use current window as parent
+ setTitle("Delete Package");
+ setResizable(false);
+ this.whiskPackage = whiskPackage;
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ JPanel dialogPanel = new JPanel(new BorderLayout());
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("<html>");
+ builder.append("Are you sure you want to delete " + whiskPackage.getName() + " package?");
+ if (!whiskPackage.getBinding().isPresent()) {
+ if (!whiskPackage.getActions().isEmpty()) {
+ builder.append("<br/><br/>");
+ builder.append("The following actions will be deleted together<br/>");
+ }
+ for (CompactWhiskAction action : whiskPackage.getActions()) {
+ builder.append("- " + action.getName() + "<br/>");
+ }
+ builder.append("<br/><br/>");
+ }
+ builder.append("</html>");
+
+ JLabel label = new JLabel(builder.toString());
+ label.setPreferredSize(new Dimension(400, 100));
+ dialogPanel.add(label, BorderLayout.CENTER);
+
+ return dialogPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageCreationManagerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageCreationManagerDialog.java
new file mode 100644
index 0000000..5de1397
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageCreationManagerDialog.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.pkg;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui.PackageCreationManagerDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class PackageCreationManagerDialog extends DialogWrapperWithApply {
+
+ private PackageCreationManagerDialogForm packageCreationManagerDialogForm;
+
+ public PackageCreationManagerDialog(Project project, WhiskAuth auth) {
+ super(project, true); // use current window as parent
+ packageCreationManagerDialogForm = new PackageCreationManagerDialogForm(project, auth);
+ setTitle("Create Package");
+ setResizable(false);
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ if (packageCreationManagerDialogForm != null) {
+ return packageCreationManagerDialogForm.getContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void doApplyAction() {
+ if (packageCreationManagerDialogForm != null) {
+ packageCreationManagerDialogForm.createPackage();
+ }
+ super.doApplyAction();
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (packageCreationManagerDialogForm != null && !myApplyAction.isApplied()) {
+ packageCreationManagerDialogForm.createPackage();
+ }
+ super.doOKAction();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageManagerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageManagerDialog.java
new file mode 100644
index 0000000..78a366a
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageManagerDialog.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.pkg;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui.PackageManagerDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class PackageManagerDialog extends DialogWrapperWithApply {
+
+ private PackageManagerDialogForm packageManagerDialogForm;
+
+ public PackageManagerDialog(Project project, WhiskAuth auth, WhiskPackageWithActions whiskPackage) {
+ super(project, true); // use current window as parent
+ packageManagerDialogForm = new PackageManagerDialogForm(project, auth, whiskPackage);
+ setTitle("Manage Package");
+ setResizable(false);
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ if (packageManagerDialogForm != null) {
+ return this.packageManagerDialogForm.getContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void doApplyAction() {
+ if (packageManagerDialogForm != null) {
+ packageManagerDialogForm.updatePackage();
+ }
+ super.doApplyAction();
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (packageManagerDialogForm != null && !myApplyAction.isApplied()) {
+ packageManagerDialogForm.updatePackage();
+ }
+ super.doOKAction();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.form
new file mode 100644
index 0000000..6e8a746
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.form
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui.PackageCreationManagerDialogForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="2d156" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="e6557" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="100" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Name"/>
+ </properties>
+ </component>
+ <component id="f6602" class="javax.swing.JTextField" binding="nameJTextField">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="62535" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="5" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <component id="2f681" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="-1" height="5"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.java
new file mode 100644
index 0000000..9a10c24
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskPackageService;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class PackageCreationManagerDialogForm {
+ private static final Logger LOG = Logger.getInstance(PackageCreationManagerDialogForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private JTextField nameJTextField;
+ private JPanel mainJPanel;
+
+ private WhiskPackageService whiskPackageService = WhiskPackageService.getInstance();
+
+ private Project project;
+ private WhiskAuth whiskAuth;
+
+ public PackageCreationManagerDialogForm(Project project, WhiskAuth whiskAuth) {
+ this.project = project;
+ this.whiskAuth = whiskAuth;
+ }
+
+ public void createPackage() {
+ try {
+ String name = nameJTextField.getText().trim();
+
+ Map<String, Object> payload = new LinkedHashMap<>();
+ payload.put("name", name);
+ payload.put("namespace", "_");
+
+ Optional<WhiskPackageWithActions> created = whiskPackageService.createWhiskPackage(whiskAuth, name, payload);
+ if (created.isPresent()) {
+ NOTIFIER.notify(project, name + " created", NotificationType.INFORMATION);
+ } else {
+ NOTIFIER.notify(project, name + " already exist", NotificationType.ERROR);
+ }
+ } catch (IOException e) {
+ String msg = "Failed to create package: ";
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.form
new file mode 100644
index 0000000..b7729e8
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.form
@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui.PackageManagerDialogForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="6" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="1060" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="e083a" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="800" height="30"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="67410" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="635bb" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ <maximum-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Name"/>
+ </properties>
+ </component>
+ <component id="a98f5" class="javax.swing.JLabel" binding="nameJLabel">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ <component id="faa4" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="5" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="e942a" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="818fc" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ <maximum-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Namespace"/>
+ </properties>
+ </component>
+ <component id="4c848" class="javax.swing.JLabel" binding="namespaceJPanel">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ <component id="e60d9" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="5" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <component id="73d3d" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="5" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="8ee3" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="800" height="30"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="d02f0" binding="sharedJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="58b28" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ <maximum-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Shared"/>
+ </properties>
+ </component>
+ <component id="c5ea3" class="javax.swing.JCheckBox" binding="shareJCheckBox">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ <component id="c38a4" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="5" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="d76d" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="a3d13" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ <maximum-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Version"/>
+ </properties>
+ </component>
+ <component id="c1dcf" class="javax.swing.JLabel" binding="versionJLabel">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="250" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ <component id="662c8" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="5" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <component id="dd7ea" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="5" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="71261" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="800" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="d4c6f" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ <maximum-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Default Parameter"/>
+ </properties>
+ </component>
+ <component id="f162d" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="5" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ <scrollpane id="20925">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="700" height="150"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="36ec3" class="javax.swing.JTextArea" binding="defaultParameterTextArea">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ </children>
+ </grid>
+ <component id="d0bd5" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="e5d4b" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="811bd" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.java
new file mode 100644
index 0000000..51937c3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.java
@@ -0,0 +1,131 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.utils.ParameterUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.Binding;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskPackageService;
+import org.apache.commons.lang.StringUtils;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.intellij.uiDesigner.core.GridConstraints.*;
+
+public class PackageManagerDialogForm {
+ private static final Logger LOG = Logger.getInstance(PackageManagerDialogForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private JPanel mainJPanel;
+ private JCheckBox shareJCheckBox;
+ private JLabel nameJLabel;
+ private JLabel namespaceJPanel;
+ private JLabel versionJLabel;
+ private JTextArea defaultParameterTextArea;
+ private JPanel sharedJPanel;
+
+ private WhiskPackageService whiskPackageService = WhiskPackageService.getInstance();
+
+ private Project project;
+ private WhiskAuth whiskAuth;
+ private WhiskPackageWithActions whiskPackage;
+
+ public PackageManagerDialogForm(Project project, WhiskAuth whiskAuth, WhiskPackageWithActions whiskPackage) {
+ this.project = project;
+ this.whiskAuth = whiskAuth;
+ this.whiskPackage = whiskPackage;
+
+ nameJLabel.setText(whiskPackage.getName());
+ namespaceJPanel.setText(whiskPackage.getNamespace());
+ versionJLabel.setText(whiskPackage.getVersion());
+
+ Optional<Binding> binding = whiskPackage.getBinding();
+ if (binding.isPresent()) {
+ sharedJPanel.remove(shareJCheckBox);
+ Binding b = binding.get();
+ JLabel label = new JLabel("from " + b.getNamespace() + "/" + b.getName());
+ label.setPreferredSize(new Dimension(250, -1));
+ sharedJPanel.add(label, new GridConstraints(0, 2, 1, 1,
+ SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK,
+ SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+ new Dimension(-1, -1), new Dimension(250, -1), new Dimension(-1, -1), 0));
+ } else {
+ shareJCheckBox.setSelected(whiskPackage.isPublish());
+ }
+
+ try {
+ defaultParameterTextArea.setText(JsonParserUtils.writeParameterToJson(whiskPackage.getParameters()));
+ } catch (JsonProcessingException e) {
+ LOG.error("Failed to parse json: " + whiskPackage.getName(), e);
+ }
+ }
+
+ public void updatePackage() {
+ try {
+ /**
+ * Update default parameters
+ */
+ // parameters
+ List<Map<String, Object>> params = parametersToCollection(whiskPackage, defaultParameterTextArea.getText());
+ // payload
+ Map<String, Object> payload = new LinkedHashMap<>();
+ payload.put("name", whiskPackage.getName());
+ payload.put("namespace", whiskPackage.getNamespace());
+ payload.put("parameters", params);
+ if (shareJCheckBox.isValid()) {
+ payload.put("publish", shareJCheckBox.isSelected());
+ }
+
+ whiskPackageService.updateWhiskPackage(whiskAuth, whiskPackage.getName(), payload)
+ .ifPresent(updated -> NOTIFIER.notify(project, updated.getName() + " updated", NotificationType.INFORMATION));
+ } catch (IOException e) {
+ String msg = "Failed to update package: " + whiskPackage.getName();
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+
+ }
+
+ private boolean validateParams(String params) throws IOException {
+ return JsonParserUtils.isValidJson(params);
+ }
+
+ private List<Map<String, Object>> parametersToCollection(WhiskPackageWithActions pkg, String param) throws IOException {
+ if (StringUtils.isEmpty(param) || !validateParams(param)) {
+ return pkg.getParameters();
+ }
+ return ParameterUtils.mapToListMap(JsonParserUtils.parseMap(param));
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/DeleteTriggerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/DeleteTriggerDialog.java
new file mode 100644
index 0000000..e9f06a2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/DeleteTriggerDialog.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.trigger;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DeleteTriggerDialog extends DialogWrapper {
+
+ private WhiskTriggerMetaData whiskTriggerMetaData;
+
+ public DeleteTriggerDialog(Project project, WhiskTriggerMetaData whiskTriggerMetaData) {
+ super(true); // use current window as parent
+ setTitle("Delete Trigger");
+ setResizable(false);
+ this.whiskTriggerMetaData = whiskTriggerMetaData;
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ JPanel dialogPanel = new JPanel(new BorderLayout());
+
+ JLabel label = new JLabel("Are you sure you want to delete " + whiskTriggerMetaData.getName() + " trigger?");
+ label.setPreferredSize(new Dimension(100, 30));
+ dialogPanel.add(label, BorderLayout.CENTER);
+
+ return dialogPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/TriggerCreationManagerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/TriggerCreationManagerDialog.java
new file mode 100644
index 0000000..6454823
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/TriggerCreationManagerDialog.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.trigger;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.ui.TriggerCreationManagerDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class TriggerCreationManagerDialog extends DialogWrapperWithApply {
+
+ private TriggerCreationManagerDialogForm triggerCreationManagerDialogForm;
+
+ public TriggerCreationManagerDialog(Project project, WhiskAuth auth) {
+ super(project, true); // use current window as parent
+ triggerCreationManagerDialogForm = new TriggerCreationManagerDialogForm(project, auth);
+ setTitle("Create Trigger");
+ setResizable(false);
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ if (triggerCreationManagerDialogForm != null) {
+ return triggerCreationManagerDialogForm.getContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void doApplyAction() {
+ if (triggerCreationManagerDialogForm != null) {
+ triggerCreationManagerDialogForm.createTrigger();
+ }
+ super.doApplyAction();
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (triggerCreationManagerDialogForm != null && !myApplyAction.isApplied()) {
+ triggerCreationManagerDialogForm.createTrigger();
+ }
+ super.doOKAction();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/TriggerManagerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/TriggerManagerDialog.java
new file mode 100644
index 0000000..98e0fcf
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/TriggerManagerDialog.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.trigger;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.ui.TriggerManagerDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+public class TriggerManagerDialog extends DialogWrapperWithApply {
+
+ private TriggerManagerDialogForm triggerManagerDialogForm;
+
+ public TriggerManagerDialog(Project project, WhiskAuth auth, ExecutableWhiskTrigger trigger, List<WhiskActionMetaData> actions) {
+ super(project, true); // use current window as parent
+ triggerManagerDialogForm = new TriggerManagerDialogForm(project, auth, trigger, actions);
+ setTitle("Manage Trigger");
+ setResizable(false);
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ if (triggerManagerDialogForm != null) {
+ return this.triggerManagerDialogForm.getContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void doApplyAction() {
+ if (triggerManagerDialogForm != null) {
+ triggerManagerDialogForm.updateTrigger();
+ }
+ super.doApplyAction();
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (triggerManagerDialogForm != null && !myApplyAction.isApplied()) {
+ triggerManagerDialogForm.updateTrigger();
+ }
+ super.doOKAction();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/listener/RemoveLinkedRuleListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/listener/RemoveLinkedRuleListener.java
new file mode 100644
index 0000000..1c98bc4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/listener/RemoveLinkedRuleListener.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.trigger.listener;
+
+public interface RemoveLinkedRuleListener {
+
+ void removeLinkedRule(String ruleName);
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.form
new file mode 100644
index 0000000..ca70047
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.form
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.trigger.ui.LinkedActionsForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="74847" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Action"/>
+ </properties>
+ </component>
+ <component id="613fe" class="javax.swing.JTextField" binding="actionJTextField">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="300" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="e8a35" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Rule"/>
+ </properties>
+ </component>
+ <component id="ebfc1" class="javax.swing.JTextField" binding="ruleJTextField">
+ <constraints>
+ <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="300" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="5e36b" class="javax.swing.JButton" binding="deleteJButton">
+ <constraints>
+ <grid row="0" column="4" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.java
new file mode 100644
index 0000000..47a3ae3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.trigger.ui;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.listener.RemoveLinkedRuleListener;
+
+import javax.swing.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import static com.intellij.icons.AllIcons.General.Remove;
+
+public class LinkedActionsForm {
+ private JTextField actionJTextField;
+ private JTextField ruleJTextField;
+ private JButton deleteJButton;
+ private JPanel mainJPanel;
+
+ public LinkedActionsForm(Project project, String actionName, String ruleName, RemoveLinkedRuleListener remover) {
+ actionJTextField.setText(actionName);
+ actionJTextField.setEnabled(false);
+ ruleJTextField.setText(ruleName);
+ ruleJTextField.setEnabled(false);
+
+ deleteJButton.setIcon(Remove);
+ deleteJButton.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ String ruleName = ruleJTextField.getText();
+ remover.removeLinkedRule(ruleName);
+ }
+ });
+ }
+
+
+ public String getAction() {
+ return actionJTextField.getText();
+ }
+
+ public String getRule() {
+ return ruleJTextField.getText();
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.form
new file mode 100644
index 0000000..25f5275
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.form
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.trigger.ui.TriggerCreationManagerDialogForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="648" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="44d24" binding="triggerNameJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="939a0" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Name"/>
+ </properties>
+ </component>
+ <component id="fa786" class="javax.swing.JTextField" binding="triggerNameJTextField">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="450" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="6825b" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="a087a" binding="triggerDefaultParameterJPanl" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="cc040" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Default Parameter"/>
+ </properties>
+ </component>
+ <component id="13369" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ <scrollpane id="1795a">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="450" height="150"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="ef664" class="javax.swing.JTextArea" binding="triggerDefaultParameterJTextArea">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ </children>
+ </grid>
+ <component id="c5a25" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="5189c" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.java
new file mode 100644
index 0000000..ea016f5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.trigger.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.utils.ParameterUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public class TriggerCreationManagerDialogForm {
+ private static final Logger LOG = Logger.getInstance(TriggerCreationManagerDialogForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private JPanel mainJPanel;
+ private JTextField triggerNameJTextField;
+ private JPanel triggerNameJPanel;
+ private JPanel triggerDefaultParameterJPanl;
+ private JTextArea triggerDefaultParameterJTextArea;
+
+ private Project project;
+ private WhiskAuth auth;
+ private WhiskTriggerService whiskTriggerService;
+
+ public TriggerCreationManagerDialogForm(Project project, WhiskAuth auth) {
+ this.project = project;
+ this.auth = auth;
+ this.whiskTriggerService = WhiskTriggerService.getInstance();
+ }
+
+ public void createTrigger() {
+ try {
+ // name
+ String triggerName = triggerNameJTextField.getText().trim();
+ // parameters
+ Optional<String> params = ParameterUtils.validateParams(triggerDefaultParameterJTextArea.getText());
+ if (!params.isPresent()) {
+ NOTIFIER.notify(project, "The json format of the parameter is incorrect.", NotificationType.ERROR);
+ return;
+ }
+
+ List<Map<String, Object>> validParams = ParameterUtils.mapToListMap(JsonParserUtils.parseMap(params.get()));
+ // payload
+ Map<String, Object> payload = new LinkedHashMap<>();
+ payload.put("name", triggerName);
+ payload.put("parameters", validParams);
+ whiskTriggerService.createWhiskTrigger(auth, triggerName, payload);
+
+ refreshWhiskTree();
+ NOTIFIER.notify(project, triggerName + " created", NotificationType.INFORMATION);
+ } catch (IOException e) {
+ String msg = "Failed to create trigger: " + triggerNameJTextField.getText().trim();
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }
+
+ /**
+ * Helper functions.
+ */
+ private void refreshWhiskTree() {
+ EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+ WhiskService service = ServiceManager.getService(project, WhiskService.class);
+ try {
+ List<WhiskEndpoint> endpoints = JsonParserUtils.parseWhiskEndpoints(service.getEndpoints());
+ if (!endpoints.isEmpty()) {
+ EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+ } else {
+ final String msg = "There are no endpoints saved.";
+ LOG.info(msg);
+ NOTIFIER.notify(project, msg, NotificationType.INFORMATION);
+ }
+ } catch (IOException e) {
+ final String msg = "Entities cannot be loaded.";
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerManagerDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerManagerDialogForm.form
new file mode 100644
index 0000000..4220be2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerManagerDialogForm.form
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.trigger.ui.TriggerManagerDialogForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="6" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="1018" height="677"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="c6dc7" binding="tirggerNameJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="f997a" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Name"/>
+ </properties>
+ </component>
+ <component id="eeae8" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <enabled value="true"/>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ <component id="2eac4" class="javax.swing.JLabel" binding="triggerNameJLabel">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="800" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <horizontalAlignment value="2"/>
+ <text value="Label"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="194f" binding="triggerActionsJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="d68b0" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Linked Actions"/>
+ </properties>
+ </component>
+ <scrollpane id="70daf" binding="linkedActionsJScrollPane">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="800" height="150"/>
+ </grid>
+ </constraints>
+ <properties>
+ <horizontalScrollBarPolicy value="31"/>
+ <verticalScrollBarPolicy value="20"/>
+ </properties>
+ <border type="none"/>
+ <children>
+ <grid id="3b8c2" layout-manager="GridLayoutManager" row-count="2" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints/>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="957bf" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Action"/>
+ </properties>
+ </component>
+ <component id="cd93d" class="javax.swing.JComboBox" binding="selectActionJComboBox">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="305" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="f4ad6" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Rule"/>
+ </properties>
+ </component>
+ <component id="676" class="javax.swing.JTextField" binding="ruleNameJTextField">
+ <constraints>
+ <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="300" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="aa829" class="javax.swing.JButton" binding="addJButton">
+ <constraints>
+ <grid row="0" column="4" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ <grid id="3651a" binding="linkedActionsJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="5" vsize-policy="3" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ </children>
+ </grid>
+ </children>
+ </scrollpane>
+ <component id="994a7" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="f96ea" binding="triggerDefaultParameterJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="7cf0" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text value="Default Parameter"/>
+ </properties>
+ </component>
+ <component id="e4ee8" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ <scrollpane id="a4ee0">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="800" height="150"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none">
+ <color color="-4473925"/>
+ </border>
+ <children>
+ <component id="cfc60" class="javax.swing.JTextArea" binding="defaultParameterJTextArea">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ </children>
+ </grid>
+ <component id="6c728" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="cc882" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="9736e" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerManagerDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerManagerDialogForm.java
new file mode 100644
index 0000000..34e9793
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerManagerDialogForm.java
@@ -0,0 +1,247 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.trigger.ui;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.utils.ParameterUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.SimplifiedWhiskRule;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskRuleService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.RefreshActionOrTriggerListener;
+import org.apache.commons.lang.RandomStringUtils;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import javax.swing.*;
+import javax.swing.event.ListDataListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.util.*;
+
+import static com.intellij.icons.AllIcons.General.Add;
+
+public class TriggerManagerDialogForm {
+ private static final Logger LOG = Logger.getInstance(TriggerManagerDialogForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private static final String DEFAULT_RULE_TEXTAREA_MSG = "If not entered, it is automatically generated.";
+
+ private JPanel mainJPanel;
+ private JPanel tirggerNameJPanel;
+ private JPanel triggerActionsJPanel;
+ private JLabel triggerNameJLabel;
+
+ private JComboBox selectActionJComboBox;
+ private JTextField ruleNameJTextField;
+ private JButton addJButton;
+ private JScrollPane linkedActionsJScrollPane;
+ private JPanel linkedActionsJPanel;
+ private JPanel triggerDefaultParameterJPanel;
+ private JTextArea defaultParameterJTextArea;
+
+ private Project project;
+ private Map<String, LinkedActionsForm> cachedRules = new HashMap<>();
+ private Map<String, LinkedActionsForm> removedRules = new HashMap<>();
+ private ExecutableWhiskTrigger cachedTrigger;
+ private WhiskAuth auth;
+ private WhiskTriggerService whiskTriggerService;
+ private WhiskRuleService whiskRuleService;
+
+ public TriggerManagerDialogForm(Project project, WhiskAuth auth, ExecutableWhiskTrigger trigger, List<WhiskActionMetaData> actions) {
+ this.project = project;
+ this.auth = auth;
+ this.cachedTrigger = trigger;
+ this.whiskTriggerService = WhiskTriggerService.getInstance();
+ this.whiskRuleService = WhiskRuleService.getInstance();
+
+ triggerNameJLabel.setText(trigger.getName());
+
+ try {
+ defaultParameterJTextArea.setText(JsonParserUtils.writeParameterToJson(trigger.getParameters()));
+ } catch (JsonProcessingException e) {
+ LOG.error("Failed to parse json: " + trigger.getName(), e);
+ }
+
+ selectActionJComboBox.setModel(new ComboBoxModel() {
+ private WhiskActionMetaData selected;
+
+ @Override
+ public void setSelectedItem(Object anItem) {
+ selected = (WhiskActionMetaData) anItem;
+ }
+
+ @Override
+ public Object getSelectedItem() {
+ return selected;
+ }
+
+ @Override
+ public int getSize() {
+ return actions.size();
+ }
+
+ @Override
+ public Object getElementAt(int index) {
+ return actions.get(index);
+ }
+
+ @Override
+ public void addListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+
+ @Override
+ public void removeListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+ });
+
+ ruleNameJTextField.setText(DEFAULT_RULE_TEXTAREA_MSG);
+
+ String namespace = trigger.getNamespace();
+ for (Map.Entry<String, SimplifiedWhiskRule> set : trigger.getRules().entrySet()) {
+ String ruleName = set.getKey().replace(namespace + "/", "");
+ String actionName = set.getValue().getAction().getPkgActionName();
+ addLinkedAction(actionName, ruleName);
+ }
+
+ addJButton.setIcon(Add);
+ addJButton.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (selectActionJComboBox.getSelectedItem() == null) {
+ return;
+ }
+
+ String ruleName = getRuleName(ruleNameJTextField.getText());
+ if (existRule(ruleName)) {
+ NOTIFIER.notify(project, "The " + ruleName + " already exist", NotificationType.WARNING);
+ return;
+ }
+
+ WhiskActionMetaData action = (WhiskActionMetaData) selectActionJComboBox.getSelectedItem();
+ String actionName = action.getWhiskPackage().map(pkg -> pkg + "/" + action.getName()).orElse(action.getName());
+ addLinkedAction(actionName, ruleName);
+ }
+ });
+ }
+
+ public void updateTrigger() {
+ try {
+ /**
+ * Update default parameters
+ */
+ Optional<String> params = ParameterUtils.validateParams(defaultParameterJTextArea.getText());
+ if (!params.isPresent()) {
+ NOTIFIER.notify(project, "The json format of the parameter is incorrect.", NotificationType.ERROR);
+ return;
+ }
+ // parameters
+ List<Map<String, Object>> validParams = ParameterUtils.mapToListMap(JsonParserUtils.parseMap(params.get()));
+ // payload
+ Map<String, Object> payload = new LinkedHashMap<>();
+ payload.put("name", cachedTrigger.getName());
+ payload.put("parameters", validParams);
+ whiskTriggerService.updateWhiskTrigger(auth, cachedTrigger.getName(), payload);
+
+ /**
+ * Update rules
+ */
+ for (LinkedActionsForm linkedActionsForm : cachedRules.values()) {
+ String ruleName = linkedActionsForm.getRule();
+ String actionName = linkedActionsForm.getAction();
+ // payload
+ Map<String, Object> rulePayload = new LinkedHashMap<>();
+ rulePayload.put("name", ruleName);
+ rulePayload.put("trigger", "/_/" + cachedTrigger.getName());
+ rulePayload.put("action", "/_/" + actionName);
+ rulePayload.put("status", "");
+ whiskRuleService.updateWhiskRule(auth, ruleName, rulePayload).ifPresent(rule ->
+ LOG.info("Updated rule: " + rule.getName()));
+ }
+
+ /**
+ * Remove rules
+ */
+ for (LinkedActionsForm linkedActionsForm : removedRules.values()) {
+ whiskRuleService.deleteWhiskRule(auth, linkedActionsForm.getRule()).ifPresent(rule ->
+ LOG.info("Deleted rule: " + rule.getName()));
+ }
+
+ NOTIFIER.notify(project, cachedTrigger.getName() + " updated", NotificationType.INFORMATION);
+ EventUtils.publish(project, RefreshActionOrTriggerListener.TOPIC, RefreshActionOrTriggerListener::fetchActionMetadata);
+ } catch (IOException e) {
+ String msg = "Failed to update trigger: " + cachedTrigger.getName();
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }
+
+ /**
+ * Helper functions.
+ */
+ private void addLinkedAction(String actionName, String ruleName) {
+ LinkedActionsForm linkedActionsForm = new LinkedActionsForm(project, actionName, ruleName, this::removeLinkedAction);
+ linkedActionsJPanel.setLayout(new BoxLayout(linkedActionsJPanel, BoxLayout.Y_AXIS));
+ linkedActionsJPanel.add(linkedActionsForm.getContent());
+ cachedRules.put(ruleName, linkedActionsForm);
+ linkedActionsJPanel.updateUI();
+ }
+
+ private void removeLinkedAction(String ruleName) {
+ LinkedActionsForm form = cachedRules.get(ruleName);
+ removedRules.put(ruleName, form);
+ cachedRules.remove(ruleName);
+ linkedActionsJPanel.remove(form.getContent());
+ linkedActionsJPanel.updateUI();
+ }
+
+ private boolean existRule(String ruleName) {
+ for (String r : cachedRules.keySet()) {
+ if (r.equals(ruleName.trim())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private String getRuleName(String userInput) {
+ if (DEFAULT_RULE_TEXTAREA_MSG.trim().equals(userInput.trim())) {
+ // auto generate
+ return generateName();
+ } else {
+ // use user input
+ return userInput.trim();
+ }
+ }
+
+ private String generateName() {
+ return RandomStringUtils.randomAlphabetic(30);
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditor.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditor.java
new file mode 100644
index 0000000..7aec225
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditor.java
@@ -0,0 +1,191 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.editor;
+
+import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorLocation;
+import com.intellij.openapi.fileEditor.FileEditorState;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.WhiskUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActivationService;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityEntry;
+import com.navercorp.openwhisk.intellij.explorer.editor.ui.ActivationViewEditorForm;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public class ActivationViewEditor implements FileEditor {
+ private static final Logger LOG = Logger.getInstance(ActivationViewEditor.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private Project project;
+ private ActivationViewEditorForm activationViewEditorForm;
+ private WhiskActivationService whiskActivationService = WhiskActivationService.getInstance();
+
+ public ActivationViewEditor(Project project,
+ List<WhiskEndpoint> endpoints,
+ Optional<WhiskAuth> whiskAuth,
+ Optional<WhiskActionMetaData> action,
+ Optional<WhiskTriggerMetaData> trigger) {
+ activationViewEditorForm = new ActivationViewEditorForm(project, endpoints);
+ activationViewEditorForm.cacheWhiskAuth(whiskAuth);
+
+ /**
+ * Initialize ComboBox values
+ */
+ List<WhiskNamespace> namespaces = endpoints.stream().flatMap(ep -> ep.getNamespaces().stream()).collect(Collectors.toList());
+ activationViewEditorForm.initializeNamespaceJComboBox(namespaces);
+
+ whiskAuth.ifPresent(auth ->
+ getEntity(action, trigger).ifPresent(entity -> {
+
+ /**
+ * Load Activations
+ *
+ * Note: Activations of binding package action cannot be loaded.
+ * TODO load activations of binding action after the follow upstream pr is merged: https://github.com/apache/openwhisk/pull/4919
+ */
+ try {
+ List<WhiskActivationMetaData> activations =
+ whiskActivationService.getWhiskActivations(auth, entity.toEntityName(), 100, 0); // TODO pagination
+ activationViewEditorForm.initializeActivationTable(activations);
+ } catch (IOException e) {
+ LOG.error(e);
+ }
+
+ /**
+ * Set value to namespace combobox
+ */
+ WhiskUtils.findWhiskNamespace(endpoints, auth.getAuth())
+ .ifPresent(namespace -> activationViewEditorForm.setNamespaceJComboBox(namespace));
+
+ /**
+ * Set value to action or trigger combobox
+ */
+ activationViewEditorForm.setActionOrTriggerJComboBox(entity);
+ }));
+ }
+
+ private Optional<ComboBoxEntityEntry> getEntity(Optional<WhiskActionMetaData> action, Optional<WhiskTriggerMetaData> trigger) {
+ if (action.isPresent() && trigger.isPresent()) {
+ return Optional.empty();
+ } else if (action.isPresent()) {
+ return action.map(WhiskActionMetaData::toCombBoxEntityEntry);
+ } else if (trigger.isPresent()) {
+ return trigger.map(WhiskTriggerMetaData::toCombBoxEntityEntry);
+ } else {
+ return Optional.of(ComboBoxEntityEntry.NONE_COMBO_BOX_ENTITY_ENTRY);
+ }
+ }
+
+ @NotNull
+ @Override
+ public JComponent getComponent() {
+ return activationViewEditorForm.getContent();
+ }
+
+ @Nullable
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return "activation editor";
+ }
+
+ @Override
+ public void setState(@NotNull FileEditorState state) {
+
+ }
+
+ @Override
+ public boolean isModified() {
+ return false;
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) {
+
+ }
+
+ @Override
+ public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) {
+
+ }
+
+ @Nullable
+ @Override
+ public FileEditorLocation getCurrentLocation() {
+ return null;
+ }
+
+ @Override
+ public void dispose() {
+
+ }
+
+ @Nullable
+ @Override
+ public <T> T getUserData(@NotNull Key<T> key) {
+ return null;
+ }
+
+ @Override
+ public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
+
+ }
+
+ @Override
+ public void selectNotify() {
+
+ }
+
+ @Override
+ public void deselectNotify() {
+
+ }
+
+ @Nullable
+ @Override
+ public BackgroundEditorHighlighter getBackgroundHighlighter() {
+ return null; // Return {@code null} if no background highlighting activity necessary for this file editor.
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditorProvider.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditorProvider.java
new file mode 100644
index 0000000..f1d3643
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditorProvider.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.editor;
+
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorPolicy;
+import com.intellij.openapi.fileEditor.FileEditorProvider;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+public class ActivationViewEditorProvider implements FileEditorProvider, DumbAware {
+ @Override
+ public boolean accept(@NotNull Project project, @NotNull VirtualFile file) {
+ if (file instanceof ActivationViewVirtualFile) {
+ return true;
+ }
+ return false;
+ }
+
+ @NotNull
+ @Override
+ public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) {
+ ActivationViewVirtualFile activation = (ActivationViewVirtualFile) file;
+ return new ActivationViewEditor(project, activation.getEndpoints(), activation.getWhiskAuth(), activation.getAction(), activation.getTrigger());
+ }
+
+ @NotNull
+ @Override
+ public String getEditorTypeId() {
+ return "activation-editor";
+ }
+
+ @NotNull
+ @Override
+ public FileEditorPolicy getPolicy() {
+ return FileEditorPolicy.HIDE_DEFAULT_EDITOR;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFile.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFile.java
new file mode 100644
index 0000000..484fdb3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFile.java
@@ -0,0 +1,147 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.editor;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Optional;
+
+public class ActivationViewVirtualFile extends VirtualFile {
+
+ private List<WhiskEndpoint> endpoints;
+ private Optional<WhiskAuth> whiskAuth;
+ private Optional<WhiskActionMetaData> action;
+ private Optional<WhiskTriggerMetaData> trigger;
+
+ private ActivationViewVirtualFileSystem fileSystem;
+
+ public ActivationViewVirtualFile(@NotNull Project project,
+ List<WhiskEndpoint> endpoints,
+ Optional<WhiskAuth> whiskAuth,
+ Optional<WhiskActionMetaData> action,
+ Optional<WhiskTriggerMetaData> trigger) {
+ this.endpoints = endpoints;
+ this.whiskAuth = whiskAuth;
+ this.action = action;
+ this.trigger = trigger;
+ this.fileSystem = ActivationViewVirtualFileSystem.getInstance();
+ }
+
+ public List<WhiskEndpoint> getEndpoints() {
+ return endpoints;
+ }
+
+ public Optional<WhiskAuth> getWhiskAuth() {
+ return whiskAuth;
+ }
+
+ public Optional<WhiskActionMetaData> getAction() {
+ return action;
+ }
+
+ public Optional<WhiskTriggerMetaData> getTrigger() {
+ return trigger;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return "Activation View";
+ }
+
+ @NotNull
+ @Override
+ public VirtualFileSystem getFileSystem() {
+ return fileSystem;
+ }
+
+ @NotNull
+ @Override
+ public String getPath() {
+ return "";
+ }
+
+ @Override
+ public boolean isWritable() {
+ return false;
+ }
+
+ @Override
+ public boolean isDirectory() {
+ return false;
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public VirtualFile getParent() {
+ return null;
+ }
+
+ @Override
+ public VirtualFile[] getChildren() {
+ return new VirtualFile[0];
+ }
+
+ @NotNull
+ @Override
+ public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException {
+ return new ByteArrayOutputStream();
+ }
+
+ @NotNull
+ @Override
+ public byte[] contentsToByteArray() throws IOException {
+ return null;
+ }
+
+ @Override
+ public long getTimeStamp() {
+ return 0;
+ }
+
+ @Override
+ public long getLength() {
+ return 0;
+ }
+
+ @Override
+ public void refresh(boolean asynchronous, boolean recursive, @Nullable Runnable postRunnable) {
+
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return null;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFileSystem.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFileSystem.java
new file mode 100644
index 0000000..640750f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFileSystem.java
@@ -0,0 +1,114 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.editor;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileListener;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+
+public class ActivationViewVirtualFileSystem extends VirtualFileSystem {
+
+ private static final String PROTOCOL = "activation-view";
+
+ private static final IOException READONLY_FILE_SYSTEM = new IOException("Operation not support");
+
+ public ActivationViewVirtualFileSystem() {
+ }
+
+ public static ActivationViewVirtualFileSystem getInstance() {
+ return (ActivationViewVirtualFileSystem) VirtualFileManager.getInstance().getFileSystem(PROTOCOL);
+ }
+
+ @NotNull
+ @Override
+ public String getProtocol() {
+ return PROTOCOL;
+ }
+
+ @Nullable
+ @Override
+ public VirtualFile findFileByPath(@NotNull String path) {
+ return null;
+ }
+
+ @Override
+ public void refresh(boolean asynchronous) {
+
+ }
+
+ @Nullable
+ @Override
+ public VirtualFile refreshAndFindFileByPath(@NotNull String path) {
+ return null;
+ }
+
+ @Override
+ public void addVirtualFileListener(@NotNull VirtualFileListener listener) {
+
+ }
+
+ @Override
+ public void removeVirtualFileListener(@NotNull VirtualFileListener listener) {
+
+ }
+
+ @Override
+ protected void deleteFile(Object requestor, @NotNull VirtualFile vFile) throws IOException {
+
+ }
+
+ @Override
+ protected void moveFile(Object requestor, @NotNull VirtualFile vFile, @NotNull VirtualFile newParent) throws IOException {
+
+ }
+
+ @Override
+ protected void renameFile(Object requestor, @NotNull VirtualFile vFile, @NotNull String newName) throws IOException {
+
+ }
+
+ @NotNull
+ @Override
+ protected VirtualFile createChildFile(Object requestor, @NotNull VirtualFile vDir, @NotNull String fileName) throws IOException {
+ throw READONLY_FILE_SYSTEM;
+ }
+
+ @NotNull
+ @Override
+ protected VirtualFile createChildDirectory(Object requestor, @NotNull VirtualFile vDir, @NotNull String dirName) throws IOException {
+ throw READONLY_FILE_SYSTEM;
+ }
+
+ @NotNull
+ @Override
+ protected VirtualFile copyFile(Object requestor,
+ @NotNull VirtualFile virtualFile,
+ @NotNull VirtualFile newParent,
+ @NotNull String copyName) throws IOException {
+ throw READONLY_FILE_SYSTEM;
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityEntry.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityEntry.java
new file mode 100644
index 0000000..ff8e4f1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityEntry.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.editor.model;
+
+import java.util.Objects;
+import java.util.Optional;
+
+public class ComboBoxEntityEntry {
+ public static final ComboBoxEntityEntry NONE_COMBO_BOX_ENTITY_ENTRY = new ComboBoxEntityEntry("None (Please select an entity)", ComboBoxEntityType.NONE);
+
+ private String name;
+ private ComboBoxEntityType type;
+
+ public ComboBoxEntityEntry() {
+ }
+
+ public ComboBoxEntityEntry(String name, ComboBoxEntityType type) {
+ this.name = name;
+ this.type = type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public ComboBoxEntityType getType() {
+ return type;
+ }
+
+ public void setType(ComboBoxEntityType type) {
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ComboBoxEntityEntry that = (ComboBoxEntityEntry) o;
+ return Objects.equals(name, that.name) && type == that.type;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, type);
+ }
+
+ public Optional<String> toEntityName() {
+ switch (type) {
+ case ACTION:
+ case TRIGGER:
+ return Optional.of(name);
+ case NONE:
+ default:
+ return Optional.empty();
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityType.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityType.java
new file mode 100644
index 0000000..a31e7ff
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityType.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.editor.model;
+
+public enum ComboBoxEntityType {
+ NONE, ACTION, TRIGGER
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.form
new file mode 100644
index 0000000..c234f3c
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.form
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.editor.ui.ActivationViewEditorForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <xy x="20" y="20" width="2220" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="5711a" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints border-constraint="North"/>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="96fee" layout-manager="GridLayoutManager" row-count="1" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="14afe" class="javax.swing.JComboBox" binding="namespaceJComboBox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="300" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="c419f" class="javax.swing.JComboBox" binding="actionOrTriggerJComboBox">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="300" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="1fb64" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <maximum-size width="5" height="-1"/>
+ </grid>
+ </constraints>
+ <properties>
+ <orientation value="1"/>
+ </properties>
+ </component>
+ <component id="a14c9" class="javax.swing.JButton" binding="searchJButton">
+ <constraints>
+ <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Search"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <grid id="87efb" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints border-constraint="Center"/>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <scrollpane id="3ee83">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="a8016" class="javax.swing.JTable" binding="activationsJTable">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ </children>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.java
new file mode 100644
index 0000000..a908ec7
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.java
@@ -0,0 +1,276 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.editor.ui;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.navercorp.openwhisk.intellij.common.utils.FileUtils;
+import com.navercorp.openwhisk.intellij.common.utils.WhiskUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActivationService;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityEntry;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import javax.swing.*;
+import javax.swing.event.ListDataListener;
+import javax.swing.table.DefaultTableModel;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class ActivationViewEditorForm {
+ private static final Logger LOG = Logger.getInstance(ActivationViewEditorForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.KOREA);
+ private static final int ACTIVATION_ID_COLUMN = 1;
+
+ private JPanel mainJPanel;
+ private JPanel actionsToobarJPanel;
+ private JTable activationsJTable;
+ private JComboBox namespaceJComboBox;
+ private JComboBox actionOrTriggerJComboBox;
+ private JButton searchJButton;
+
+ private WhiskActivationService whiskActivationService = WhiskActivationService.getInstance();
+ private FileEditorManager fileEditorManager;
+
+ private DefaultTableModel activationsTableModel;
+
+ private Optional<WhiskAuth> currentAuth = Optional.empty();
+
+ public ActivationViewEditorForm(Project project, List<WhiskEndpoint> endpoints) {
+ this.fileEditorManager = FileEditorManager.getInstance(project);
+
+ activationsTableModel = new DefaultTableModel(initActivationColumnModel(), 0) {
+ @Override
+ public boolean isCellEditable(int row, int column) {
+ // Activation ID(1) & Entity(6) are editable to copy value
+ return column == 1 || column == 6;
+ }
+ };
+
+ activationsJTable.setModel(activationsTableModel);
+ activationsJTable.setDragEnabled(true);
+ activationsJTable.setAutoCreateRowSorter(true);
+
+ activationsJTable.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() > 1 && currentAuth.isPresent()) {
+ int row = activationsJTable.rowAtPoint(e.getPoint());
+ String activationId = (String) activationsTableModel.getValueAt(row, ACTIVATION_ID_COLUMN);
+ try {
+ whiskActivationService.getWhiskActivation(currentAuth.get(), activationId)
+ .ifPresent(activationWithLogs -> openActivationEditor(project, activationWithLogs));
+ } catch (IOException ex) {
+ LOG.error("Failed to fetch activations: " + activationId, ex);
+ }
+ }
+ }
+ });
+
+ searchJButton.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ WhiskNamespace whiskNamespace = (WhiskNamespace) namespaceJComboBox.getSelectedItem();
+ ComboBoxEntityEntry entity = (ComboBoxEntityEntry) actionOrTriggerJComboBox.getSelectedItem();
+ WhiskUtils.findWhiskAuth(endpoints, whiskNamespace).ifPresent(auth ->
+ initializeActivationTable(loadActivations(auth, Optional.ofNullable(entity))));
+ }
+ });
+ }
+
+ public void initializeNamespaceJComboBox(List<WhiskNamespace> namespaces) {
+ namespaceJComboBox.setModel(new ComboBoxModel<WhiskNamespace>() {
+ private WhiskNamespace namespace;
+
+ @Override
+ public void setSelectedItem(Object anItem) {
+ namespace = (WhiskNamespace) anItem;
+ }
+
+ @Override
+ public WhiskNamespace getSelectedItem() {
+ return this.namespace;
+ }
+
+ @Override
+ public int getSize() {
+ return namespaces.size();
+ }
+
+ @Override
+ public WhiskNamespace getElementAt(int index) {
+ return namespaces.get(index);
+ }
+
+ @Override
+ public void addListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+
+ @Override
+ public void removeListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+ });
+
+ namespaceJComboBox.addActionListener(e -> {
+ WhiskNamespace namespace = (WhiskNamespace) namespaceJComboBox.getSelectedItem();
+ initializeActionAndTriggerJComboBox(namespace.getActions(), namespace.getTriggers());
+ });
+ }
+
+ public void initializeActionAndTriggerJComboBox(List<WhiskActionMetaData> actions, List<WhiskTriggerMetaData> triggers) {
+ /**
+ * It shows the list of none, action, trigger in one JComboBox at once. The order is as follows: None ~ Actions ~ triggers
+ *
+ * TODO Add binding action
+ */
+ List<ComboBoxEntityEntry> entries = Stream.of(
+ Stream.of(ComboBoxEntityEntry.NONE_COMBO_BOX_ENTITY_ENTRY),
+ actions.stream().map(WhiskActionMetaData::toCombBoxEntityEntry),
+ triggers.stream().map(WhiskTriggerMetaData::toCombBoxEntityEntry))
+ .flatMap(a -> a)
+ .collect(Collectors.toList());
+
+ actionOrTriggerJComboBox.setModel(new ComboBoxModel<ComboBoxEntityEntry>() {
+ private ComboBoxEntityEntry selected;
+
+ @Override
+ public void setSelectedItem(Object anItem) {
+ this.selected = (ComboBoxEntityEntry) anItem;
+ }
+
+ @Override
+ public ComboBoxEntityEntry getSelectedItem() {
+ return selected;
+ }
+
+ @Override
+ public int getSize() {
+ return entries.size();
+ }
+
+ @Override
+ public ComboBoxEntityEntry getElementAt(int index) {
+ return entries.get(index);
+ }
+
+ @Override
+ public void addListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+
+ @Override
+ public void removeListDataListener(ListDataListener l) {
+ // nothing to do
+ }
+ });
+ }
+
+ public void cacheWhiskAuth(Optional<WhiskAuth> whiskAuth) {
+ this.currentAuth = whiskAuth;
+ }
+
+ public void initializeActivationTable(List<WhiskActivationMetaData> activations) {
+ clearActivationTable();
+ for (WhiskActivationMetaData activation : activations) {
+ activationsTableModel.addRow(createRow(activation));
+ }
+ }
+
+ public void setNamespaceJComboBox(WhiskNamespace namespace) {
+ namespaceJComboBox.setSelectedItem(namespace);
+ initializeActionAndTriggerJComboBox(namespace.getActions(), namespace.getTriggers());
+ }
+
+ public void setActionOrTriggerJComboBox(ComboBoxEntityEntry entity) {
+ actionOrTriggerJComboBox.setSelectedItem(entity);
+ }
+
+ /**
+ * Helper functions.
+ */
+ private List<WhiskActivationMetaData> loadActivations(WhiskAuth auth, Optional<ComboBoxEntityEntry> entity) {
+ try {
+ return whiskActivationService.getWhiskActivations(auth, entity.flatMap(ComboBoxEntityEntry::toEntityName), 100, 0); // TODO pagination
+ } catch (IOException e) {
+ LOG.error(e);
+ return new ArrayList<>();
+ }
+ }
+
+ private void openActivationEditor(Project project, WhiskActivationWithLogs activationWithLogs) {
+ try {
+ final String tmpFilePath = project.getBasePath() + "/.idea/openwhisk";
+ VirtualFile activationFile = FileUtils.writeActivationToFile(tmpFilePath, activationWithLogs);
+ fileEditorManager.openFile(activationFile, true);
+ } catch (IOException e) {
+ LOG.error("Failed to open file: " + activationWithLogs.getName(), e);
+ }
+ }
+
+ private void clearActivationTable() {
+ activationsTableModel.setRowCount(0);
+ }
+
+ private Vector<String> initActivationColumnModel() {
+ Vector<String> columnModel = new Vector<>();
+ columnModel.add("Datetime");
+ columnModel.add("Activation ID");
+ columnModel.add("Kind");
+ columnModel.add("Start");
+ columnModel.add("Duration");
+ columnModel.add("Status");
+ columnModel.add("Entity");
+ return columnModel;
+ }
+
+ private Vector<String> createRow(WhiskActivationMetaData activation) {
+ LocalDateTime ldt = LocalDateTime.ofInstant(Instant.ofEpochMilli(activation.getStart()), ZoneId.systemDefault());
+ Vector<String> rowModel = new Vector<>();
+ rowModel.add(dateFormat.format(ldt)); // Datetime
+ rowModel.add(activation.getActivationId()); // Activation Id
+ rowModel.add(activation.getKind()); // Kind
+ rowModel.add(activation.getStartType()); // Start
+ rowModel.add(activation.getDuration() + "ms"); // Duration
+ rowModel.add(activation.getStatus()); // Status
+ rowModel.add(activation.getNamespace() + "/" + activation.getName() + ":" + activation.getVersion()); // Entity
+ return rowModel;
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/WhiskExplorerWindowFactory.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/WhiskExplorerWindowFactory.java
new file mode 100644
index 0000000..055ca9e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/WhiskExplorerWindowFactory.java
@@ -0,0 +1,153 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowFactory;
+import com.intellij.ui.content.Content;
+import com.intellij.ui.content.ContentFactory;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskNamespaceService;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.ui.WhiskExplorerWindowForm;
+import org.apache.commons.lang.StringUtils;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class WhiskExplorerWindowFactory implements ToolWindowFactory {
+ private static final Logger LOG = Logger.getInstance(WhiskExplorerWindowFactory.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ @Override
+ public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
+ try {
+ // Load endpoint from ~/.wskprops
+ WhiskService service = ServiceManager.getService(project, WhiskService.class);
+
+ String filterEndpoints = filterEndpoints(service.getEndpoints());
+ LOG.info(filterEndpoints);
+ List<WhiskEndpoint> endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(filterEndpoints));
+ WhiskNamespaceService whiskNamespaceService = WhiskNamespaceService.getInstance();
+
+ readWskProps().ifPresent(auth -> {
+ Optional<WhiskNamespace> validNamespace = whiskNamespaceService.validateNamespace(auth);
+ if (validNamespace.isPresent()) {
+ WhiskNamespace ns = validNamespace.get();
+ Optional<WhiskEndpoint> existEndpoint = hasWhiskEndpoint(endpoints, auth);
+ if (existEndpoint.isPresent()) {
+ Optional<WhiskNamespace> existNamespace = hasWhiskNamespace(existEndpoint.get().getNamespaces(), auth);
+ if (!existNamespace.isPresent()) {
+ int epIndex = endpoints.indexOf(existEndpoint.get());
+ WhiskEndpoint ep = endpoints.get(epIndex);
+ ep.addNamespaces(ns);
+ endpoints.set(epIndex, ep);
+ }
+ } else {
+ endpoints.add(new WhiskEndpoint("endpoint(" + endpoints.size() + ")", auth.getApihost(), Arrays.asList(ns)));
+ }
+ } else {
+ final String msg = "Cannot read the " + System.getProperty("user.home") + "/.wskprops file Please check the file.";
+ NOTIFIER.notify(project, msg, NotificationType.WARNING);
+ }
+ });
+ service.setEndpoints(JsonParserUtils.writeEndpointsToJson(endpoints));
+ service.loadState(service);
+ } catch (IOException e) {
+ final String msg = "Endpoints cannot be loaded.";
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+
+ // Tree View
+ WhiskExplorerWindowForm whiskExplorerWindowForm = new WhiskExplorerWindowForm(project, toolWindow);
+ ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
+ Content content = contentFactory.createContent(whiskExplorerWindowForm.getContent(), null, false);
+ toolWindow.getContentManager().addContent(content);
+ }
+
+ private String filterEndpoints(String endpoints) {
+ if (StringUtils.isEmpty(endpoints)) {
+ return "[]";
+ }
+
+ try {
+ List<Map<String, Object>> eps = JsonParserUtils.parseListMap(endpoints)
+ .stream()
+ .filter(ep -> {
+ String apihost = (String) ep.get("apihost");
+ return StringUtils.isNotEmpty(apihost);
+ }).collect(Collectors.toList());
+ return JsonParserUtils.writeListMapToJson(eps);
+ } catch (IOException e) {
+ LOG.error("Failed to parse json endpoints to map: ", endpoints);
+ return "[]";
+ }
+ }
+
+ private Optional<WhiskEndpoint> hasWhiskEndpoint(List<WhiskEndpoint> endpoints, WhiskAuth auth) {
+ for (WhiskEndpoint ep : endpoints) {
+ if (ep.getApihost().equals(auth.getApihost())) {
+ return Optional.of(ep);
+ }
+ }
+ return Optional.empty();
+ }
+
+ private Optional<WhiskNamespace> hasWhiskNamespace(List<WhiskNamespace> namespaces, WhiskAuth auth) {
+ for (WhiskNamespace ns : namespaces) {
+ if (ns.getAuth().equals(auth.getAuth())) {
+ return Optional.of(ns);
+ }
+ }
+ return Optional.empty();
+ }
+
+ private Optional<WhiskAuth> readWskProps() {
+ Path path = Paths.get(System.getProperty("user.home") + "/.wskprops");
+ Charset cs = StandardCharsets.UTF_8;
+ try {
+ Map<String, String> wskprops = new HashMap<>();
+ for (String prop : Files.readAllLines(path, cs)) {
+ String[] p = prop.split("=");
+ wskprops.put(p[0], p[1]);
+ }
+ return Optional.of(new WhiskAuth(wskprops.get("AUTH"), wskprops.get("APIHOST")));
+ } catch (IOException e) {
+ LOG.error(e);
+ return Optional.empty();
+ } catch (Exception e) {
+ LOG.error(e);
+ return Optional.empty();
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddEndpointAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddEndpointAction.java
new file mode 100644
index 0000000..9e55d82
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddEndpointAction.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.AddEndpointDialog;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.General.Add;
+
+public class AddEndpointAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(AddEndpointAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ public AddEndpointAction() {
+ super("Add Endpoint", "Add Endpoint", Add);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ AddEndpointDialog dialog = new AddEndpointDialog(e.getProject());
+ if (dialog.showAndGet()) {
+ LOG.info("AddEndpointDialogForm is closed");
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddNamespaceAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddNamespaceAction.java
new file mode 100644
index 0000000..65e6c5b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddNamespaceAction.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.namespace.AddNamespaceDialog;
+import org.jetbrains.annotations.NotNull;
+
+public class AddNamespaceAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(AddNamespaceAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskEndpoint whiskEndpoint;
+
+ public AddNamespaceAction(WhiskEndpoint whiskEndpoint) {
+ super("Add Namespace", "Add Namespace", AllIcons.General.Add);
+ this.whiskEndpoint = whiskEndpoint;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskEndpoint != null) {
+ AddNamespaceDialog dialog = new AddNamespaceDialog(e.getProject(), whiskEndpoint);
+ if (dialog.showAndGet()) {
+ LOG.info("AddNamespaceDialog is closed");
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreatePackageAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreatePackageAction.java
new file mode 100644
index 0000000..1478aa5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreatePackageAction.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.pkg.PackageCreationManagerDialog;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.General.Add;
+
+public class CreatePackageAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(CreatePackageAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskAuth whiskAuth;
+
+ public CreatePackageAction(WhiskAuth whiskAuth) {
+ super("Create Package", "Create package", Add);
+ this.whiskAuth = whiskAuth;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskAuth != null) {
+ PackageCreationManagerDialog dialog = new PackageCreationManagerDialog(e.getProject(), whiskAuth);
+ if (dialog.showAndGet()) {
+ LOG.info("TriggerManagerDialog is closed");
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreateTriggerAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreateTriggerAction.java
new file mode 100644
index 0000000..b65b4d5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreateTriggerAction.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.TriggerCreationManagerDialog;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.General.Add;
+
+public class CreateTriggerAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(CreateTriggerAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskAuth whiskAuth;
+
+ public CreateTriggerAction(WhiskAuth whiskAuth) {
+ super("Create Trigger", "Create trigger", Add);
+ this.whiskAuth = whiskAuth;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskAuth != null) {
+ TriggerCreationManagerDialog dialog = new TriggerCreationManagerDialog(e.getProject(), whiskAuth);
+ if (dialog.showAndGet()) {
+ LOG.info("TriggerManagerDialog is closed");
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteActionAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteActionAction.java
new file mode 100644
index 0000000..ca560f7
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteActionAction.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.DeleteActionDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+
+import static com.intellij.icons.AllIcons.Actions.GC;
+
+public class DeleteActionAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(DeleteActionAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskActionMetaData whiskActionMetaData;
+ private WhiskAuth whiskAuth;
+ private WhiskActionService whiskActionService;
+
+ public DeleteActionAction(WhiskAuth whiskAuth, WhiskActionMetaData whiskActionMetaData) {
+ super("Delete", "Delete action", GC);
+ this.whiskActionMetaData = whiskActionMetaData;
+ this.whiskAuth = whiskAuth;
+ this.whiskActionService = WhiskActionService.getInstance();
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskAuth != null && whiskActionMetaData != null) {
+ if ((new DeleteActionDialog(e.getProject())).showAndGet()) {
+ try {
+ whiskActionService.deleteWhiskActions(whiskAuth, whiskActionMetaData.getWhiskPackage(), whiskActionMetaData.getName())
+ .ifPresent(deletedAction -> {
+ String msg = deletedAction.getName() + " action is deleted";
+ LOG.info(msg);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.INFORMATION);
+
+ ActionManager.getInstance().getAction("WhiskExplorer.Actions.Controls.Refresh").actionPerformed(e);
+ });
+ } catch (IOException ex) {
+ final String msg = "The action cannot be deleted: " + whiskActionMetaData.getName();
+ LOG.error(msg, ex);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteEndpointAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteEndpointAction.java
new file mode 100644
index 0000000..0cb0f61
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteEndpointAction.java
@@ -0,0 +1,97 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.DeleteEndpointDialog;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.intellij.icons.AllIcons.General.Remove;
+
+public class DeleteEndpointAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(DeleteEndpointAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskEndpoint whiskEndpoint;
+
+ public DeleteEndpointAction(WhiskEndpoint whiskEndpoint) {
+ super("Delete Endpoint", "Delete Endpoint", Remove);
+ this.whiskEndpoint = whiskEndpoint;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskEndpoint != null) {
+ final Project project = e.getProject();
+ DeleteEndpointDialog dialog = new DeleteEndpointDialog(project, whiskEndpoint);
+ if (dialog.showAndGet()) {
+
+ /**
+ * Load endpoints
+ */
+ WhiskService whiskService = ServiceManager.getService(project, WhiskService.class);
+ List<WhiskEndpoint> endpoints = new ArrayList<>();
+ try {
+ endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+ } catch (IOException ex) {
+ LOG.error("Endpoint parsing failed", ex);
+ }
+
+ List<WhiskEndpoint> updatedEndpoints = removeEndpoint(endpoints, whiskEndpoint);
+ saveEndpoints(whiskService, updatedEndpoints);
+
+ // Update action tree
+ EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+ NOTIFIER.notify(project, whiskEndpoint.getAlias() + " deleted successfully.", NotificationType.INFORMATION);
+ }
+ }
+ }
+
+
+ private List<WhiskEndpoint> removeEndpoint(List<WhiskEndpoint> endpoints, WhiskEndpoint endpoint) {
+ return endpoints.stream()
+ .filter(ep -> !ep.getAlias().equals(endpoint.getAlias())) // remove endpoint
+ .collect(Collectors.toList());
+ }
+
+ private void saveEndpoints(WhiskService whiskService, List<WhiskEndpoint> newEndpoints) {
+ try {
+ String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+ whiskService.setEndpoints(eps);
+ whiskService.loadState(whiskService);
+ } catch (JsonProcessingException e) {
+ LOG.error("Endpoint parsing failed", e);
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteNamespaceAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteNamespaceAction.java
new file mode 100644
index 0000000..6be6fc9
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteNamespaceAction.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.namespace.DeleteNamespaceDialog;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.intellij.icons.AllIcons.General.Remove;
+
+public class DeleteNamespaceAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(DeleteNamespaceAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskNamespace whiskNamespace;
+
+ public DeleteNamespaceAction(WhiskNamespace whiskNamespace) {
+ super("Delete Namespace", "Delete Namespace", Remove);
+ this.whiskNamespace = whiskNamespace;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskNamespace != null) {
+ final Project project = e.getProject();
+ DeleteNamespaceDialog dialog = new DeleteNamespaceDialog(project, whiskNamespace);
+ if (dialog.showAndGet()) {
+
+ /**
+ * Load endpoints
+ */
+ WhiskService whiskService = ServiceManager.getService(project, WhiskService.class);
+ List<WhiskEndpoint> endpoints = new ArrayList<>();
+ try {
+ endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+ } catch (IOException ex) {
+ LOG.error("Endpoint parsing failed", ex);
+ }
+
+ List<WhiskEndpoint> updatedEndpoints = removeNamespace(endpoints, whiskNamespace);
+ saveEndpoints(whiskService, updatedEndpoints);
+
+ // Update action tree
+ EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+ NOTIFIER.notify(project, whiskNamespace.getPath() + " deleted successfully.", NotificationType.INFORMATION);
+ }
+ }
+ }
+
+
+ private List<WhiskEndpoint> removeNamespace(List<WhiskEndpoint> endpoints, WhiskNamespace namespace) {
+ return endpoints.stream()
+ .peek(ep -> {
+ List<WhiskNamespace> namespaces = ep.getNamespaces()
+ .stream()
+ .filter(ns -> !ns.getPath().equals(namespace.getPath()) && !ns.getAuth().equals(namespace.getAuth())) // remove namespace
+ .collect(Collectors.toList());
+ ep.setNamespaces(namespaces);
+ })
+ .collect(Collectors.toList());
+ }
+
+ private void saveEndpoints(WhiskService whiskService, List<WhiskEndpoint> newEndpoints) {
+ try {
+ String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+ whiskService.setEndpoints(eps);
+ whiskService.loadState(whiskService);
+ } catch (JsonProcessingException e) {
+ LOG.error("Endpoint parsing failed", e);
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeletePackageAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeletePackageAction.java
new file mode 100644
index 0000000..00c29ec
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeletePackageAction.java
@@ -0,0 +1,129 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskPackageService;
+import com.navercorp.openwhisk.intellij.explorer.dialog.pkg.DeletePackageDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static com.intellij.icons.AllIcons.Actions.GC;
+
+public class DeletePackageAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(DeletePackageAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskAuth whiskAuth;
+ private WhiskPackage whiskPackage;
+
+ private WhiskPackageService whiskPackageService = WhiskPackageService.getInstance();
+ private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+
+ public DeletePackageAction(WhiskAuth whiskAuth, WhiskPackage whiskPackage) {
+ super("Delete", "Delete package", GC);
+ this.whiskPackage = whiskPackage;
+ this.whiskAuth = whiskAuth;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskAuth != null && whiskPackage != null) {
+ try {
+ whiskPackageService.getWhiskPackage(whiskAuth, whiskPackage.getNamespace(), whiskPackage.getName()).ifPresent(whiskPackageWithActions -> {
+ if ((new DeletePackageDialog(e.getProject(), whiskPackageWithActions)).showAndGet()) {
+ deletePackage(e, whiskPackageWithActions);
+ }
+ });
+ } catch (IOException ex) {
+ final String msg = "The package cannot be deleted: " + whiskPackage.getName();
+ LOG.error(msg, ex);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+ }
+ }
+ }
+
+ private void deletePackage(AnActionEvent e, WhiskPackageWithActions whiskPackageWithActions) {
+ try {
+ /**
+ * Delete actions
+ */
+ List<ExecutableWhiskAction> deletedActions = new ArrayList<>();
+ if (!whiskPackageWithActions.getBinding().isPresent()) {
+ deletedActions.addAll(deleteActions(e, whiskPackageWithActions));
+ }
+
+ /**
+ * Delete package
+ */
+ Optional<WhiskPackageWithActions> deletedPackage = whiskPackageService.deleteWhiskPackage(whiskAuth, whiskPackage.getName());
+ if (deletedPackage.isPresent()) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("<html>");
+ builder.append("The following entities were deleted<br/>");
+ builder.append("- " + deletedPackage.get().getName() + "<br/>");
+ for (ExecutableWhiskAction deleted : deletedActions) {
+ builder.append("- " + deletedPackage.get().getName() + "/" + deleted.getName() + "<br/>");
+ }
+ builder.append("</html>");
+ String msg = builder.toString();
+ LOG.info(msg);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.INFORMATION);
+
+ ActionManager.getInstance().getAction("WhiskExplorer.Actions.Controls.Refresh").actionPerformed(e);
+ } else {
+ String msg = whiskPackageWithActions.getName() + " package can't be deleted";
+ LOG.info(msg);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+ }
+ } catch (IOException ex) {
+ String msg = whiskPackageWithActions.getName() + " package can't be deleted";
+ LOG.error(msg, ex);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+ }
+ }
+
+ private List<ExecutableWhiskAction> deleteActions(AnActionEvent e, WhiskPackageWithActions whiskPackageWithActions) {
+ List<ExecutableWhiskAction> deletedActions = new ArrayList<>();
+ try {
+ for (CompactWhiskAction action : whiskPackageWithActions.getActions()) {
+ whiskActionService.deleteWhiskActions(whiskAuth, Optional.of(whiskPackageWithActions.getName()), action.getName())
+ .ifPresent(deletedActions::add);
+ }
+ } catch (IOException ex) {
+ String msg = whiskPackageWithActions.getName() + " action can't be deleted";
+ LOG.error(msg, ex);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+ }
+ return deletedActions;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteTriggerAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteTriggerAction.java
new file mode 100644
index 0000000..4867b19
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteTriggerAction.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.DeleteTriggerDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+
+import static com.intellij.icons.AllIcons.Actions.GC;
+
+public class DeleteTriggerAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(DeleteTriggerAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskTriggerMetaData whiskTriggerMetaData;
+ private WhiskAuth whiskAuth;
+
+ private WhiskTriggerService whiskTriggerService;
+
+ public DeleteTriggerAction(WhiskAuth whiskAuth, WhiskTriggerMetaData whiskTriggerMetaData) {
+ super("Delete", "Delete trigger", GC);
+ this.whiskTriggerMetaData = whiskTriggerMetaData;
+ this.whiskAuth = whiskAuth;
+ this.whiskTriggerService = WhiskTriggerService.getInstance();
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskAuth != null && whiskTriggerMetaData != null) {
+ if ((new DeleteTriggerDialog(e.getProject(), whiskTriggerMetaData)).showAndGet()) {
+ try {
+ whiskTriggerService.deleteWhiskTrigger(whiskAuth, whiskTriggerMetaData.getName()).ifPresent(deletedTrigger -> {
+ String msg = deletedTrigger.getName() + " trigger is deleted";
+ LOG.info(msg);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.INFORMATION);
+ });
+ ActionManager.getInstance().getAction("WhiskExplorer.Actions.Controls.Refresh").actionPerformed(e);
+ } catch (IOException ex) {
+ final String msg = "The trigger cannot be deleted: " + whiskTriggerMetaData.getName();
+ LOG.error(msg, ex);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditActionAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditActionAction.java
new file mode 100644
index 0000000..aa5be37
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditActionAction.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.ActionManagerDialog;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class EditActionAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(EditActionAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskAuth whiskAuth;
+ private WhiskActionMetaData whiskActionMetaData;
+
+ private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+
+ public EditActionAction(WhiskAuth whiskAuth, WhiskActionMetaData whiskActionMetaData) {
+ super("Edit", "Edit Action", Edit);
+ this.whiskAuth = whiskAuth;
+ this.whiskActionMetaData = whiskActionMetaData;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskAuth != null && whiskActionMetaData != null) {
+ try {
+ List<WhiskActionMetaData> actions = whiskActionService.getWhiskActions(whiskAuth);
+ whiskActionService.getWhiskAction(whiskAuth,
+ Optional.of(whiskActionMetaData.getNamespacePath()),
+ whiskActionMetaData.getWhiskPackage(),
+ whiskActionMetaData.getName())
+ .ifPresent(action -> {
+ ActionManagerDialog dialog = new ActionManagerDialog(e.getProject(), whiskAuth, action, actions);
+ if (dialog.showAndGet()) {
+ LOG.info("ActionManagerDialog is closed");
+ }
+ });
+ } catch (IOException ex) {
+ final String msg = "The action cannot be loaded: " + whiskActionMetaData.getFullyQualifiedName();
+ LOG.error(msg, ex);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditEndpointAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditEndpointAction.java
new file mode 100644
index 0000000..064eddd
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditEndpointAction.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.EditEndpointDialog;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class EditEndpointAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(EditEndpointAction.class);
+
+ private WhiskEndpoint whiskEndpoint;
+
+ public EditEndpointAction(WhiskEndpoint whiskEndpoint) {
+ super("Edit Endpoint", "Edit Endpoint", Edit);
+ this.whiskEndpoint = whiskEndpoint;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent event) {
+ EditEndpointDialog dialog = new EditEndpointDialog(event.getProject(), whiskEndpoint);
+ if (dialog.showAndGet()) {
+ LOG.info("closed");
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditNamespaceAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditNamespaceAction.java
new file mode 100644
index 0000000..7708f46
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditNamespaceAction.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.explorer.dialog.namespace.EditNamespaceDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class EditNamespaceAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(EditNamespaceAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskAuth whiskAuth;
+ private WhiskNamespace whiskNamespace;
+
+ public EditNamespaceAction(WhiskAuth whiskAuth, WhiskNamespace whiskNamespace) {
+ super("Edit Namespace", "Edit Namespace", Edit);
+ this.whiskAuth = whiskAuth;
+ this.whiskNamespace = whiskNamespace;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskAuth != null) {
+ EditNamespaceDialog dialog = new EditNamespaceDialog(e.getProject(), whiskAuth, whiskNamespace);
+ if (dialog.showAndGet()) {
+ LOG.info("EditNamespaceDialog is closed");
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditPackageAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditPackageAction.java
new file mode 100644
index 0000000..b0b7838
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditPackageAction.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskPackageService;
+import com.navercorp.openwhisk.intellij.explorer.dialog.pkg.PackageManagerDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class EditPackageAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(EditPackageAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskAuth whiskAuth;
+ private WhiskPackage whiskPackage;
+
+ private WhiskPackageService whiskPackageService = WhiskPackageService.getInstance();
+
+ public EditPackageAction(WhiskAuth whiskAuth, WhiskPackage whiskPackage) {
+ super("Edit", "Edit Package", Edit);
+ this.whiskAuth = whiskAuth;
+ this.whiskPackage = whiskPackage;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskAuth != null) {
+ try {
+ whiskPackageService.getWhiskPackage(whiskAuth, whiskPackage.getNamespace(), whiskPackage.getName())
+ .ifPresent(whiskPackageWithActions -> {
+ PackageManagerDialog dialog = new PackageManagerDialog(e.getProject(), whiskAuth, whiskPackageWithActions);
+ if (dialog.showAndGet()) {
+ LOG.info("PackageManagerDialog is closed");
+ }
+ });
+ } catch (IOException ex) {
+ final String msg = "The package cannot be loaded: " + whiskPackage.getName();
+ LOG.error(msg, ex);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditTriggerAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditTriggerAction.java
new file mode 100644
index 0000000..c9f9d83
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditTriggerAction.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.TriggerManagerDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.List;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class EditTriggerAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(EditTriggerAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskAuth whiskAuth;
+ private WhiskTriggerMetaData whiskTriggerMetaData;
+
+ private WhiskTriggerService whiskTriggerService = WhiskTriggerService.getInstance();
+ private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+
+ public EditTriggerAction(WhiskAuth whiskAuth, WhiskTriggerMetaData whiskTriggerMetaData) {
+ super("Edit", "Edit trigger", Edit);
+ this.whiskAuth = whiskAuth;
+ this.whiskTriggerMetaData = whiskTriggerMetaData;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskAuth != null && whiskTriggerMetaData != null) {
+ try {
+ whiskTriggerService.getWhiskTrigger(whiskAuth, whiskTriggerMetaData.getName())
+ .ifPresent(executableWhiskTrigger -> openEditTriggerDialog(e.getProject(), executableWhiskTrigger));
+ } catch (IOException ex) {
+ final String msg = "The trigger cannot be loaded: " + whiskTriggerMetaData.getName();
+ LOG.error(msg, ex);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+ }
+ }
+ }
+
+ private void openEditTriggerDialog(Project project, ExecutableWhiskTrigger executableWhiskTrigger) {
+ try {
+ List<WhiskActionMetaData> actions = whiskActionService.getWhiskActions(whiskAuth);
+ TriggerManagerDialog dialog = new TriggerManagerDialog(project, whiskAuth, executableWhiskTrigger, actions);
+ if (dialog.showAndGet()) {
+ LOG.info("TriggerManagerDialog is closed");
+ }
+ } catch (IOException ex) {
+ final String msg = "The trigger cannot be loaded: " + whiskTriggerMetaData.getName();
+ LOG.error(msg, ex);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenActivationViewAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenActivationViewAction.java
new file mode 100644
index 0000000..cea1e45
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenActivationViewAction.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.explorer.editor.ActivationViewVirtualFile;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.intellij.icons.AllIcons.Nodes.Module;
+
+public class OpenActivationViewAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(OpenActivationViewAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private List<WhiskEndpoint> endpoints;
+
+ private Optional<WhiskAuth> whiskAuth;
+ private Optional<WhiskActionMetaData> action;
+ private Optional<WhiskTriggerMetaData> trigger;
+
+ public OpenActivationViewAction(List<WhiskEndpoint> endpoints,
+ Optional<WhiskAuth> whiskAuth,
+ Optional<WhiskActionMetaData> action,
+ Optional<WhiskTriggerMetaData> trigger) {
+ super("Activation", "Open activation view", Module);
+ this.endpoints = endpoints;
+ this.whiskAuth = whiskAuth;
+ this.action = action;
+ this.trigger = trigger;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ final Project project = e.getProject();
+ FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
+ VirtualFile virtualFile = new ActivationViewVirtualFile(project, endpoints, whiskAuth, action, trigger);
+ fileEditorManager.openFile(virtualFile, true);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndFireTriggerAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndFireTriggerAction.java
new file mode 100644
index 0000000..adaad0b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndFireTriggerAction.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.OpenAndFireTriggerControlActionListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+
+import static com.intellij.icons.AllIcons.Actions.Execute;
+
+public class OpenAndFireTriggerAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(OpenAndFireTriggerAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskTriggerMetaData whiskTriggerMetaData;
+ private WhiskAuth whiskAuth;
+
+ private WhiskTriggerService whiskTriggerService;
+
+ public OpenAndFireTriggerAction(WhiskAuth whiskAuth, WhiskTriggerMetaData whiskTriggerMetaData) {
+ super("Fire", "Fire trigger", Execute);
+ this.whiskTriggerMetaData = whiskTriggerMetaData;
+ this.whiskAuth = whiskAuth;
+ this.whiskTriggerService = WhiskTriggerService.getInstance();
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskAuth != null && whiskTriggerMetaData != null) {
+ try {
+ whiskTriggerService.getWhiskTrigger(whiskAuth, whiskTriggerMetaData.getName()).ifPresent(executableWhiskTrigger ->
+ EventUtils.publish(e.getProject(),
+ OpenAndFireTriggerControlActionListener.TOPIC,
+ (l) -> l.openAndFireTriggerControlWindow(whiskAuth, executableWhiskTrigger)));
+ } catch (IOException ex) {
+ final String msg = "The trigger cannot be loaded: " + whiskTriggerMetaData.getName();
+ LOG.error(msg, ex);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndRunActionAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndRunActionAction.java
new file mode 100644
index 0000000..c204f86
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndRunActionAction.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.OpenAndRunActionControlActionListener;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import static com.intellij.icons.AllIcons.Actions.Execute;
+
+public class OpenAndRunActionAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(OpenAndRunActionAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+
+ private WhiskActionMetaData whiskActionMetaData;
+ private WhiskAuth whiskAuth;
+
+ public OpenAndRunActionAction(WhiskAuth whiskAuth, WhiskActionMetaData whiskActionMetaData) {
+ super("Run", "Run action", Execute);
+ this.whiskActionMetaData = whiskActionMetaData;
+ this.whiskAuth = whiskAuth;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (whiskAuth != null && whiskActionMetaData != null) {
+ try {
+ whiskActionService.getWhiskAction(whiskAuth,
+ Optional.ofNullable(whiskActionMetaData.getNamespacePath()),
+ whiskActionMetaData.getWhiskPackage(),
+ whiskActionMetaData.getName())
+ .ifPresent(executableWhiskAction ->
+ EventUtils.publish(e.getProject(),
+ OpenAndRunActionControlActionListener.TOPIC,
+ (l) -> l.openAndRunActionControlWindow(whiskAuth, executableWhiskAction)));
+ } catch (IOException ex) {
+ final String msg = "The action cannot be loaded: " + whiskActionMetaData.getFullyQualifiedName();
+ LOG.error(msg, ex);
+ NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+ }
+
+ }
+
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/RefreshTreeAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/RefreshTreeAction.java
new file mode 100644
index 0000000..b5d7814
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/RefreshTreeAction.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import static com.intellij.icons.AllIcons.Actions.Refresh;
+
+public class RefreshTreeAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(RefreshTreeAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ public RefreshTreeAction() {
+ super(Refresh);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent event) {
+ EventUtils.publish(event.getProject(), RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskActionGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskActionGroup.java
new file mode 100644
index 0000000..e7adc8a
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskActionGroup.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.DeleteActionAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.EditActionAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.OpenActivationViewAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.OpenAndRunActionAction;
+
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskActionGroup extends DefaultActionGroup {
+
+ public WhiskActionGroup(WhiskAuth whiskAuth, WhiskActionMetaData whiskActionMetaData, List<WhiskEndpoint> endpoints) {
+ add(new OpenAndRunActionAction(whiskAuth, whiskActionMetaData));
+ add(new OpenActivationViewAction(endpoints, Optional.of(whiskAuth), Optional.of(whiskActionMetaData), Optional.empty()));
+ add(new EditActionAction(whiskAuth, whiskActionMetaData));
+ add(new DeleteActionAction(whiskAuth, whiskActionMetaData));
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskEndpointGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskEndpointGroup.java
new file mode 100644
index 0000000..637cd9a
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskEndpointGroup.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.AddNamespaceAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.DeleteEndpointAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.EditEndpointAction;
+
+public class WhiskEndpointGroup extends DefaultActionGroup {
+
+ public WhiskEndpointGroup(WhiskEndpoint whiskEndpoint) {
+ add(new EditEndpointAction(whiskEndpoint));
+ add(new DeleteEndpointAction(whiskEndpoint));
+ add(new AddNamespaceAction(whiskEndpoint));
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskNamespaceGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskNamespaceGroup.java
new file mode 100644
index 0000000..4c6ff7c
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskNamespaceGroup.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.CreatePackageAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.DeleteNamespaceAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.EditNamespaceAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.OpenActivationViewAction;
+
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskNamespaceGroup extends DefaultActionGroup {
+
+ public WhiskNamespaceGroup(WhiskAuth whiskAuth, WhiskNamespace namespace, List<WhiskEndpoint> endpoints) {
+ add(new EditNamespaceAction(whiskAuth, namespace));
+ add(new DeleteNamespaceAction(namespace));
+ add(new CreatePackageAction(whiskAuth));
+ add(new OpenActivationViewAction(endpoints, Optional.of(whiskAuth), Optional.empty(), Optional.empty()));
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskPackageGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskPackageGroup.java
new file mode 100644
index 0000000..3a700fc
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskPackageGroup.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.DeletePackageAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.EditPackageAction;
+
+public class WhiskPackageGroup extends DefaultActionGroup {
+
+ public WhiskPackageGroup(WhiskAuth whiskAuth, WhiskPackage whiskPackage) {
+ add(new EditPackageAction(whiskAuth, whiskPackage));
+ add(new DeletePackageAction(whiskAuth, whiskPackage));
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerGroup.java
new file mode 100644
index 0000000..90ea4ad
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerGroup.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.DeleteTriggerAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.EditTriggerAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.OpenActivationViewAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.OpenAndFireTriggerAction;
+
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskTriggerGroup extends DefaultActionGroup {
+
+ public WhiskTriggerGroup(WhiskAuth whiskAuth, WhiskTriggerMetaData whiskTriggerMetaData, List<WhiskEndpoint> endpoints) {
+ add(new OpenAndFireTriggerAction(whiskAuth, whiskTriggerMetaData));
+ add(new OpenActivationViewAction(endpoints, Optional.of(whiskAuth), Optional.empty(), Optional.of(whiskTriggerMetaData)));
+ add(new EditTriggerAction(whiskAuth, whiskTriggerMetaData));
+ add(new DeleteTriggerAction(whiskAuth, whiskTriggerMetaData));
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerRootGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerRootGroup.java
new file mode 100644
index 0000000..f825760
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerRootGroup.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.CreateTriggerAction;
+
+public class WhiskTriggerRootGroup extends DefaultActionGroup {
+
+ public WhiskTriggerRootGroup(WhiskAuth whiskAuth) {
+ add(new CreateTriggerAction(whiskAuth));
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenActionControlActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenActionControlActionListener.java
new file mode 100644
index 0000000..13971b7
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenActionControlActionListener.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+
+import java.util.EventListener;
+
+public interface OpenActionControlActionListener extends EventListener {
+
+ Topic<OpenActionControlActionListener> TOPIC = Topic.create("Open Action Control", OpenActionControlActionListener.class);
+
+ void openActionControlWindow(WhiskAuth whiskAuth, ExecutableWhiskAction executableWhiskAction);
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenTriggerControlActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenTriggerControlActionListener.java
new file mode 100644
index 0000000..0eb73e3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenTriggerControlActionListener.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+
+import java.util.EventListener;
+
+public interface OpenTriggerControlActionListener extends EventListener {
+
+ Topic<OpenTriggerControlActionListener> TOPIC = Topic.create("Open Trigger Control", OpenTriggerControlActionListener.class);
+
+ void openTriggerControlWindow(WhiskAuth whiskAuth, ExecutableWhiskTrigger executableWhiskTrigger);
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/RefreshWhiskTreeListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/RefreshWhiskTreeListener.java
new file mode 100644
index 0000000..1a015fc
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/RefreshWhiskTreeListener.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface RefreshWhiskTreeListener extends EventListener {
+ Topic<RefreshWhiskTreeListener> TOPIC = Topic.create("Refresh Whisk Tree", RefreshWhiskTreeListener.class);
+
+ void refreshWhiskTree();
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTree.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTree.java
new file mode 100644
index 0000000..5cd3eb9
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTree.java
@@ -0,0 +1,178 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.tree;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerRoot;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskPackageService;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskTree implements TreeModel {
+ private static final Logger LOG = Logger.getInstance(WhiskTree.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private DefaultTreeModel innerModel;
+ private DefaultMutableTreeNode root;
+ private WhiskPackageService whiskPackageService;
+
+ public WhiskTree(List<WhiskEndpoint> endpoints, WhiskPackageService whiskPackageService) {
+ this.whiskPackageService = whiskPackageService;
+ this.setTree(endpoints);
+ }
+
+ public void setTree(List<WhiskEndpoint> endpoints) {
+ this.root = new DefaultMutableTreeNode("root");
+
+ // endpoints
+ for (WhiskEndpoint e : endpoints) {
+ DefaultMutableTreeNode endPointNode = new DefaultMutableTreeNode(e);
+
+ // namespaces
+ for (WhiskNamespace ns : e.getNamespaces()) {
+ DefaultMutableTreeNode namespaceNode = new DefaultMutableTreeNode(ns);
+ endPointNode.add(namespaceNode);
+
+ //triggers
+ DefaultMutableTreeNode triggerRootNode = new DefaultMutableTreeNode(new WhiskTriggerRoot());
+ namespaceNode.add(triggerRootNode);
+ List<WhiskTriggerMetaData> triggers = ns.getTriggers();
+ for (WhiskTriggerMetaData t : triggers) {
+ DefaultMutableTreeNode triggerNode = new DefaultMutableTreeNode(t);
+ triggerRootNode.add(triggerNode);
+ }
+
+ // packages
+ List<WhiskPackage> packages = ns.getPackages();
+ for (WhiskPackage p : packages) {
+ DefaultMutableTreeNode pkgNode = new DefaultMutableTreeNode(p);
+ namespaceNode.add(pkgNode);
+
+ // Add origin action of binding package to tree
+ p.getBinding().ifPresent(binding -> {
+ try {
+ whiskPackageService.getWhiskPackage(new WhiskAuth(ns.getAuth(), e.getApihost()), binding.getNamespace(), binding.getName())
+ .ifPresent(packageWithActions ->
+ packageWithActions.getActions()
+ .forEach(action -> {
+ DefaultMutableTreeNode actionNode = new DefaultMutableTreeNode(action);
+ pkgNode.add(actionNode);
+ }));
+ } catch (IOException ex) {
+ final String msg = binding.getNamespace() + "/" + binding.getName() + " package information cannot be retrieved.";
+ LOG.error(msg, ex);
+ NOTIFIER.notify(msg, NotificationType.ERROR);
+ ex.printStackTrace();
+ }
+ });
+ }
+
+ // actions
+ List<WhiskActionMetaData> actions = ns.getActions();
+ for (WhiskActionMetaData a : actions) {
+ DefaultMutableTreeNode actionNode = new DefaultMutableTreeNode(a);
+ if (a.getWhiskPackage().isPresent()) {
+ String pkgPath = a.getWhiskPackage().get();
+ Optional<DefaultMutableTreeNode> pkgNode = findPackageNode(namespaceNode, pkgPath);
+ if (pkgNode.isPresent()) {
+ pkgNode.get().add(actionNode);
+ } else {
+ namespaceNode.add(actionNode);
+ }
+ } else {
+ namespaceNode.add(actionNode);
+ }
+ }
+ }
+ this.root.add(endPointNode);
+ }
+
+ this.innerModel = new DefaultTreeModel(root);
+ }
+
+ // Search 1 depth
+ private Optional<DefaultMutableTreeNode> findPackageNode(DefaultMutableTreeNode ns, String pkgPath) {
+ for (int i = 0; i < ns.getChildCount(); i++) {
+ DefaultMutableTreeNode children = (DefaultMutableTreeNode) ns.getChildAt(i);
+ if (children.getUserObject() instanceof WhiskPackage) {
+ WhiskPackage existPkg = (WhiskPackage) children.getUserObject();
+ if (pkgPath.equals(existPkg.getName())) {
+ return Optional.of(children);
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Object getRoot() {
+ return this.innerModel.getRoot();
+ }
+
+ @Override
+ public Object getChild(Object parent, int index) {
+ return this.innerModel.getChild(parent, index);
+ }
+
+ @Override
+ public int getChildCount(Object parent) {
+ return this.innerModel.getChildCount(parent);
+ }
+
+ @Override
+ public boolean isLeaf(Object node) {
+ return this.innerModel.isLeaf(node);
+ }
+
+ @Override
+ public void valueForPathChanged(TreePath path, Object newValue) {
+ this.innerModel.valueForPathChanged(path, newValue);
+
+ }
+
+ @Override
+ public int getIndexOfChild(Object parent, Object child) {
+ return this.innerModel.getIndexOfChild(parent, child);
+ }
+
+ @Override
+ public void addTreeModelListener(TreeModelListener l) {
+ this.innerModel.addTreeModelListener(l);
+
+ }
+
+ @Override
+ public void removeTreeModelListener(TreeModelListener l) {
+ this.innerModel.removeTreeModelListener(l);
+
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTreeCellRenderer.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTreeCellRenderer.java
new file mode 100644
index 0000000..f910a9f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTreeCellRenderer.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.tree;
+
+import com.intellij.ui.ColoredTreeCellRenderer;
+import com.navercorp.openwhisk.intellij.common.Icons;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerRoot;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+
+import static com.intellij.icons.AllIcons.Modules.SourceRoot;
+import static com.intellij.icons.AllIcons.Nodes.Module;
+import static com.intellij.icons.AllIcons.Webreferences.Server;
+
+public class WhiskTreeCellRenderer extends ColoredTreeCellRenderer {
+ @Override
+ public void customizeCellRenderer(@NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+ DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
+ Object userObject = treeNode.getUserObject();
+ if (userObject instanceof WhiskEndpoint) {
+ WhiskEndpoint whiskEndpoint = (WhiskEndpoint) userObject;
+ setIcon(Server);
+ append(whiskEndpoint.getAlias() + " (" + whiskEndpoint.getApihost() + ")");
+ } else if (userObject instanceof WhiskNamespace) {
+ WhiskNamespace whiskNamespace = (WhiskNamespace) userObject;
+ setIcon(SourceRoot);
+ append(whiskNamespace.getPath());
+ } else if (userObject instanceof WhiskPackage) {
+ WhiskPackage whiskPackage = (WhiskPackage) userObject;
+ setIcon(Module);
+ String boundMark = whiskPackage.getBinding().map(b -> " (from " + b.getNamespace() + "/" + b.getName() + ")").orElse("");
+ append(whiskPackage.getName() + boundMark);
+ } else if (userObject instanceof WhiskAction) {
+ WhiskAction whiskAction = (WhiskAction) userObject;
+ setIcon(whiskAction.getKindIcon());
+ append(whiskAction.getName() + whiskAction.getKindExtension());
+ } else if (userObject instanceof CompactWhiskAction) {
+ CompactWhiskAction compactWhiskAction = (CompactWhiskAction) userObject;
+ setIcon(compactWhiskAction.getKindIcon());
+ append(compactWhiskAction.getName() + compactWhiskAction.getKindExtension());
+ } else if (userObject instanceof WhiskTriggerRoot) {
+ append("Triggers");
+ setIcon(Icons.ENTITY_TRIGGER_ROOT);
+ } else if (userObject instanceof WhiskTriggerMetaData) {
+ WhiskTriggerMetaData whiskTriggerMetaData = (WhiskTriggerMetaData) userObject;
+ setIcon(Icons.ENTITY_TRIGGER);
+ append(whiskTriggerMetaData.getName());
+ } else {
+ append(userObject.toString());
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.form
new file mode 100644
index 0000000..28ee2c3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.form
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.toolwindow.ui.WhiskExplorerWindowForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="9f9d2" binding="actionsJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints border-constraint="North"/>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <scrollpane id="4961f" binding="contentsJPanel">
+ <constraints border-constraint="Center"/>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="7b9b9" class="javax.swing.JTree" binding="whiskJTree">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.java
new file mode 100644
index 0000000..0ecf27e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.java
@@ -0,0 +1,462 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.toolwindow.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionPopupMenu;
+import com.intellij.openapi.actionSystem.ActionToolbar;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.ToolWindow;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.FileUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerRoot;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskPackageService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.actiongroup.*;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.OpenActionControlActionListener;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.OpenTriggerControlActionListener;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.tree.WhiskTree;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.tree.WhiskTreeCellRenderer;
+import org.apache.commons.lang.StringUtils;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskExplorerWindowForm {
+ private static final Logger LOG = Logger.getInstance(WhiskExplorerWindowForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private JPanel mainJPanel;
+ private JPanel actionsJPanel;
+ private JScrollPane contentsJPanel;
+ private JTree whiskJTree;
+
+ private FileEditorManager fileEditorManager;
+ private Project project;
+
+ private List<WhiskEndpoint> endpoints = new ArrayList<>();
+
+ public WhiskExplorerWindowForm(Project project, ToolWindow toolWindow) {
+ this.project = project;
+ this.fileEditorManager = FileEditorManager.getInstance(project);
+
+ String actionGroupName = "WhiskExplorer.Actions.Controls";
+ ActionManager actionManager = ActionManager.getInstance();
+ ActionGroup actionGroup = (ActionGroup) actionManager.getAction(actionGroupName);
+ ActionToolbar actionToolbar = actionManager.createActionToolbar("", actionGroup, true);
+
+ actionToolbar.setTargetComponent(actionsJPanel);
+ actionsJPanel.add(actionToolbar.getComponent());
+
+ WhiskService service = ServiceManager.getService(project, WhiskService.class);
+ WhiskActionService whiskActionService = WhiskActionService.getInstance();
+ WhiskPackageService whiskPackageService = WhiskPackageService.getInstance();
+ WhiskTriggerService whiskTriggerService = WhiskTriggerService.getInstance();
+
+ setWhiskTree(service, whiskActionService, whiskPackageService, whiskTriggerService);
+ }
+
+ private void setWhiskTree(WhiskService service,
+ WhiskActionService whiskActionService,
+ WhiskPackageService whiskPackageService,
+ WhiskTriggerService whiskTriggerService) {
+
+ if (StringUtils.isNotEmpty(service.getEndpoints())) {
+ try {
+ endpoints = getEntities(whiskPackageService,
+ whiskActionService,
+ whiskTriggerService,
+ JsonParserUtils.parseWhiskEndpoints(service.getEndpoints()));
+ } catch (IOException e) {
+ final String msg = "Failed to parsing endpoints: " + service.getEndpoints();
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }
+
+ whiskJTree.setModel(new WhiskTree(endpoints, whiskPackageService));
+ whiskJTree.setRootVisible(false);
+ whiskJTree.setCellRenderer(new WhiskTreeCellRenderer());
+ expandToNamespace(whiskJTree);
+ whiskJTree.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() > 1) {
+ if (whiskJTree == null) {
+ return;
+ }
+
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) whiskJTree.getLastSelectedPathComponent();
+ if (node == null) {
+ return;
+ }
+
+ Object userObject = node.getUserObject();
+ if (userObject instanceof WhiskEndpoint) {
+ WhiskEndpoint whiskEndpoint = (WhiskEndpoint) userObject;
+ LOG.info(whiskEndpoint.getAlias());
+ } else if (userObject instanceof WhiskNamespace) {
+ WhiskNamespace whiskNamespace = (WhiskNamespace) userObject;
+ LOG.info(whiskNamespace.getPath());
+ } else if (userObject instanceof WhiskPackage) {
+ WhiskPackage whiskPackage = (WhiskPackage) userObject;
+ LOG.info(whiskPackage.getName());
+ } else if (userObject instanceof WhiskActionMetaData) {
+ WhiskActionMetaData whiskAction = (WhiskActionMetaData) userObject;
+ LOG.info(whiskAction.getName());
+
+ DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) node.getParent();
+ if (parentNode.getUserObject() instanceof WhiskPackage) {
+ // TODO refactor getAuthFromTreeNode method
+ WhiskPackage pkg = (WhiskPackage) parentNode.getUserObject();
+
+ DefaultMutableTreeNode namespaceNode = (DefaultMutableTreeNode) parentNode.getParent();
+ WhiskNamespace namespace = (WhiskNamespace) namespaceNode.getUserObject();
+
+ DefaultMutableTreeNode endpointNode = (DefaultMutableTreeNode) namespaceNode.getParent();
+ WhiskEndpoint endpoint = (WhiskEndpoint) endpointNode.getUserObject();
+
+ WhiskAuth auth = new WhiskAuth(namespace.getAuth(), endpoint.getApihost());
+
+ try {
+ whiskActionService.getWhiskAction(auth,
+ Optional.ofNullable(namespace.getPath()),
+ Optional.ofNullable(pkg.getName()), whiskAction.getName())
+ .ifPresent(executableWhiskAction -> openEditorAndWhiskRunWindow(auth, executableWhiskAction));
+ } catch (IOException e1) {
+ final String msg1 = "The action cannot be opened.";
+ LOG.error(msg1, e1);
+ NOTIFIER.notify(project, msg1, NotificationType.ERROR);
+ }
+ } else if (parentNode.getUserObject() instanceof WhiskNamespace) {
+ WhiskNamespace namespace = (WhiskNamespace) parentNode.getUserObject();
+
+ DefaultMutableTreeNode endpointNode = (DefaultMutableTreeNode) parentNode.getParent();
+ WhiskEndpoint endpoint = (WhiskEndpoint) endpointNode.getUserObject();
+
+ WhiskAuth auth = new WhiskAuth(namespace.getAuth(), endpoint.getApihost());
+
+ try {
+ whiskActionService.getWhiskAction(auth, Optional.ofNullable(namespace.getPath()), Optional.empty(), whiskAction.getName())
+ .ifPresent(executableWhiskAction -> openEditorAndWhiskRunWindow(auth, executableWhiskAction));
+ } catch (IOException e1) {
+ final String msg1 = "The action cannot be opened.";
+ LOG.error(msg1, e1);
+ NOTIFIER.notify(project, msg1, NotificationType.ERROR);
+ }
+
+ } else {
+ LOG.error("[WhiskActionMetaData] The action cannot be loaded: " + whiskAction.getFullyQualifiedName());
+ }
+ } else if (userObject instanceof CompactWhiskAction) {
+ CompactWhiskAction action = (CompactWhiskAction) userObject;
+ LOG.info(action.getName());
+
+ DefaultMutableTreeNode boundPkgNode = (DefaultMutableTreeNode) node.getParent();
+ if (boundPkgNode.getUserObject() instanceof WhiskPackage) {
+ WhiskPackage boundPkg = (WhiskPackage) boundPkgNode.getUserObject();
+
+ DefaultMutableTreeNode namespaceNode = (DefaultMutableTreeNode) boundPkgNode.getParent();
+ WhiskNamespace namespace = (WhiskNamespace) namespaceNode.getUserObject();
+
+ DefaultMutableTreeNode endpointNode = (DefaultMutableTreeNode) namespaceNode.getParent();
+ WhiskEndpoint endpoint = (WhiskEndpoint) endpointNode.getUserObject();
+
+ WhiskAuth auth = new WhiskAuth(namespace.getAuth(), endpoint.getApihost());
+
+ boundPkg.getBinding().ifPresent(p -> {
+ try {
+ //When clicked, the code is fetched from the remote server.
+ whiskActionService.getWhiskAction(auth,
+ Optional.ofNullable(p.getNamespace()),
+ Optional.ofNullable(p.getName()), action.getName())
+ .ifPresent(executableWhiskAction -> openEditorAndWhiskRunWindow(auth, executableWhiskAction));
+ } catch (IOException e1) {
+ final String msg1 = "The action cannot be opened.";
+ LOG.error(msg1, e1);
+ NOTIFIER.notify(project, msg1, NotificationType.ERROR);
+ }
+
+ });
+ } else {
+ LOG.error("[CompactWhiskAction] The action cannot be loaded: " + action.getName());
+ }
+ } else if (userObject instanceof WhiskTriggerRoot) {
+ // Nothing to do
+ LOG.debug("[WhiskTriggerRoot] is selected");
+ } else if (userObject instanceof WhiskTriggerMetaData) {
+ DefaultMutableTreeNode namespaceNode = (DefaultMutableTreeNode) node.getParent().getParent();
+
+ WhiskNamespace namespace = (WhiskNamespace) namespaceNode.getUserObject();
+
+ DefaultMutableTreeNode endpointNode = (DefaultMutableTreeNode) namespaceNode.getParent();
+ WhiskEndpoint endpoint = (WhiskEndpoint) endpointNode.getUserObject();
+
+ WhiskAuth auth = new WhiskAuth(namespace.getAuth(), endpoint.getApihost());
+
+ WhiskTriggerMetaData whiskTriggerMetaData = (WhiskTriggerMetaData) userObject;
+ LOG.info(whiskTriggerMetaData.getName());
+ try {
+ whiskTriggerService.getWhiskTrigger(auth, whiskTriggerMetaData.getName()).ifPresent(executableWhiskTrigger ->
+ EventUtils.publish(project,
+ OpenTriggerControlActionListener.TOPIC,
+ (l) -> l.openTriggerControlWindow(auth, executableWhiskTrigger)));
+ } catch (IOException ex) {
+ final String msg = "The trigger cannot be loaded: " + whiskTriggerMetaData.getName();
+ LOG.error(msg, ex);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ } else {
+ LOG.error("The unknown object of tree: " + userObject.toString());
+ }
+ }
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON3) {
+ if (whiskJTree == null) {
+ return;
+ }
+
+ TreePath path = whiskJTree.getPathForLocation(e.getX(), e.getY());
+ if (path == null) {
+ return;
+ }
+
+ whiskJTree.setSelectionPath(path);
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
+ if (node.getUserObject() instanceof WhiskEndpoint) {
+ WhiskEndpoint whiskEndpoint = (WhiskEndpoint) node.getUserObject();
+ ActionGroup actionGroup = new WhiskEndpointGroup(whiskEndpoint);
+ ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+ actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+ } else if (node.getUserObject() instanceof WhiskNamespace) {
+ getAuthFromTreeNode(node).ifPresent(auth -> {
+ WhiskNamespace whiskNamespace = (WhiskNamespace) node.getUserObject();
+ ActionGroup actionGroup = new WhiskNamespaceGroup(auth, whiskNamespace, endpoints);
+ ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+ actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+ });
+ } else if (node.getUserObject() instanceof WhiskActionMetaData) {
+ WhiskActionMetaData whiskActionMetaData = (WhiskActionMetaData) node.getUserObject();
+ getAuthFromTreeNode(node).ifPresent(auth -> {
+ ActionGroup actionGroup = new WhiskActionGroup(auth, whiskActionMetaData, endpoints);
+ ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+ actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+ });
+ } else if (node.getUserObject() instanceof CompactWhiskAction) {
+ CompactWhiskAction compactWhiskAction = (CompactWhiskAction) node.getUserObject();
+ WhiskPackage pkg = (WhiskPackage) ((DefaultMutableTreeNode) node.getParent()).getUserObject();
+ WhiskNamespace ns = (WhiskNamespace) ((DefaultMutableTreeNode) node.getParent().getParent()).getUserObject();
+
+ getAuthFromTreeNode(node).ifPresent(auth -> {
+ try {
+ whiskActionService.getWhiskAction(auth, Optional.of(ns.getPath()), Optional.of(pkg.getName()), compactWhiskAction.getName())
+ .ifPresent(action -> {
+ ActionGroup actionGroup = new WhiskActionGroup(auth, toBindingWhiskActionMetaData(pkg, action), endpoints);
+ ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+ actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+ });
+ } catch (IOException ex) {
+ final String msg = "The action cannot be loaded.";
+ LOG.error(msg, ex);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ });
+ } else if (node.getUserObject() instanceof WhiskTriggerMetaData) {
+ WhiskTriggerMetaData whiskTriggerMetaData = (WhiskTriggerMetaData) node.getUserObject();
+ getAuthFromTreeNode(node).ifPresent(auth -> {
+ ActionGroup actionGroup = new WhiskTriggerGroup(auth, whiskTriggerMetaData, endpoints);
+ ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+ actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+ });
+ } else if (node.getUserObject() instanceof WhiskTriggerRoot) {
+ getAuthFromTreeNode(node).ifPresent(auth -> {
+ ActionGroup actionGroup = new WhiskTriggerRootGroup(auth);
+ ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+ actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+ });
+ } else if (node.getUserObject() instanceof WhiskPackage) {
+ getAuthFromTreeNode(node).ifPresent(auth -> {
+ WhiskPackage whiskPackage = (WhiskPackage) node.getUserObject();
+ ActionGroup actionGroup = new WhiskPackageGroup(auth, whiskPackage);
+ ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+ actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+ });
+ }
+ }
+ }
+ });
+
+ EventUtils.subscribe(project, project, RefreshWhiskTreeListener.TOPIC, () -> {
+ if (whiskJTree != null) {
+ try {
+ List<WhiskEndpoint> whiskEndpoints = JsonParserUtils.parseWhiskEndpoints(service.getEndpoints());
+ endpoints = getEntities(whiskPackageService, whiskActionService, whiskTriggerService, whiskEndpoints);
+ whiskJTree.setModel(new WhiskTree(endpoints, whiskPackageService));
+ expandToNamespace(whiskJTree);
+
+ if (whiskEndpoints.isEmpty()) {
+ final String msg = "There are no endpoints saved.";
+ LOG.info(msg);
+ NOTIFIER.notify(project, msg, NotificationType.INFORMATION);
+ }
+ } catch (IOException e) {
+ final String msg = "Failed to parsing endpoints: " + service.getEndpoints();
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }
+ });
+ }
+
+ private WhiskActionMetaData toBindingWhiskActionMetaData(WhiskPackage whiskPackage, ExecutableWhiskAction action) {
+ return new WhiskActionMetaData(action.getName(),
+ whiskPackage.getNamespace() + "/" + whiskPackage.getName(),
+ action.getVersion(),
+ action.getUpdated(),
+ action.isPublish(),
+ action.getAnnotations(),
+ action.getLimits(),
+ action.getExec().toExecMetaData());
+ }
+
+
+ private void expandToNamespace(JTree tree) {
+ final Enumeration<?> topLevelNodes = ((TreeNode) tree.getModel().getRoot()).children();
+ final int level = 2; // The level is the distance from the root to this node.
+ expandNode(tree, topLevelNodes, level);
+ }
+
+ private void expandNode(JTree tree, Enumeration<?> nodes, int level) {
+ while (nodes.hasMoreElements()) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes.nextElement();
+ tree.expandPath(new TreePath(node.getPath()));
+ if (node.getLevel() < level) {
+ expandNode(tree, node.children(), level);
+ }
+ }
+ }
+
+ private void openEditorAndWhiskRunWindow(WhiskAuth auth, ExecutableWhiskAction executableWhiskAction) {
+ try {
+ final String tmpFilePath = project.getBasePath() + "/.idea/openwhisk";
+
+ if (!executableWhiskAction.isSequenceAction()) {
+ /**
+ * Open Editor
+ */
+ VirtualFile actionFile = FileUtils.writeActionToFile(tmpFilePath, executableWhiskAction);
+ fileEditorManager.openFile(actionFile, true);
+ }
+
+ /**
+ * Open Whisk Run Window
+ */
+ EventUtils.publish(project, OpenActionControlActionListener.TOPIC, (l) -> l.openActionControlWindow(auth, executableWhiskAction));
+ } catch (IOException e1) {
+ final String msg1 = "The action cannot be opened.";
+ LOG.error(msg1, e1);
+ NOTIFIER.notify(project, msg1, NotificationType.ERROR);
+ }
+
+ }
+
+ private Optional<WhiskAuth> getAuthFromTreeNode(DefaultMutableTreeNode node) {
+ try {
+ DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent();
+ if (node.getUserObject() instanceof WhiskNamespace
+ && parent.getUserObject() instanceof WhiskEndpoint) {
+ WhiskNamespace namespace = (WhiskNamespace) node.getUserObject();
+ WhiskEndpoint endpoint = (WhiskEndpoint) parent.getUserObject();
+ return Optional.of(new WhiskAuth(namespace.getAuth(), endpoint.getApihost()));
+ } else if (node.getUserObject() instanceof WhiskActionMetaData
+ || node.getUserObject() instanceof CompactWhiskAction
+ || node.getUserObject() instanceof WhiskTriggerMetaData
+ || node.getUserObject() instanceof WhiskTriggerRoot
+ || node.getUserObject() instanceof WhiskPackage
+ ) {
+ return getAuthFromTreeNode(parent);
+ }
+ } catch (Exception e) {
+ LOG.error(e);
+ return Optional.empty();
+ }
+ return Optional.empty();
+ }
+
+ private List<WhiskEndpoint> getEntities(WhiskPackageService whiskPackageService, WhiskActionService
+ whiskActionService, WhiskTriggerService whiskTriggerService, List<WhiskEndpoint> whiskEndpoints) {
+ List<WhiskEndpoint> newWhiskEndpoints = new ArrayList<>();
+ for (WhiskEndpoint ep : whiskEndpoints) {
+ List<WhiskNamespace> newNamespaces = new ArrayList<>();
+ for (WhiskNamespace ns : ep.getNamespaces()) {
+ try {
+ WhiskAuth auth = new WhiskAuth(ns.getAuth(), ep.getApihost());
+ // get pkg, action
+ ns.setPackages(whiskPackageService.getWhiskPackages(auth));
+ ns.setActions(whiskActionService.getWhiskActions(auth));
+ ns.setTriggers(whiskTriggerService.getWhiskTriggers(auth));
+
+ newNamespaces.add(ns);
+ } catch (IOException e) {
+ final String msg = ns.getPath() + " entities cannot be loaded.";
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }
+ ep.setNamespaces(newNamespaces);
+ newWhiskEndpoints.add(ep);
+ }
+ return newWhiskEndpoints;
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+
+}
+
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/WhiskRunWindowFactory.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/WhiskRunWindowFactory.java
new file mode 100644
index 0000000..8dad293
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/WhiskRunWindowFactory.java
@@ -0,0 +1,173 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.run.toolwindow;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowFactory;
+import com.intellij.openapi.wm.ToolWindowManager;
+import com.intellij.ui.content.Content;
+import com.intellij.ui.content.ContentFactory;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActivationService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.*;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.ActionManagerDialog;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.TriggerManagerDialog;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.OpenActionControlActionListener;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.OpenTriggerControlActionListener;
+import com.navercorp.openwhisk.intellij.run.toolwindow.ui.WhiskRunWindowForm;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.List;
+
+import static com.intellij.icons.AllIcons.Debugger.Console;
+import static com.navercorp.openwhisk.intellij.run.toolwindow.ui.WhiskRunWindowForm.ENTITY_ACTION;
+import static com.navercorp.openwhisk.intellij.run.toolwindow.ui.WhiskRunWindowForm.ENTITY_TRIGGER;
+
+public class WhiskRunWindowFactory implements ToolWindowFactory {
+ private static final Logger LOG = Logger.getInstance(WhiskRunWindowFactory.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private static final String ID = "Whisk Run";
+
+ private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+ private WhiskActivationService whiskActivationService = WhiskActivationService.getInstance();
+ private WhiskTriggerService whiskTriggerService = WhiskTriggerService.getInstance();
+
+ private WhiskRunWindowForm whiskRunWindowForm;
+
+ @Override
+ public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
+ this.whiskRunWindowForm = new WhiskRunWindowForm(project, toolWindow);
+ ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
+ Content content = contentFactory.createContent(whiskRunWindowForm.getContent(), null, false);
+ toolWindow.getContentManager().addContent(content);
+
+ toolWindow.setIcon(Console);
+ ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
+ toolWindowManager.getToolWindow(ID).hide(() -> {
+ });
+
+ /**
+ * Receive Action events
+ */
+ EventUtils.subscribe(project, project, OpenActionControlActionListener.TOPIC,
+ (auth, executableWhiskAction) ->
+ toolWindowManager.getToolWindow(ID).show(() ->
+ initializeWhiskTabWithAction(auth, executableWhiskAction, false)));
+
+ EventUtils.subscribe(project, project, OpenAndRunActionControlActionListener.TOPIC,
+ (auth, executableWhiskAction) ->
+ toolWindowManager.getToolWindow(ID).show(() ->
+ initializeWhiskTabWithAction(auth, executableWhiskAction, true)));
+
+ /**
+ * Receive Trigger events
+ */
+ EventUtils.subscribe(project, project, OpenTriggerControlActionListener.TOPIC, (auth, executableWhiskTrigger) ->
+ toolWindowManager.getToolWindow(ID).show(() ->
+ initializeWhiskTabWithTrigger(auth, executableWhiskTrigger, false)));
+
+ EventUtils.subscribe(project, project, OpenAndFireTriggerControlActionListener.TOPIC, (auth, executableWhiskTrigger) ->
+ toolWindowManager.getToolWindow(ID).show(() ->
+ initializeWhiskTabWithTrigger(auth, executableWhiskTrigger, true)));
+
+ /**
+ * Receive Action Toolbar events
+ */
+ EventUtils.subscribe(project, project, RunActionListener.TOPIC, () ->
+ whiskRunWindowForm.getCachedAuth().ifPresent(auth -> {
+ String entity = whiskRunWindowForm.getCurrentEntity();
+ if (entity.equals(ENTITY_ACTION)) {
+ whiskRunWindowForm.getCachedAction().ifPresent(action ->
+ whiskRunWindowForm.runAction(whiskActionService, auth, action));
+ } else if (entity.equals(ENTITY_TRIGGER)) {
+ whiskRunWindowForm.getCachedTrigger().ifPresent(trigger ->
+ whiskRunWindowForm.fireTrigger(whiskTriggerService, whiskActivationService, auth, trigger));
+ }
+ }));
+
+ EventUtils.subscribe(project, project, RefreshActionOrTriggerListener.TOPIC, () ->
+ whiskRunWindowForm.getCachedAuth().ifPresent(auth -> {
+ String entity = whiskRunWindowForm.getCurrentEntity();
+ if (entity.equals(ENTITY_ACTION)) {
+ whiskRunWindowForm.getCachedAction().ifPresent(action ->
+ whiskRunWindowForm.refreshActionMetaData(whiskActionService, auth, action));
+ } else if (entity.equals(ENTITY_TRIGGER)) {
+ whiskRunWindowForm.getCachedTrigger().ifPresent(trigger ->
+ whiskRunWindowForm.refreshTrigger(auth, whiskTriggerService, trigger));
+ }
+ }));
+
+ EventUtils.subscribe(project, project, OpenActionManagerListener.TOPIC, () ->
+ whiskRunWindowForm.getCachedAuth().ifPresent(auth -> {
+ try {
+ List<WhiskActionMetaData> actions = whiskActionService.getWhiskActions(auth);
+ String entity = whiskRunWindowForm.getCurrentEntity();
+ if (entity.equals(ENTITY_ACTION)) {
+ whiskRunWindowForm.getCachedAction().ifPresent(action -> {
+ ActionManagerDialog dialog = new ActionManagerDialog(project, auth, action, actions);
+ if (dialog.showAndGet()) {
+ LOG.info("ActionManagerDialog is closed");
+ }
+ });
+ } else if (entity.equals(ENTITY_TRIGGER)) {
+ whiskRunWindowForm.getCachedTrigger().ifPresent(trigger -> {
+ TriggerManagerDialog dialog = new TriggerManagerDialog(project, auth, trigger, actions);
+ if (dialog.showAndGet()) {
+ LOG.info("TriggerManagerDialog is closed");
+ }
+ });
+ }
+ } catch (IOException e) {
+ final String msg = "The action cannot be executed.";
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }));
+ }
+
+ private void initializeWhiskTabWithAction(WhiskAuth auth, ExecutableWhiskAction executableWhiskAction, boolean isRunAction) {
+ whiskRunWindowForm.cacheAction(executableWhiskAction);
+ whiskRunWindowForm.cacheAuth(auth);
+ whiskRunWindowForm.setTitle(executableWhiskAction.getFullyQualifiedName());
+ whiskRunWindowForm.initializeActionTab(executableWhiskAction);
+ if (isRunAction) {
+ whiskRunWindowForm.runAction(whiskActionService, auth, executableWhiskAction);
+ }
+ }
+
+ private void initializeWhiskTabWithTrigger(WhiskAuth auth, ExecutableWhiskTrigger executableWhiskTrigger, boolean isFireTrigger) {
+ whiskRunWindowForm.cacheAuth(auth);
+ whiskRunWindowForm.cacheTrigger(executableWhiskTrigger);
+ whiskRunWindowForm.setTitle(executableWhiskTrigger.getName());
+ whiskRunWindowForm.initializeTriggerTab(executableWhiskTrigger);
+ if (isFireTrigger) {
+ whiskRunWindowForm.fireTrigger(whiskTriggerService, whiskActivationService, auth, executableWhiskTrigger);
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/OpenActionManagerDialogAction.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/OpenActionManagerDialogAction.java
new file mode 100644
index 0000000..5fdce38
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/OpenActionManagerDialogAction.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.run.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.OpenActionManagerListener;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class OpenActionManagerDialogAction extends AnAction {
+
+ public OpenActionManagerDialogAction() {
+ super(Edit);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ EventUtils.publish(e.getProject(), OpenActionManagerListener.TOPIC, OpenActionManagerListener::openActionManagerDialog);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RefreshMetaDataAction.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RefreshMetaDataAction.java
new file mode 100644
index 0000000..9bf026c
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RefreshMetaDataAction.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.run.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.RefreshActionOrTriggerListener;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.Actions.Refresh;
+
+public class RefreshMetaDataAction extends AnAction {
+
+ public RefreshMetaDataAction() {
+ super(Refresh);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ EventUtils.publish(e.getProject(), RefreshActionOrTriggerListener.TOPIC, RefreshActionOrTriggerListener::fetchActionMetadata);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RunActionAction.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RunActionAction.java
new file mode 100644
index 0000000..45469a5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RunActionAction.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.run.toolwindow.action;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.RunActionListener;
+import org.jetbrains.annotations.NotNull;
+
+public class RunActionAction extends AnAction {
+
+ public RunActionAction() {
+ super(AllIcons.Actions.Execute);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ EventUtils.publish(e.getProject(), RunActionListener.TOPIC, RunActionListener::runAction);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenActionManagerListener.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenActionManagerListener.java
new file mode 100644
index 0000000..460a68f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenActionManagerListener.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.run.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface OpenActionManagerListener extends EventListener {
+
+ Topic<OpenActionManagerListener> TOPIC = Topic.create("Open Action Manager Dialog", OpenActionManagerListener.class);
+
+ void openActionManagerDialog();
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndFireTriggerControlActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndFireTriggerControlActionListener.java
new file mode 100644
index 0000000..b7265c2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndFireTriggerControlActionListener.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.run.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+
+import java.util.EventListener;
+
+public interface OpenAndFireTriggerControlActionListener extends EventListener {
+
+ Topic<OpenAndFireTriggerControlActionListener> TOPIC = Topic.create("Open and Fire Trigger Control", OpenAndFireTriggerControlActionListener.class);
+
+ void openAndFireTriggerControlWindow(WhiskAuth whiskAuth, ExecutableWhiskTrigger executableWhiskTrigger);
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndRunActionControlActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndRunActionControlActionListener.java
new file mode 100644
index 0000000..32e4e07
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndRunActionControlActionListener.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.run.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+
+import java.util.EventListener;
+
+public interface OpenAndRunActionControlActionListener extends EventListener {
+
+ Topic<OpenAndRunActionControlActionListener> TOPIC = Topic.create("Open and Run Action Control", OpenAndRunActionControlActionListener.class);
+
+ void openAndRunActionControlWindow(WhiskAuth whiskAuth, ExecutableWhiskAction executableWhiskAction);
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RefreshActionOrTriggerListener.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RefreshActionOrTriggerListener.java
new file mode 100644
index 0000000..8274caa
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RefreshActionOrTriggerListener.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.run.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface RefreshActionOrTriggerListener extends EventListener {
+
+ Topic<RefreshActionOrTriggerListener> TOPIC = Topic.create("Refresh action or trigger", RefreshActionOrTriggerListener.class);
+
+ void fetchActionMetadata();
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RunActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RunActionListener.java
new file mode 100644
index 0000000..92bca3f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RunActionListener.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.run.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface RunActionListener extends EventListener {
+
+ Topic<RunActionListener> TOPIC = Topic.create("Run Action", RunActionListener.class);
+
+ void runAction();
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.form b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.form
new file mode 100644
index 0000000..20a2e29
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.form
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.run.toolwindow.ui.WhiskRunWindowForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="4810" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <splitpane id="d27a" binding="executionJSplitPane">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="200" height="200"/>
+ </grid>
+ </constraints>
+ <properties>
+ <continuousLayout value="true"/>
+ <dividerLocation value="600"/>
+ <dividerSize value="10"/>
+ <oneTouchExpandable value="false"/>
+ </properties>
+ <border type="none"/>
+ <children>
+ <grid id="94e72" binding="parameterJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <splitpane position="left"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="e30f0" binding="executionActionsJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints border-constraint="North"/>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="e2a93" class="javax.swing.JSeparator">
+ <constraints border-constraint="South"/>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+ <scrollpane id="da4f6">
+ <constraints border-constraint="Center"/>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="75f7b" class="javax.swing.JTextArea" binding="paramJTextArea">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ </children>
+ </grid>
+ <scrollpane id="e04bb" binding="resultJScrollPane">
+ <constraints>
+ <splitpane position="right"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="a1c48" class="javax.swing.JTextArea" binding="resultJTextArea">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ </children>
+ </splitpane>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.java
new file mode 100644
index 0000000..38e330f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.java
@@ -0,0 +1,281 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.run.toolwindow.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionToolbar;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.utils.ParameterUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActivationService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import org.apache.commons.lang.StringUtils;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.IOException;
+import java.util.Optional;
+
+public class WhiskRunWindowForm {
+ private static final Logger LOG = Logger.getInstance(WhiskRunWindowForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ public static final String ENTITY_ACTION = "Action";
+ public static final String ENTITY_TRIGGER = "Trigger";
+
+ /**
+ * Main Panel.
+ */
+ private JPanel mainJPanel;
+
+ private JSplitPane executionJSplitPane;
+ // Parameter
+ private JPanel parameterJPanel;
+ private JPanel executionActionsJPanel;
+ private JTextArea paramJTextArea;
+ private JTextArea resultJTextArea;
+ private JScrollPane resultJScrollPane;
+
+ /**
+ * Cache data.
+ */
+ private ToolWindow toolWindow;
+ private Project project;
+ private Optional<ExecutableWhiskAction> currentAction = Optional.empty();
+ private Optional<ExecutableWhiskTrigger> currentTrigger = Optional.empty();
+ private Optional<WhiskAuth> currentAuth = Optional.empty();
+ private String currentEntity = "";
+
+ public WhiskRunWindowForm(Project project, ToolWindow toolWindow) {
+ this.toolWindow = toolWindow;
+ this.project = project;
+
+ final Color defaultBackgroundColor = EditorColorsManager.getInstance().getSchemeForCurrentUITheme().getDefaultBackground();
+
+ /**
+ * Action Tab
+ */
+ ActionManager actionManager = ActionManager.getInstance();
+ ActionGroup actionActionGroup = (ActionGroup) actionManager.getAction("WhiskRunWindow.Actions.Controls");
+ ActionToolbar actionActionToolbar = actionManager.createActionToolbar("", actionActionGroup, true);
+ actionActionToolbar.setTargetComponent(executionActionsJPanel);
+ executionActionsJPanel.add(actionActionToolbar.getComponent());
+
+ resultJTextArea.setText("Please run action");
+ resultJTextArea.setBackground(defaultBackgroundColor);
+ executionJSplitPane.setBackground(defaultBackgroundColor);
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+
+ public void updateResult(String result) {
+ resultJTextArea.setText(result);
+ }
+
+ public void cacheAction(ExecutableWhiskAction action) {
+ this.currentAction = Optional.ofNullable(action);
+ }
+
+ public void cacheTrigger(ExecutableWhiskTrigger trigger) {
+ this.currentTrigger = Optional.ofNullable(trigger);
+ }
+
+ public void cacheAuth(WhiskAuth auth) {
+ this.currentAuth = Optional.ofNullable(auth);
+ }
+
+ public Optional<ExecutableWhiskAction> getCachedAction() {
+ return this.currentAction;
+ }
+
+ public Optional<ExecutableWhiskTrigger> getCachedTrigger() {
+ return this.currentTrigger;
+ }
+
+ public Optional<WhiskAuth> getCachedAuth() {
+ return this.currentAuth;
+ }
+
+ public void setTitle(String title) {
+ toolWindow.setTitle(title);
+ }
+
+ public String getCurrentEntity() {
+ return currentEntity;
+ }
+
+ /**
+ * Action Tab.
+ */
+ public void initializeActionTab(ExecutableWhiskAction action) {
+ try {
+ paramJTextArea.setText(JsonParserUtils.writeParameterToJson(action.getParameters()));
+ resultJTextArea.setText("Please run action");
+ currentEntity = ENTITY_ACTION;
+ } catch (IOException e) {
+ LOG.error("Failed to parse json: " + action.getFullyQualifiedName(), e);
+ }
+ }
+
+ public void runAction(WhiskActionService whiskActionService, WhiskAuth auth, ExecutableWhiskAction action) {
+ ProgressManager.getInstance().run(new Task.Backgroundable(project, "Run action") {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ try {
+ if (StringUtils.isEmpty(paramJTextArea.getText())) {
+ paramJTextArea.setText("{}");
+ }
+
+ Optional<String> params = ParameterUtils.validateParams(paramJTextArea.getText());
+ if (params.isPresent()) {
+ String result = whiskActionService.invokeWhiskAction(auth,
+ Optional.ofNullable(action.getNamespacePath()),
+ action.getWhiskPackage(),
+ action.getName(),
+ params.get());
+ updateResult(result);
+ } else {
+ NOTIFIER.notify(project, "The json format of the parameter is incorrect.", NotificationType.ERROR);
+ }
+ } catch (IOException e) {
+ LOG.error("Failed to invoke action: " + action.getFullyQualifiedName(), e);
+ }
+
+ }
+ });
+ }
+
+ public void refreshActionMetaData(WhiskActionService whiskActionService, WhiskAuth auth, ExecutableWhiskAction action) {
+ try {
+ whiskActionService.getWhiskAction(auth, Optional.ofNullable(action.getNamespacePath()), action.getWhiskPackage(), action.getName())
+ .ifPresent(executableWhiskAction -> {
+ cacheAction(executableWhiskAction);
+ initializeActionTab(executableWhiskAction);
+ NOTIFIER.notify(project, "Refreshed action metadata: " + executableWhiskAction.getFullyQualifiedName(), NotificationType.INFORMATION);
+ });
+ } catch (IOException e) {
+ LOG.error("Failed to fetch action: " + action.getFullyQualifiedName(), e);
+ }
+ }
+
+ /**
+ * Trigger Tab.
+ */
+ public void initializeTriggerTab(ExecutableWhiskTrigger trigger) {
+ try {
+ paramJTextArea.setText(JsonParserUtils.writeParameterToJson(trigger.getParameters()));
+ resultJTextArea.setText("Please fire trigger");
+ currentEntity = ENTITY_TRIGGER;
+ } catch (IOException e) {
+ LOG.error("Failed to parse json: " + trigger.getName(), e);
+ }
+ }
+
+ public void fireTrigger(WhiskTriggerService whiskTriggerService,
+ WhiskActivationService whiskActivationService,
+ WhiskAuth auth,
+ ExecutableWhiskTrigger trigger) {
+ ProgressManager.getInstance().run(new Task.Backgroundable(project, "Fire trigger") {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ try {
+ if (StringUtils.isEmpty(paramJTextArea.getText())) {
+ paramJTextArea.setText("{}");
+ }
+
+ Optional<String> params = ParameterUtils.validateParams(paramJTextArea.getText());
+ if (params.isPresent()) {
+ Optional<String> result = whiskTriggerService.fireWhiskTrigger(auth, trigger.getName(), params.get());
+ if (result.isPresent()) {
+ String activationId = (String) JsonParserUtils.parseMap(result.get()).get("activationId");
+ getActivation(whiskActivationService, auth, activationId, 10)
+ .ifPresent(response -> updateResult(response));
+ } else {
+ updateResult("Trigger was fired (no return value)");
+ }
+
+ } else {
+ NOTIFIER.notify(project, "The json format of the parameter is incorrect.", NotificationType.ERROR);
+ }
+ } catch (IOException e) {
+ LOG.error("Failed to fire trigger: " + trigger.getName(), e);
+ }
+ }
+ });
+ }
+
+ private Optional<String> getActivation(WhiskActivationService whiskActivationService, WhiskAuth auth, String activationId, int retryCount) {
+ if (retryCount == 0) {
+ LOG.error("Failed to get activation " + activationId);
+ return Optional.empty();
+ }
+ try {
+ return whiskActivationService.getWhiskActivation(auth, activationId).flatMap(this::getResponse);
+ } catch (IOException e) {
+ LOG.info("Get activation " + activationId + ", retry count: " + retryCount);
+ waitThread(500);
+ return getActivation(whiskActivationService, auth, activationId, retryCount - 1);
+ }
+ }
+
+ private Optional<String> getResponse(WhiskActivationWithLogs activation) {
+ try {
+ return Optional.ofNullable(JsonParserUtils.beautifyJson(JsonParserUtils.writeMapToJson(activation.getResponse())));
+ } catch (IOException e) {
+ LOG.error("Failed to parse response", e);
+ return Optional.empty();
+ }
+ }
+
+ private void waitThread(long ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException ex) {
+ // ignore
+ }
+ }
+
+ public void refreshTrigger(WhiskAuth auth, WhiskTriggerService whiskTriggerService, ExecutableWhiskTrigger old) {
+ try {
+ whiskTriggerService.getWhiskTrigger(auth, old.getName()).ifPresent(newTrigger -> {
+ cacheTrigger(newTrigger);
+ initializeTriggerTab(newTrigger);
+ });
+ } catch (IOException e) {
+ final String msg = "The action cannot be executed.";
+ LOG.error(msg, e);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployCmdDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployCmdDialog.java
new file mode 100644
index 0000000..4e6c909
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployCmdDialog.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.dialog;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.WskDeployCmd;
+import com.navercorp.openwhisk.intellij.wskdeploy.dialog.ui.WskDeployCmdDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class WskDeployCmdDialog extends DialogWrapperWithApply {
+
+
+ private WskDeployCmdDialogForm wskDeployCmdDialogForm;
+ private WskDeployCmd cmd;
+
+ public WskDeployCmdDialog(Project project, WskDeployCmd cmd) {
+ super(project, true); // use current window as parent
+ setTitle("Run WskDeploy Command");
+ setResizable(false);
+ this.cmd = cmd;
+ wskDeployCmdDialogForm = new WskDeployCmdDialogForm(project, cmd);
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ if (wskDeployCmdDialogForm != null) {
+ return this.wskDeployCmdDialogForm.getContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void doApplyAction() {
+ if (wskDeployCmdDialogForm != null) {
+ wskDeployCmdDialogForm.runWskDeploy();
+ }
+ super.doApplyAction();
+ }
+
+ @Override
+ protected void doOKAction() {
+ if (wskDeployCmdDialogForm != null && !myApplyAction.isApplied()) {
+ wskDeployCmdDialogForm.runWskDeploy();
+ }
+ super.doOKAction();
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployTempleteDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployTempleteDialog.java
new file mode 100644
index 0000000..3b7e05f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployTempleteDialog.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.dialog;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class WskDeployTempleteDialog extends DialogWrapper {
+
+ private Project project;
+ private String[] metaFiles;
+ private String[] srcFiles;
+
+ public WskDeployTempleteDialog(Project project, String[] metaFiles, String[] srcFiles) {
+ super(true); // use current window as parent
+ setTitle("Create WskDeploy Template");
+ setResizable(false);
+ this.project = project;
+ this.metaFiles = metaFiles;
+ this.srcFiles = srcFiles;
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ JPanel dialogPanel = new JPanel(new BorderLayout());
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("<html>");
+ builder.append("Are you sure you want to create wskdeploy project in your workspace?<br/>");
+ builder.append("The following file will be created:<br/>");
+ for (String path : metaFiles) {
+ builder.append("- " + path + "<br/>");
+ }
+
+ for (String path : srcFiles) {
+ builder.append("- src/" + path + "<br/>");
+ }
+ builder.append("</html>");
+ JLabel label = new JLabel(builder.toString());
+ label.setPreferredSize(new Dimension(500, 100));
+ dialogPanel.add(label, BorderLayout.CENTER);
+
+ return dialogPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.form
new file mode 100644
index 0000000..575c4df
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.form
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.wskdeploy.dialog.ui.WskDeployCmdDialogForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="c2540" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="399fc" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="56c6e" class="javax.swing.JLabel" binding="selectMsgJabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Label"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <scrollpane id="f14e7">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="de57e" class="javax.swing.JList" binding="namespaceJList">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.java
new file mode 100644
index 0000000..a9c3a05
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.dialog.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.error.NotExistFileException;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuthWithName;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.WskDeployCmd;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.WskDeployCmdResponse;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.navercorp.openwhisk.intellij.common.utils.CommandUtils.runCommand;
+
+public class WskDeployCmdDialogForm {
+ private static final Logger LOG = Logger.getInstance(WskDeployCmdDialogForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private JPanel mainJPanel;
+ private JList namespaceJList;
+ private JLabel selectMsgJabel;
+
+ private Project project;
+ private WskDeployCmd cmd;
+
+ public WskDeployCmdDialogForm(Project project, WskDeployCmd cmd) {
+ this.project = project;
+ this.cmd = cmd;
+
+ selectMsgJabel.setText("Please select the namespace you want to " + cmd.getCmdName() + ".");
+
+ try {
+ WhiskService whiskService = ServiceManager.getService(project, WhiskService.class);
+ List<WhiskEndpoint> endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+
+ namespaceJList.setListData(endpoints.stream().flatMap(ep ->
+ ep.getNamespaces().stream().map(ns ->
+ new WhiskAuthWithName(new WhiskAuth(ns.getAuth(), ep.getApihost()), ns.getPath()))).toArray());
+
+ } catch (IOException e) {
+ LOG.error("Endpoint parsing failed", e);
+ }
+ }
+
+ public void runWskDeploy() {
+ try {
+ WhiskAuthWithName auth = (WhiskAuthWithName) namespaceJList.getSelectedValue();
+
+ LOG.info(cmd.toCmdString());
+ WskDeployCmdResponse response = runCommand(cmd.toCmd(auth.getAuth())); // TODO running background thread
+ LOG.info(response.getSuccessOutput());
+ String errMsg = response.getErrorOutput();
+ if (!errMsg.isEmpty()) {
+ LOG.error(errMsg);
+ }
+
+ if (response.getExistCode() == 0) {
+ NOTIFIER.notify(project, response.getSuccessOutput(), NotificationType.INFORMATION);
+ EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+ } else {
+ NOTIFIER.notify(project, response.getErrorOutput(), NotificationType.ERROR);
+ }
+
+ } catch (NotExistFileException | IOException | InterruptedException ex) {
+ LOG.error(ex);
+ NOTIFIER.notify(project, "Failed to running command(" + cmd.getCmdName() + ") for " + ex.getMessage(), NotificationType.ERROR);
+ }
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/WskDeployWindowFactory.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/WskDeployWindowFactory.java
new file mode 100644
index 0000000..8a461d2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/WskDeployWindowFactory.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.toolwindow;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowFactory;
+import com.intellij.ui.content.Content;
+import com.intellij.ui.content.ContentFactory;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.ui.WskDeployWindowForm;
+import org.jetbrains.annotations.NotNull;
+
+public class WskDeployWindowFactory implements ToolWindowFactory {
+ private static final Logger LOG = Logger.getInstance(WskDeployWindowFactory.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ @Override
+ public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
+ WskDeployWindowForm wskDeployWindowForm = new WskDeployWindowForm(project, toolWindow);
+ ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
+ Content content = contentFactory.createContent(wskDeployWindowForm.getContent(), null, false);
+ toolWindow.getContentManager().addContent(content);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/ChooseWskDeployBinAction.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/ChooseWskDeployBinAction.java
new file mode 100644
index 0000000..efeccf2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/ChooseWskDeployBinAction.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.ValidationUtils;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener.ChooseWskDeployBinaryListener;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Optional;
+
+import static com.navercorp.openwhisk.intellij.common.Icons.OPEN_DISK_HOVER;
+
+public class ChooseWskDeployBinAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(ChooseWskDeployBinAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ ChooseWskDeployBinAction() {
+ super(OPEN_DISK_HOVER);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ VirtualFile file = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFileDescriptor(), e.getProject(), null);
+ if (file == null) {
+ return;
+ }
+
+ Optional<VirtualFile> validWskDeploy = ValidationUtils.validateWskDeploy(Optional.of(file));
+ if (validWskDeploy.isPresent()) {
+ NOTIFIER.notify(e.getProject(), file.getName() + " file has been registered with wskdeploy.", NotificationType.INFORMATION);
+ EventUtils.publish(e.getProject(), ChooseWskDeployBinaryListener.TOPIC, l -> l.chooseWskDeployBinary(validWskDeploy.get()));
+ } else {
+ NOTIFIER.notify(e.getProject(), file.getName() + " file cannot be used with wskdeploy.", NotificationType.ERROR);
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/CreateManifestTemplateAction.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/CreateManifestTemplateAction.java
new file mode 100644
index 0000000..b2381d9
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/CreateManifestTemplateAction.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.action;
+
+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.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.search.FilenameIndex;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.ResourceUtil;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.wskdeploy.dialog.WskDeployTempleteDialog;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener.RefreshWskDeployManifestListener;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+public class CreateManifestTemplateAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(CreateManifestTemplateAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private static final String[] META_FILES = new String[]{"manifest.yaml", "HOW-TO-DEPLOY.md"};
+ private static final String[] SOURCE_FILES = new String[]{"index.js", "index.test.js", "package.json"};
+
+ CreateManifestTemplateAction() {
+ super(AllIcons.Actions.Menu_paste);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ final Project project = e.getProject();
+ final String basePath = project.getBasePath();
+
+ if ((new WskDeployTempleteDialog(project, META_FILES, SOURCE_FILES)).showAndGet()) {
+ ApplicationManager.getApplication().runWriteAction(() -> {
+ for (String filePath : META_FILES) {
+ copyFile(project, "/template", filePath, basePath);
+ }
+
+ for (String filePath : SOURCE_FILES) {
+ copyFile(project, "/template/src", filePath, basePath + "/src");
+ }
+
+ // refresh manifest file tree
+ EventUtils.publish(project, RefreshWskDeployManifestListener.TOPIC, RefreshWskDeployManifestListener::refreshWskDeployManifest);
+ });
+ }
+ }
+
+ private void copyFile(Project project, String resourcePath, String fileName, String toDir) {
+ try {
+ boolean exist = Arrays.stream(FilenameIndex.getFilesByName(project, fileName, GlobalSearchScope.allScope(project)))
+ .anyMatch(file -> file.getVirtualFile().getPath().startsWith(toDir));
+
+ if (!exist) {
+ final VirtualFile file = VfsUtil.findFileByURL(ResourceUtil.getResource(getClass(), resourcePath, fileName));
+ final VirtualFile parent = VfsUtil.createDirectories(toDir);
+ VfsUtilCore.copyFile(this, file, parent);
+ NOTIFIER.notify(project, fileName + " file is created", NotificationType.INFORMATION);
+ } else {
+ NOTIFIER.notify(project, fileName + " file already exist", NotificationType.WARNING);
+ }
+ } catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/RefreshWskDeployAction.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/RefreshWskDeployAction.java
new file mode 100644
index 0000000..ec6586d
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/RefreshWskDeployAction.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener.RefreshWskDeployManifestListener;
+
+import static com.intellij.icons.AllIcons.Actions.Refresh;
+
+public class RefreshWskDeployAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance(RefreshWskDeployAction.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ public RefreshWskDeployAction() {
+ super(Refresh);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent event) {
+ EventUtils.publish(event.getProject(), RefreshWskDeployManifestListener.TOPIC, RefreshWskDeployManifestListener::refreshWskDeployManifest);
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/ChooseWskDeployBinaryListener.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/ChooseWskDeployBinaryListener.java
new file mode 100644
index 0000000..73fbdb7
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/ChooseWskDeployBinaryListener.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface ChooseWskDeployBinaryListener extends EventListener {
+ Topic<ChooseWskDeployBinaryListener> TOPIC = Topic.create("Choose wskdeploy binary", ChooseWskDeployBinaryListener.class);
+
+ void chooseWskDeployBinary(VirtualFile wskDeployBin);
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/RefreshWskDeployManifestListener.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/RefreshWskDeployManifestListener.java
new file mode 100644
index 0000000..64a00d5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/RefreshWskDeployManifestListener.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface RefreshWskDeployManifestListener extends EventListener {
+ Topic<RefreshWskDeployManifestListener> TOPIC = Topic.create("Refresh wskdeploy manifest", RefreshWskDeployManifestListener.class);
+
+ void refreshWskDeployManifest();
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeCellRenderer.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeCellRenderer.java
new file mode 100644
index 0000000..6398693
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeCellRenderer.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.tree;
+
+import com.intellij.ui.ColoredTreeCellRenderer;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.*;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+
+import static com.intellij.icons.AllIcons.Actions.Execute;
+import static com.intellij.icons.AllIcons.Actions.Rollback;
+import static com.navercorp.openwhisk.intellij.common.Icons.RUN_ANYTHING;
+import static com.navercorp.openwhisk.intellij.common.Icons.YAML;
+
+public class WskDeployTreeCellRenderer extends ColoredTreeCellRenderer {
+ @Override
+ public void customizeCellRenderer(@NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+ DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
+ Object userObject = treeNode.getUserObject();
+ if (userObject instanceof WskDeployManifest) {
+ WskDeployManifest manifest = (WskDeployManifest) userObject;
+ setIcon(YAML);
+ append(manifest.getPath());
+ } else if (userObject instanceof WskDeployCmdDeploy) {
+ WskDeployCmdDeploy wskDeployCmdDeploy = (WskDeployCmdDeploy) userObject;
+ setIcon(Execute);
+ append(wskDeployCmdDeploy.getCmdName());
+ } else if (userObject instanceof WskDeployCmdUndeploy) {
+ WskDeployCmdUndeploy wskDeployCmdUndeploy = (WskDeployCmdUndeploy) userObject;
+ setIcon(Rollback);
+ append(wskDeployCmdUndeploy.getCmdName());
+ } else if (userObject instanceof WskDeployBinary) {
+ WskDeployBinary wskDeployBinary = (WskDeployBinary) userObject;
+ setIcon(RUN_ANYTHING);
+ append("wskdeploy (" + wskDeployBinary.getFullPath() + ")");
+ } else if (userObject instanceof NullWskDeployBinary) {
+ setIcon(RUN_ANYTHING);
+ append("There is no registered wskdeploy binary. Please find the file and register.");
+ } else {
+ append(userObject.toString());
+ }
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeModel.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeModel.java
new file mode 100644
index 0000000..aa5889e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeModel.java
@@ -0,0 +1,106 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.tree;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.*;
+
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+import java.util.List;
+import java.util.Optional;
+
+public class WskDeployTreeModel implements TreeModel {
+ private static final Logger LOG = Logger.getInstance(WskDeployTreeModel.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private DefaultTreeModel innerModel;
+ private DefaultMutableTreeNode root;
+
+ public WskDeployTreeModel(WskDeployFile wskDeployFile, List<WskDeployManifest> manifests) {
+ setTree(wskDeployFile, manifests);
+ }
+
+ public void setTree(WskDeployFile wskDeployFile, List<WskDeployManifest> manifests) {
+ this.root = new DefaultMutableTreeNode(wskDeployFile);
+
+ for (WskDeployManifest manifest : manifests) {
+ DefaultMutableTreeNode manifestNode = new DefaultMutableTreeNode(manifest);
+ root.add(manifestNode);
+
+ Optional<WskDeployBinary> wskDeployBinary = toWskDeployBinary(wskDeployFile);
+ manifestNode.add(new DefaultMutableTreeNode(new WskDeployCmdDeploy(wskDeployBinary, manifest)));
+ manifestNode.add(new DefaultMutableTreeNode(new WskDeployCmdUndeploy(wskDeployBinary, manifest)));
+ }
+
+ this.innerModel = new DefaultTreeModel(this.root);
+ }
+
+ private Optional<WskDeployBinary> toWskDeployBinary(WskDeployFile wskDeployFile) {
+ if (wskDeployFile instanceof WskDeployBinary) {
+ return Optional.of((WskDeployBinary) wskDeployFile);
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Object getRoot() {
+ return this.innerModel.getRoot();
+ }
+
+ @Override
+ public Object getChild(Object parent, int index) {
+ return this.innerModel.getChild(parent, index);
+ }
+
+ @Override
+ public int getChildCount(Object parent) {
+ return this.innerModel.getChildCount(parent);
+ }
+
+ @Override
+ public boolean isLeaf(Object node) {
+ return this.innerModel.isLeaf(node);
+ }
+
+ @Override
+ public void valueForPathChanged(TreePath path, Object newValue) {
+ this.innerModel.valueForPathChanged(path, newValue);
+
+ }
+
+ @Override
+ public int getIndexOfChild(Object parent, Object child) {
+ return this.innerModel.getIndexOfChild(parent, child);
+ }
+
+ @Override
+ public void addTreeModelListener(TreeModelListener l) {
+ this.innerModel.addTreeModelListener(l);
+
+ }
+
+ @Override
+ public void removeTreeModelListener(TreeModelListener l) {
+ this.innerModel.removeTreeModelListener(l);
+
+ }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.form b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.form
new file mode 100644
index 0000000..156f6e1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.form
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.ui.WskDeployWindowForm">
+ <grid id="27dc6" binding="mainJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="1986e" binding="actionsJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints border-constraint="North"/>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <scrollpane id="dd1" binding="contentsJPanel">
+ <constraints border-constraint="Center"/>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="e6669" class="javax.swing.JTree" binding="wskdeployJTree">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ <grid id="4b4b2" binding="downloadGuideJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints border-constraint="South"/>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="fd9f3" class="javax.swing.JLabel" binding="urlJLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Label"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.java
new file mode 100644
index 0000000..1b0996d
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.java
@@ -0,0 +1,213 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionToolbar;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.psi.search.FilenameIndex;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.service.WskDeployService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.ValidationUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.*;
+import com.navercorp.openwhisk.intellij.wskdeploy.dialog.WskDeployCmdDialog;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener.ChooseWskDeployBinaryListener;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener.RefreshWskDeployManifestListener;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.tree.WskDeployTreeCellRenderer;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.tree.WskDeployTreeModel;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class WskDeployWindowForm {
+ private static final Logger LOG = Logger.getInstance(WskDeployWindowForm.class);
+ private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+ private static final String WSKDEPLOY_URL = "https://github.com/apache/openwhisk-wskdeploy/releases";
+
+ private JPanel mainJPanel;
+ private JPanel actionsJPanel;
+ private JScrollPane contentsJPanel;
+ private JTree wskdeployJTree;
+ private JPanel downloadGuideJPanel;
+ private JLabel urlJLabel;
+
+ private WskDeployService wskDeployService;
+
+ public WskDeployWindowForm(Project project, ToolWindow toolWindow) {
+ this.wskDeployService = ServiceManager.getService(project, WskDeployService.class);
+
+ // Toolbar
+ String actionGroupName = "WskDeployWindow.Actions.Controls";
+ ActionManager actionManager = ActionManager.getInstance();
+ ActionGroup actionGroup = (ActionGroup) actionManager.getAction(actionGroupName);
+ ActionToolbar actionToolbar = actionManager.createActionToolbar("", actionGroup, true);
+ actionToolbar.setTargetComponent(actionsJPanel);
+ actionsJPanel.add(actionToolbar.getComponent());
+
+ // Download url for wskdeploy
+ urlJLabel.setText("<html> WskDeploy download : <a href=\\\"\\\">" + WSKDEPLOY_URL + "</a></html>");
+ urlJLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
+ urlJLabel.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ try {
+ Desktop.getDesktop().browse(new URI(WSKDEPLOY_URL));
+ } catch (IOException | URISyntaxException ex) {
+ String msg = "Cannot open " + WSKDEPLOY_URL;
+ LOG.error(msg, ex);
+ NOTIFIER.notify(project, msg, NotificationType.ERROR);
+ }
+ }
+ });
+
+ setWskDeployTree(project, loadWskDeployManifest(project));
+ }
+
+ private void setWskDeployTree(Project project, List<WskDeployManifest> manifests) {
+ boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows"); // TODO make a global value
+ WskDeployFile wskDeployFile = loadRegisteredWskDeploy() // 1. Registered wskdeploy
+ .orElse(loadWskDeployFileFromLocal(isWindows)); // 2. Local wskdeploy
+
+ wskdeployJTree.setModel(new WskDeployTreeModel(wskDeployFile, manifests));
+ wskdeployJTree.setCellRenderer(new WskDeployTreeCellRenderer());
+ expandAllNode(wskdeployJTree);
+ wskdeployJTree.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() > 1) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) wskdeployJTree.getLastSelectedPathComponent();
+ Object userObject = node.getUserObject();
+ if (userObject instanceof WskDeployCmdDeploy) {
+ WskDeployCmdDeploy cmd = (WskDeployCmdDeploy) userObject;
+ if (new WskDeployCmdDialog(project, cmd).showAndGet()) {
+ LOG.info("WskDeployCmdDialog(deploy) closed");
+ }
+ } else if (userObject instanceof WskDeployCmdUndeploy) {
+ WskDeployCmdUndeploy cmd = (WskDeployCmdUndeploy) userObject;
+ if (new WskDeployCmdDialog(project, cmd).showAndGet()) {
+ LOG.info("WskDeployCmdDialog(undeploy) closed");
+ }
+ }
+ }
+ }
+ });
+
+ EventUtils.subscribe(project, project, RefreshWskDeployManifestListener.TOPIC, () -> {
+ WskDeployFile reloadedWskDeployFile = loadRegisteredWskDeploy() // 1. Registered wskdeploy
+ .orElse(loadWskDeployFileFromLocal(isWindows)); // 2. Local wskdeploy
+
+ wskdeployJTree.setModel(new WskDeployTreeModel(reloadedWskDeployFile, loadWskDeployManifest(project)));
+ expandAllNode(wskdeployJTree);
+ });
+
+ EventUtils.subscribe(project, project, ChooseWskDeployBinaryListener.TOPIC, (chosenWskDeployFile) -> {
+ WskDeployBinary wskDeployBinary = new WskDeployBinary(chosenWskDeployFile.getPath(), chosenWskDeployFile.getName());
+ wskDeployService.setWskdeployName(wskDeployBinary.getName());
+ wskDeployService.setWskdeployPath(wskDeployBinary.getFullPath());
+ wskDeployService.loadState(wskDeployService);
+
+ wskdeployJTree.setModel(new WskDeployTreeModel(wskDeployBinary, loadWskDeployManifest(project)));
+ expandAllNode(wskdeployJTree);
+ });
+ }
+
+
+ private Optional<WskDeployFile> loadRegisteredWskDeploy() {
+ if (wskDeployService.getWskdeployPath() != null && wskDeployService.getWskdeployName() != null) {
+ VirtualFile file = LocalFileSystem.getInstance().findFileByPath(wskDeployService.getWskdeployPath());
+ return ValidationUtils.validateWskDeploy(Optional.ofNullable(file))
+ .map(validWskDeploy -> new WskDeployBinary(validWskDeploy.getPath(), validWskDeploy.getName()));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ private WskDeployFile loadWskDeployFileFromLocal(boolean isWindow) {
+ String[] dirs;
+ if (isWindow) {
+ dirs = new String[]{};
+ } else {
+ dirs = new String[]{"/usr/local/bin", "/usr/bin"};
+ }
+
+ for (String dir : dirs) {
+ File[] files = findFiles(dir, "wskdeploy");
+ if (files != null && files.length > 0) {
+ VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(files[0]);
+ Optional<WskDeployBinary> wskDeployBinary = ValidationUtils.validateWskDeploy(Optional.ofNullable(file))
+ .map(validWskDeploy -> new WskDeployBinary(validWskDeploy.getPath(), validWskDeploy.getName()));
+ if (wskDeployBinary.isPresent()) {
+ return wskDeployBinary.get();
+ }
+ }
+ }
+ return new NullWskDeployBinary();
+ }
+
+ private File[] findFiles(String parentDir, String fileName) {
+ File dir = new File(parentDir);
+ return dir.listFiles((dir1, name) -> name.startsWith(fileName));
+ }
+
+ private void expandAllNode(JTree tree) {
+ for (int i = 0; i < tree.getRowCount(); i++) {
+ tree.expandRow(i);
+ }
+ }
+
+ private List<WskDeployManifest> loadWskDeployManifest(Project project) {
+ List<WskDeployManifest> manifests = new ArrayList<>();
+ Collection<VirtualFile> yamlFiles = FilenameIndex.getAllFilesByExt(project, "yaml", GlobalSearchScope.projectScope(project));
+ Collection<VirtualFile> ymlFiles = FilenameIndex.getAllFilesByExt(project, "yml", GlobalSearchScope.projectScope(project));
+ List<VirtualFile> files = Stream.of(yamlFiles, ymlFiles).flatMap(Collection::stream).collect(Collectors.toList());
+ for (VirtualFile f : files) {
+ String fullPath = f.getPath();
+ String path = fullPath.replaceAll(project.getBasePath() + "/", "");
+ String name = f.getName();
+ manifests.add(new WskDeployManifest(path, fullPath, name));
+ }
+ return manifests;
+ }
+
+ public JPanel getContent() {
+ return mainJPanel;
+ }
+
+}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
new file mode 100644
index 0000000..a7e22eb
--- /dev/null
+++ b/src/main/resources/META-INF/plugin.xml
@@ -0,0 +1,121 @@
+<idea-plugin>
+ <id>com.navercorp.openwhisk.intellij</id>
+ <name>DevTools for Apache Openwhisk</name>
+ <vendor email="dl_lambda_op@navercorp.com" url="https://github.com/naver">NAVER</vendor>
+ <version>1.1.4</version>
+ <idea-version since-build="181.*" until-build="203.*"/>
+
+ <description><![CDATA[
+<p>
+ <strong>OpenWhisk-intellij-support</strong> is an open source Intellij Plugin for <a href="https://github.com/apache/openwhisk">Apache OpenWhisk</a>. It assists users to develop/deploy/manage OpenWhisk functions in Intellij.
+</p>
+
+<h2>Feature</h2>
+
+<h3>OpenWhisk Explorer</h3>
+<ul>
+ <li>Explore all entities in your endpoints/namespaces.
+ <ul>
+ <li>The <code>.wskprops</code> file is automatically registered.</li>
+ <li>You can add the API host manually.</li>
+ <li>You can add namespace manually by API auth key.</li>
+ </ul>
+ </li>
+ <li>Show the action code with syntax highlighting.</li>
+ <li>[Soon] Edit the action code on the remote server.</li>
+ <li>Invoke the action remotely and get the activation result.</li>
+ <li>Show a list of actions related to the sequence action.</li>
+ <li>Show information about the trigger and related rules.</li>
+ <li>Show activations related to the action (Same as <code>wsk activation list ${action}</code>).</li>
+ <li>Show detailed information of the activation (Same <code>as wsk activation get ${activation_id}</code>).</li>
+ <li>Update parameters of the action, package, and trigger.</li>
+</ul>
+
+<h3>Manifest View</h3>
+<ul>
+ <li>List up manifest YAML files in the workspace.</li>
+ <li>Deploy/Undeploy OpenWhisk packages with manifest (via wskdeploy).
+ <ul>
+ <li>Deploy with the deployment file.</li>
+ <li>Deploy with multiple credentials.</li>
+ </ul>
+ </li>
+</ul>
+ ]]></description>
+
+ <change-notes><![CDATA[
+<ul>
+ <li>Support IDEA 203.* version: https://github.com/naver/openwhisk-intellij-plugin/pull/31</li>
+</ul>
+ ]]></change-notes>
+
+ <depends>com.intellij.modules.lang</depends>
+
+ <extensions defaultExtensionNs="com.intellij">
+ <toolWindow id="Whisk Explorer" icon="/objectBrowser/flattenPackages.svg" anchor="right"
+ factoryClass="com.navercorp.openwhisk.intellij.explorer.toolwindow.WhiskExplorerWindowFactory"/>
+ <toolWindow id="Whisk Run" icon="/icons/ssh.svg" anchor="bottom"
+ factoryClass="com.navercorp.openwhisk.intellij.run.toolwindow.WhiskRunWindowFactory"/>
+ <toolWindow id="WskDeploy" icon="/actions/upload.svg" anchor="left" secondary="true"
+ factoryClass="com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.WskDeployWindowFactory"/>
+ <projectService serviceImplementation="com.navercorp.openwhisk.intellij.common.service.WhiskService"/>
+ <projectService serviceImplementation="com.navercorp.openwhisk.intellij.common.service.WskDeployService"/>
+ <virtualFileSystem
+ implementationClass="com.navercorp.openwhisk.intellij.explorer.editor.ActivationViewVirtualFileSystem"
+ key="activation-view"/>
+ <fileEditorProvider
+ implementation="com.navercorp.openwhisk.intellij.explorer.editor.ActivationViewEditorProvider"/>
+ </extensions>
+
+ <actions>
+ <group id="WhiskExplorer.Actions.Controls" text="Explorer whisk tree">
+ <action id="WhiskExplorer.Actions.Controls.AddEndpoint"
+ class="com.navercorp.openwhisk.intellij.explorer.toolwindow.action.AddEndpointAction"
+ text="Add Endpoint"
+ description="Add Endpoint">
+ </action>
+ <separator/>
+ <action id="WhiskExplorer.Actions.Controls.Refresh"
+ class="com.navercorp.openwhisk.intellij.explorer.toolwindow.action.RefreshTreeAction"
+ text="Refresh"
+ description="Reload whisk data from remote">
+ </action>
+ </group>
+ <group id="WhiskRunWindow.Actions.Controls">
+ <action id="WhiskRunWindow.Actions.Controls.RunAction"
+ class="com.navercorp.openwhisk.intellij.run.toolwindow.action.RunActionAction"
+ text="Run"
+ description="Run action">
+ </action>
+ <action id="WhiskRunWindow.Actions.Controls.Refresh"
+ class="com.navercorp.openwhisk.intellij.run.toolwindow.action.RefreshMetaDataAction"
+ text="Refresh"
+ description="Refresh">
+ </action>
+ <action id="WhiskRunWindow.Actions.Controls.Edit"
+ class="com.navercorp.openwhisk.intellij.run.toolwindow.action.OpenActionManagerDialogAction"
+ text="Edit"
+ description="Edit action metadata">
+ </action>
+ </group>
+ <group id="WskDeployWindow.Actions.Controls" text="Manage wskdeploy">
+ <action id="WskDeployWindow.Actions.Controls.Refresh"
+ class="com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.action.RefreshWskDeployAction"
+ text="WskDeploy"
+ description="Refresh wskdeploy">
+ </action>
+ <action id="WskDeployWindow.Actions.Controls.CreateTemplate"
+ class="com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.action.CreateManifestTemplateAction"
+ text="Create Manifest Template"
+ description="Create Manifest Template">
+ </action>
+
+ <action id="WskDeployWindow.Actions.Controls.ChooseWskDeployBin"
+ class="com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.action.ChooseWskDeployBinAction"
+ text="Find WskDeploy binary"
+ description="Find WskDeploy binary">
+ </action>
+ </group>
+ </actions>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/src/main/resources/icons/arrowDown.svg b/src/main/resources/icons/arrowDown.svg
new file mode 100644
index 0000000..a83b43f
--- /dev/null
+++ b/src/main/resources/icons/arrowDown.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#6E6E6E" fill-rule="evenodd" d="M8 11l4.5-5h-9z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/arrowUp.svg b/src/main/resources/icons/arrowUp.svg
new file mode 100644
index 0000000..3d6b0cc
--- /dev/null
+++ b/src/main/resources/icons/arrowUp.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#6E6E6E" fill-rule="evenodd" d="M8 5l4.5 5h-9z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/event.svg b/src/main/resources/icons/event.svg
new file mode 100644
index 0000000..fe9ea4b
--- /dev/null
+++ b/src/main/resources/icons/event.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#F4AF3D" fill-rule="evenodd" d="M7.998 9.62l-6.286.003L6.765.996h4.93L7.99 5.927h7.404l-9.861 9.856z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/eventGroup.svg b/src/main/resources/icons/eventGroup.svg
new file mode 100644
index 0000000..2bd7dc3
--- /dev/null
+++ b/src/main/resources/icons/eventGroup.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#9AA7B0" fill-opacity=".8" d="M15 6h-5l-4.1 7H1V2.051C1 2.023 1.023 2 1.051 2H4.98a2.8 2.8 0 0 1 1.717.711L7.985 4H15v2z"/><path fill="#F4AF3D" d="M11.409 12.21l-3.8.002L10.664 7h2.979l-2.24 2.978h4.475l-5.96 5.956z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/docker/DockerCompose.svg b/src/main/resources/icons/language/docker/DockerCompose.svg
new file mode 100644
index 0000000..5118aef
--- /dev/null
+++ b/src/main/resources/icons/language/docker/DockerCompose.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><g transform="translate(.4 5.6)"><path fill="#34A2F2" d="M14.85 2.439c-.039-.031-.4-.304-1.162-.304-.202 0-.403.018-.601.052-.148-1.012-.984-1.505-1.021-1.527L11.86.542l-.134.195a2.744 2.744 0 0 0-.364.85c-.137.578-.054 1.12.24 1.583-.355.198-.923.246-1.038.25H.6c-.05 1.381.087 2.44.412 3.176C1.5 7.7 2.042 8.409 2.945 8.84c.903.43 1.459.576 2.753.576a9.368 9.368 0 0 0 1.743-.158 7.286 7.286 0 0 0 2.274-.826 6.253 6.253 0 0 0 1.552-1.27c.745-.844 1.19-2.384 1.52-3.218h.13c.817 0 1.318-.327 1.595-.6a1.74 1.74 0 0 0 .42-.623l.059-.171-.141-.111z"/><circle cx="4.75" cy="5.65" r="1.25" fill="#FFF"/><circle cx="4.75" cy="5.65" r="1" fill="#34A2F2" fill-rule="nonzero"/></g><path fill="#34A2F2" fill-rule="nonzero" d="M3 5h3v3H3z"/><path fill="#34A2F2" fill-rule="nonzero" d="M7 5h3v3H7z"/><path fill="#34A2F2" fill-rule="nonzero" d="M5 1h3v3H5z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/go/go.svg b/src/main/resources/icons/language/go/go.svg
new file mode 100644
index 0000000..41738b2
--- /dev/null
+++ b/src/main/resources/icons/language/go/go.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:none;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+ .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+ .st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+ .st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+ .st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+ .st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+ .st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+ .st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+ .st9{opacity:0.6;}
+</style>
+<rect id="frame_2_" x="0" class="st0" width="16" height="16"/>
+<path class="st4" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+ <path d="M1.8,12.6v-0.2c0-1.9,1.1-2.5,2.4-2.5c0.6,0,1.1,0.1,1.4,0.3v0.8c-0.3-0.1-0.7-0.3-1.3-0.3c-1.1,0-1.4,0.7-1.4,1.6v0.2
+ c0,1,0.2,1.7,1.3,1.7c0.3,0,0.5-0.1,0.7-0.1v-1H4v-0.7h1.8v2.3c-0.4,0.1-1,0.3-1.6,0.3C2.7,15,1.8,14.4,1.8,12.6z"/>
+ <path d="M6.4,12.6v-0.2c0-1.6,1-2.4,2.3-2.4c1.3,0,2.2,0.8,2.2,2.4v0.2c0,1.6-1,2.4-2.3,2.4S6.4,14.2,6.4,12.6z M9.9,12.7v-0.2
+ c0-1.1-0.3-1.7-1.2-1.7c-0.8,0-1.2,0.6-1.2,1.6v0.2c0,1.1,0.4,1.7,1.2,1.7C9.5,14.3,9.9,13.7,9.9,12.7z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/language/go/go_template.svg b/src/main/resources/icons/language/go/go_template.svg
new file mode 100644
index 0000000..933d7c4
--- /dev/null
+++ b/src/main/resources/icons/language/go/go_template.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#77BBE2" d="M12.16 10H10.8L7 12.6v2.157c-1.963-.363-3.286-1.783-3.286-3.466 0 0 .147-1.142.21-2.379h-.01c.022-.29.038-.612.038-.989 0-1.539-.238-3.368-.238-3.368C3.714 2.6 5.5 1 8 1c2.5 0 4.286 1.6 4.286 3.556 0 0-.238 2.085-.238 3.367 0 .353.02.684.045.989h-.012c.02.37.05.746.079 1.088z"/><path fill="#77BBE2" d="M12.16 10H10.8L7 12.6v2.157c-1.963-.363-3.286-1.783-3.286-3.466 0 0 .147-1.142.21-2.379h-.01c.022-.29.038-.612.038-.989 0-1.539-.238-3.368-.238-3.368C3.714 2.6 5.5 1 8 1c2.5 0 4.286 1.6 4.286 3.556 0 0-.238 2.085-.238 3.367 0 .353.02.684.045.989h-.012c.02.37.05.746.079 1.088z"/><path fill="#77BBE2" d="M13 3.18c0 .495-.373.897-.833.897-.46 0-.834-.402-.834-.898 0-.495.374-.897.834-.897.46 0 .833.402.833.897m-10 0c0 .496.373.898.833.898.46 0 .834-.402.834-.898 0-.495-.374-.897-.834-.897-.46 0-.833.402-.833.897"/><path fill="#FFF" d="M7.743 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.538-.678-1.538-1.514s.688-1.514 1.538-1.514c.85 0 1.538.678 1.538 1.514"/><path fill="#FFF" d="M7.743 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.538-.678-1.538-1.514s.688-1.514 1.538-1.514c.85 0 1.538.678 1.538 1.514"/><path fill="#000" d="M6.993 5.073c0 .409-.307.74-.687.74-.38 0-.687-.331-.687-.74 0-.408.308-.74.687-.74.38 0 .687.332.687.74"/><path fill="#FFF" d="M11.137 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.539-.678-1.539-1.514S8.75 3.564 9.6 3.564s1.538.678 1.538 1.514"/><path fill="#000" d="M10.387 5.073c0 .409-.308.74-.687.74-.38 0-.687-.331-.687-.74 0-.408.307-.74.687-.74.38 0 .687.332.687.74"/><path fill="#FFF" d="M7.523 7.923h.953v-.769h-.953z"/><path fill="#B7937F" d="M8.953 6.897c0 .284-.427.513-.953.513s-.952-.23-.952-.513c0-.283.426-.512.952-.512s.953.23.953.513"/><path fill="#000" d="M8.476 6.633c0 .288-.216.264-.484.264-.267 0-.484.024-.484-.264 0-.289.217-.522.484-.522.268 0 .484.233.484.522"/><path fill="#B7937F" fill-rule="nonzero" d="M13 8.271c0 .354-.267.641-.595.641-.329 0-.596-.287-.596-.64 0-.355.267-.642.596-.642.328 0 .595.287.595.641m-8.81 0c0 .354-.266.641-.595.641-.328 0-.595-.287-.595-.64 0-.355.267-.642.595-.642.33 0 .596.287.596.641m1 5.934c0 .354-.267.641-.596.641-.328 0-.595-.287-.595-.641 0-.354.267-.64.595-.64.329 0 .596.286.596.64"/><path fill="#231F20" fill-opacity=".7" d="M8 14v-1l3-2v1l-2.2 1.5L11 15v1z"/><path fill="#231F20" fill-opacity=".7" d="M16 14v-1l-3-2v1l2.2 1.5L13 15v1z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/go/go_template_dark.svg b/src/main/resources/icons/language/go/go_template_dark.svg
new file mode 100644
index 0000000..ffe6285
--- /dev/null
+++ b/src/main/resources/icons/language/go/go_template_dark.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#77BBE2" d="M12.16 10H10.8L7 12.6v2.157c-1.963-.363-3.286-1.783-3.286-3.466 0 0 .147-1.142.21-2.379h-.01c.022-.29.038-.612.038-.989 0-1.539-.238-3.367-.238-3.367C3.714 2.6 5.5 1 8 1c2.5 0 4.286 1.6 4.286 3.556 0 0-.238 2.085-.238 3.367 0 .352.02.684.045.989h-.012c.02.37.05.746.079 1.088z"/><path fill="#77BBE2" d="M12.16 10H10.8L7 12.6v2.157c-1.963-.363-3.286-1.783-3.286-3.466 0 0 .147-1.142.21-2.379h-.01c.022-.29.038-.612.038-.989 0-1.539-.238-3.367-.238-3.367C3.714 2.6 5.5 1 8 1c2.5 0 4.286 1.6 4.286 3.556 0 0-.238 2.085-.238 3.367 0 .352.02.684.045.989h-.012c.02.37.05.746.079 1.088z"/><path fill="#77BBE2" d="M13 3.18c0 .495-.373.897-.833.897-.46 0-.834-.402-.834-.898 0-.495.374-.897.834-.897.46 0 .833.402.833.897m-10 0c0 .496.373.898.833.898.46 0 .834-.402.834-.898 0-.495-.374-.897-.834-.897-.46 0-.833.402-.833.897"/><path fill="#FFF" d="M7.743 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.538-.678-1.538-1.514s.688-1.514 1.538-1.514c.85 0 1.538.678 1.538 1.514"/><path fill="#FFF" d="M7.743 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.538-.678-1.538-1.514s.688-1.514 1.538-1.514c.85 0 1.538.678 1.538 1.514"/><path fill="#000" d="M6.993 5.073c0 .409-.307.74-.687.74-.38 0-.687-.331-.687-.74 0-.408.308-.74.687-.74.38 0 .687.332.687.74"/><path fill="#FFF" d="M11.137 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.539-.678-1.539-1.514S8.75 3.564 9.6 3.564s1.538.678 1.538 1.514"/><path fill="#000" d="M10.387 5.073c0 .409-.308.74-.687.74-.38 0-.687-.331-.687-.74 0-.408.307-.74.687-.74.38 0 .687.332.687.74"/><path fill="#FFF" d="M7.523 7.923h.953v-.769h-.953z"/><path fill="#B7937F" d="M8.953 6.897c0 .284-.427.513-.953.513s-.952-.23-.952-.513c0-.283.426-.512.952-.512s.953.23.953.513"/><path fill="#000" d="M8.476 6.633c0 .288-.216.264-.484.264-.267 0-.484.024-.484-.264 0-.289.217-.522.484-.522.268 0 .484.233.484.522"/><path fill="#B7937F" fill-rule="nonzero" d="M13 8.271c0 .354-.267.641-.595.641-.329 0-.596-.287-.596-.64 0-.355.267-.642.596-.642.328 0 .595.287.595.641m-8.81 0c0 .354-.266.641-.595.641-.328 0-.595-.287-.595-.64 0-.355.267-.642.595-.642.33 0 .596.287.596.641m1 5.934c0 .354-.267.641-.596.641-.328 0-.595-.287-.595-.641 0-.354.267-.64.595-.64.329 0 .596.286.596.64"/><path fill="#9AA7B0" d="M8 14v-1l3-2v1l-2.2 1.5L11 15v1z"/><path fill="#9AA7B0" d="M16 14v-1l-3-2v1l2.2 1.5L13 15v1z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/java/java 2.svg b/src/main/resources/icons/language/java/java 2.svg
new file mode 100644
index 0000000..1371d72
--- /dev/null
+++ b/src/main/resources/icons/language/java/java 2.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#40B6E0" fill-opacity=".7" d="M1 16h15V9H1z"/><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#231F20" fill-opacity=".7" d="M3.395 13.588c.23.25.443.412.892.412.526 0 .713-.412.713-.702V10h1v3.588C6 14.32 5.327 15 4.45 15c-.821 0-1.3-.237-1.65-.68l.595-.732z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/java/java.svg b/src/main/resources/icons/language/java/java.svg
new file mode 100755
index 0000000..1371d72
--- /dev/null
+++ b/src/main/resources/icons/language/java/java.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#40B6E0" fill-opacity=".7" d="M1 16h15V9H1z"/><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#231F20" fill-opacity=".7" d="M3.395 13.588c.23.25.443.412.892.412.526 0 .713-.412.713-.702V10h1v3.588C6 14.32 5.327 15 4.45 15c-.821 0-1.3-.237-1.65-.68l.595-.732z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/javascript/javascript.svg b/src/main/resources/icons/language/javascript/javascript.svg
new file mode 100755
index 0000000..5f6e654
--- /dev/null
+++ b/src/main/resources/icons/language/javascript/javascript.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#F4AF3D" fill-opacity=".7" d="M1 16h15V9H1z"/><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#231F20" fill-opacity=".7" d="M2.395 13.588c.23.25.443.412.892.412.526 0 .713-.412.713-.702V10h1v3.588C5 14.32 4.327 15 3.45 15c-.821 0-1.3-.237-1.65-.68l.595-.732z"/><path fill="#231F20" fill-opacity=".7" d="M6.973 11.502c0-.369.311-.502.872-.502H9v-1H7.86C6.778 10 6 10.46 6 11.45c0 .865.42 1.24 1.471 1.466.77.165 1.025.313 1.025.632 0 .32-.358.452-1.025.452H6.3v1h1.171C9.5 15 9.5 14 9.5 13.548c0-.632-.48-1.127-1.341-1.401-.862-.274-1.186-.277-1.186-.645z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/javascript/js.svg b/src/main/resources/icons/language/javascript/js.svg
new file mode 100644
index 0000000..04e1d30
--- /dev/null
+++ b/src/main/resources/icons/language/javascript/js.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect id="frame" width="16" height="16" fill="none"/><path fill-rule="evenodd" clip-rule="evenodd" d="M2 13h12V3H2v10z" fill="#F4AF3D" fill-opacity=".6"/><path fill-rule="evenodd" clip-rule="evenodd" d="M9.167 6.802c0-.442.374-.602 1.047-.602H11.6V5h-1.368C8.934 5 8 5.551 8 6.74c0 1.037.503 1.489 1.765 1.76.925.198 1.23.374 1.23.758 0 .383-.429.542-1.23.542H8.36V11h1.405C12.2 11 12.2 9.8 12.2 9.258c0-.759-.576-1.353-1.61-1.682-1.033-.328-1.423-.332-1.423-.774z" fill="#231F20" fill-opacity=".7"/><path fill-rule="evenodd" clip-rule="evenodd" d="M3.674 9.305c.275.3.531.495 1.07.495.632 0 .856-.495.856-.843V5h1.2v4.305C6.8 10.184 5.992 11 4.94 11c-.985 0-1.56-.285-1.98-.816l.714-.879z" fill="#231F20" fill-opacity=".7"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/javascript/nodejs.svg b/src/main/resources/icons/language/javascript/nodejs.svg
new file mode 100644
index 0000000..6e01ea5
--- /dev/null
+++ b/src/main/resources/icons/language/javascript/nodejs.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect id="frame" width="16" height="16" fill="none"/><path fill-rule="evenodd" clip-rule="evenodd" d="M7.991 14.991a1.07 1.07 0 0 1-.533-.143l-1.697-1.004c-.254-.142-.13-.192-.046-.221a3.43 3.43 0 0 0 .767-.35c.038-.022.088-.014.126.009l1.304.774a.172.172 0 0 0 .158 0l5.085-2.935a.16.16 0 0 0 .077-.138V5.116c0-.059-.03-.11-.079-.14L8.072 2.044a.156.156 0 0 0-.157 0L2.833 4.976a.162.162 0 0 0-.08.139v5.867c0 .056.03.11.078.136l1.393.804c.756.378 1.218-.067 1.218-.515v-5.79c0-.084.065-.147.147-.147h.644c.08 0 .147.063.147.146v5.793c0 1.008-.55 1.588-1.506 1.588-.294 0-.526 0-1.172-.319l-1.334-.768a1.075 1.075 0 0 1-.533-.928V5.115c0-.382.203-.739.533-.927l5.09-2.936c.322-.182.75-.182 1.069 0l5.083 2.937c.328.19.533.545.533.927v5.867c0 .38-.205.736-.533.927l-5.083 2.936a1.068 1.068 0 0 1-.536.142" fill="#689F63"/><path d="M9.562 10.948c-2.225 0-2.691-1.021-2.691-1.878 0-.081.066-.146.147-.146h.657c.072 0 .133.053.145.124.1.67.394 1.007 1.74 1.007 1.071 0 1.527-.242 1.527-.81 0-.327-.13-.57-1.795-.733C7.9 8.374 7.04 8.068 7.04 6.954c0-1.026.865-1.637 2.315-1.637 1.628 0 2.435.565 2.536 1.78a.148.148 0 0 1-.147.16h-.66a.146.146 0 0 1-.143-.115c-.16-.704-.544-.929-1.589-.929-1.17 0-1.306.407-1.306.713 0 .37.16.478 1.739.687 1.563.208 2.305.5 2.305 1.599 0 1.107-.924 1.742-2.535 1.742l.007-.006z" fill="#689F63"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/php/localPHP_interpreter.svg b/src/main/resources/icons/language/php/localPHP_interpreter.svg
new file mode 100644
index 0000000..ff68b77
--- /dev/null
+++ b/src/main/resources/icons/language/php/localPHP_interpreter.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd" fill-opacity=".7"><path fill="#B99BF8" d="M0 13h16V3H0z"/><path fill="#231F20" d="M2.9 8.5c.664 0 1.1-.116 1.1-.75S3.557 7 2.9 7H2v1.5h.9zM1 6h1.971C4.142 6 5 6.642 5 7.65 5 9.286 3.971 9.5 2.872 9.5H2V11H1V6z"/><path fill="#231F20" d="M12.9 8.5c.664 0 1.1-.116 1.1-.75S13.557 7 12.9 7H12v1.5h.9zM11 6h1.971C14.142 6 15 6.642 15 7.65c0 1.636-1.029 1.85-2.128 1.85H12V11h-1V6z"/><path fill="#231F20" d="M6 6h1v2h2V6h1v5H9V9H7v2H6z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/php/php-icon.svg b/src/main/resources/icons/language/php/php-icon.svg
new file mode 100644
index 0000000..0d1e04b
--- /dev/null
+++ b/src/main/resources/icons/language/php/php-icon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#B99BF8" fill-opacity=".7" d="M1 16h15V9H1z"/><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#231F20" fill-opacity=".7" d="M3.9 12.5c.664 0 1.1-.116 1.1-.75S4.557 11 3.9 11H3v1.5h.9zM2 10h1.971C5.142 10 6 10.642 6 11.65c0 1.636-1.029 1.85-2.128 1.85H3V15H2v-5z"/><path fill="#231F20" fill-opacity=".7" d="M13.9 12.5c.664 0 1.1-.116 1.1-.75s-.443-.75-1.1-.75H13v1.5h.9zM12 10h1.971c1.171 0 2.029.642 2.029 1.65 0 1.636-1.029 1.85-2.128 1.85H13V15h-1v-5z"/><path fill="#231F20" fill-opacity=".7" d="M7 10h1v2h2v-2h1v5h-1v-2H8v2H7z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/php/php.svg b/src/main/resources/icons/language/php/php.svg
new file mode 100644
index 0000000..77e8e9a
--- /dev/null
+++ b/src/main/resources/icons/language/php/php.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:none;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+ .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+ .st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+ .st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+ .st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+ .st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+ .st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+ .st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+ .st9{opacity:0.6;}
+</style>
+<rect id="frame_4_" x="0" class="st0" width="16" height="16"/>
+<path class="st8" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+ <path d="M1.9,10.2h1.8c1.2,0,1.8,0.5,1.8,1.5c0,1-0.7,1.6-1.7,1.6H2.9V15h-1V10.2z M3.6,12.5c0.6,0,0.9-0.3,0.9-0.8
+ c0-0.6-0.2-0.8-0.8-0.8H2.9v1.6H3.6z"/>
+ <path d="M6.1,10.2h1v2h1.9v-2h1V15h-1v-2.1H7.1V15h-1V10.2z"/>
+ <path d="M11.1,10.2h1.8c1.2,0,1.8,0.5,1.8,1.5c0,1-0.7,1.6-1.7,1.6h-0.8V15h-1V10.2z M12.7,12.5c0.6,0,0.9-0.3,0.9-0.8
+ c0-0.6-0.2-0.8-0.8-0.8h-0.7v1.6H12.7z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/language/python/py.svg b/src/main/resources/icons/language/python/py.svg
new file mode 100644
index 0000000..e231e14
--- /dev/null
+++ b/src/main/resources/icons/language/python/py.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:none;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+ .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+ .st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+ .st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+ .st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+ .st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+ .st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+ .st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+ .st9{opacity:0.6;}
+</style>
+<rect id="frame_3_" x="0" class="st0" width="16" height="16"/>
+<path class="st3" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+ <path d="M1.8,10h1.8c1.2,0,1.8,0.5,1.8,1.5c0,1-0.7,1.6-1.7,1.6H2.8v1.8h-1V10z M3.5,12.4c0.6,0,0.9-0.3,0.9-0.8
+ c0-0.6-0.2-0.8-0.8-0.8H2.8v1.6H3.5z"/>
+ <path d="M7.2,13l-1.6-3h1.1l1,2.1h0l1-2.1h1l-1.6,3v1.9h-1V13z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/language/python/python.svg b/src/main/resources/icons/language/python/python.svg
new file mode 100644
index 0000000..ef015b5
--- /dev/null
+++ b/src/main/resources/icons/language/python/python.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-opacity=".9"><path fill="#40A0CF" d="M7.91 1.026c-.54 0-1.08.046-1.612.136-1.428.25-1.687.772-1.687 1.735v1.271h3.374v.424h-4.64a2.102 2.102 0 0 0-2.108 1.692 6.172 6.172 0 0 0 0 3.391 1.879 1.879 0 0 0 1.794 1.692h1.16V9.842A2.132 2.132 0 0 1 6.297 7.77h3.37a1.69 1.69 0 0 0 1.687-1.696V2.897a1.872 1.872 0 0 0-1.687-1.735 10.636 10.636 0 0 0-1.758-.136zM6.086 2.049A.636.636 0 1 1 6.09 3.32a.636.636 0 0 1-.005-1.271z"/><path fill="#FFC900" d="M11.775 6.074A2.152 2.152 0 0 1 9.668 8.19h-3.37a1.711 1.711 0 0 0-1.687 1.695v3.178c.11.883.804 1.58 1.687 1.695 1.098.34 2.272.34 3.37 0a1.925 1.925 0 0 0 1.687-1.695V11.79h-3.37v-.424h5.057c.98 0 1.345-.676 1.686-1.692a5.667 5.667 0 0 0 0-3.391 1.801 1.801 0 0 0-1.686-1.692h-1.267v1.482zM9.88 12.639a.636.636 0 1 1 .006 1.271.636.636 0 0 1-.006-1.271z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/python/pythonFile.svg b/src/main/resources/icons/language/python/pythonFile.svg
new file mode 100644
index 0000000..1f17d0f
--- /dev/null
+++ b/src/main/resources/icons/language/python/pythonFile.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 15v-.703c-.633-.097-1.117-.405-1.45-.923-.5-.777-.615-1.938-.352-2.886a2.49 2.49 0 0 1 2.384-1.97v-.214a2.081 2.081 0 0 1 .54-1.549c.384-.42.887-.743 1.457-.755.437-.074.916-.033 1.36-.033a5.49 5.49 0 0 1 1.061.11V1H8v5H3v9h5z"/><path fill="#40A0CF" fill-opacity=".9" fill-rule="nonzero" d="M11.491 6.878c-.35 0-.701.03-1.047.089-.928.162-1.097.501-1.097 1.127v.827h2.193v.275H8.524a1.366 1.366 0 0 0-1.37 1.1 4.011 4.011 0 0 0 0 2.204c.06.606.558 1.075 1.166 1.1h.754v-.991a1.386 1.386 0 0 1 1.37-1.347h2.19a1.099 1.099 0 0 0 1.096-1.103V8.094a1.217 1.217 0 0 0-1.096-1.127 6.913 6.913 0 0 0-1.143-.089zm-1.186.665a.413.413 0 1 1 .004.827.413.413 0 0 1-.004-.827z"/><path fill="#FFC900" fill-opacity=".9" fill-rule="nonzero" d="M14.004 9.196v.963a1.399 1.399 0 0 1-1.37 1.376h-2.19a1.112 1.112 0 0 0-1.097 1.102v2.065a1.27 1.27 0 0 0 1.097 1.102c.713.22 1.476.22 2.19 0a1.251 1.251 0 0 0 1.096-1.102v-.827h-2.19V13.6h3.286c.638 0 .875-.44 1.097-1.1a3.683 3.683 0 0 0 0-2.204 1.17 1.17 0 0 0-1.097-1.1h-.822zm-1.232 5.23a.413.413 0 1 1 .003.827.413.413 0 0 1-.003-.827z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/ruby/rb.svg b/src/main/resources/icons/language/ruby/rb.svg
new file mode 100644
index 0000000..1e99135
--- /dev/null
+++ b/src/main/resources/icons/language/ruby/rb.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:none;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+ .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+ .st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+ .st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+ .st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+ .st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+ .st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+ .st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+ .st9{opacity:0.6;}
+</style>
+<rect id="frame" x="0" class="st0" width="16" height="16"/>
+<path class="st1" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+ <path d="M1.8,10h1.7c1.1,0,1.8,0.5,1.8,1.4c0,0.7-0.3,1-0.9,1.2v0l1.2,2.1H4.5l-1-1.9H2.8v1.9h-1V10z M3.4,12.3
+ c0.6,0,0.8-0.3,0.8-0.8c0-0.5-0.2-0.8-0.8-0.8H2.8v1.5H3.4z"/>
+ <path d="M6.2,10H8c1.1,0,1.6,0.4,1.6,1.2c0,0.5-0.2,0.9-0.8,1.1v0c0.6,0.1,1,0.4,1,1.2c0,0.8-0.5,1.4-1.7,1.4H6.2V10z M7.8,12.1
+ c0.5,0,0.8-0.2,0.8-0.7c0-0.5-0.3-0.7-0.8-0.7H7.2v1.3H7.8z M7.9,14.2c0.6,0,0.9-0.2,0.9-0.8c0-0.5-0.3-0.7-0.9-0.7H7.2v1.5H7.9z"
+ />
+</g>
+</svg>
diff --git a/src/main/resources/icons/language/ruby/ruby.svg b/src/main/resources/icons/language/ruby/ruby.svg
new file mode 100644
index 0000000..10b36c0
--- /dev/null
+++ b/src/main/resources/icons/language/ruby/ruby.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#F26522" fill-opacity=".7" fill-rule="evenodd" d="M13.6 3H2.401L1 5.444 8 14l7-8.556z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/ruby/ruby_file.svg b/src/main/resources/icons/language/ruby/ruby_file.svg
new file mode 100644
index 0000000..d882654
--- /dev/null
+++ b/src/main/resources/icons/language/ruby/ruby_file.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#9AA7B0" fill-opacity=".8" d="M6.624 15l3.59-4.344L8.676 8H3V6h5V1h5v14H6.624zM7 1v4H3l4-4z"/><path fill="#F26522" fill-opacity=".7" d="M8.1 9H.901L0 10.556 4.5 16 9 10.556z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/ruby/rxml.svg b/src/main/resources/icons/language/ruby/rxml.svg
new file mode 100644
index 0000000..6a81c61
--- /dev/null
+++ b/src/main/resources/icons/language/ruby/rxml.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#F26522" fill-opacity=".6" d="M1 16h15V9H1z"/><path fill="#231F20" fill-opacity=".7" d="M3 15v-5h2c1.156 0 2 .6 2 1.613 0 1.014-.29 1.507-.978 1.706L7.263 15H6.127l-1.221-1.6H4V15H3zm1-2.531h1.111c.544 0 .89-.288.89-.732v-.012c0-.469-.334-.725-.896-.725H4v1.469z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/swift/SwiftLang.svg b/src/main/resources/icons/language/swift/SwiftLang.svg
new file mode 100644
index 0000000..3d51565
--- /dev/null
+++ b/src/main/resources/icons/language/swift/SwiftLang.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#EB4548" fill-rule="evenodd" d="M11.56 13.1c-1.65.988-3.917 1.09-6.198.076-1.847-.814-3.38-2.24-4.362-3.869.472.408 1.022.734 1.611 1.018 2.356 1.145 4.712 1.066 6.37.003-.002 0-.003-.001-.003-.003C6.62 8.452 4.615 6.01 3.122 4.013c-.314-.325-.55-.733-.786-1.1 1.808 1.711 4.677 3.87 5.698 4.48C5.874 5.032 3.947 2.1 4.026 2.182c3.419 3.584 6.602 5.62 6.602 5.62.106.061.187.113.252.158.07-.181.13-.37.18-.565.55-2.077-.078-4.44-1.454-6.394 3.183 1.995 5.07 5.742 4.284 8.878-.02.084-.043.168-.067.25l.028.035c1.571 2.036 1.14 4.194.943 3.787-.853-1.729-2.431-1.2-3.234-.85z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/swift/sw.svg b/src/main/resources/icons/language/swift/sw.svg
new file mode 100644
index 0000000..b3853c8
--- /dev/null
+++ b/src/main/resources/icons/language/swift/sw.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:none;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+ .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+ .st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+ .st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+ .st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+ .st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+ .st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+ .st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+ .st9{opacity:0.6;}
+</style>
+<rect id="frame_5_" x="0" class="st0" width="16" height="16"/>
+<path class="st7" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+ <path d="M1.8,14.7V14c0.4,0.2,0.9,0.3,1.4,0.3c0.6,0,0.9-0.3,0.9-0.6c0-0.4-0.2-0.5-0.7-0.7L3,12.8c-0.7-0.2-1.1-0.5-1.1-1.4
+ c0-0.8,0.5-1.4,1.7-1.4c0.7,0,1,0.1,1.3,0.3V11c-0.3-0.1-0.7-0.2-1.3-0.2c-0.5,0-0.8,0.2-0.8,0.6c0,0.3,0.2,0.5,0.6,0.6l0.4,0.1
+ c0.8,0.2,1.3,0.5,1.3,1.4c0,1-0.6,1.5-1.8,1.5C2.6,15,2.1,14.9,1.8,14.7z"/>
+ <path d="M5.4,10.1h1l0.8,3.4h0l0.9-3.4H9l0.9,3.4h0l0.8-3.4h0.9l-1.2,4.9h-1l-0.9-3.4h0l-0.9,3.4h-1L5.4,10.1z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/openDiskHover.svg b/src/main/resources/icons/openDiskHover.svg
new file mode 100644
index 0000000..39d0a38
--- /dev/null
+++ b/src/main/resources/icons/openDiskHover.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#7F8B91" fill-opacity=".9" fill-rule="evenodd" d="M3.49 7L1 11.885V3h4.999L7 5H14v2H3.49zm.615 1H16l-3.098 6H1l3.105-6z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/run_anything.svg b/src/main/resources/icons/run_anything.svg
new file mode 100644
index 0000000..790a8fe
--- /dev/null
+++ b/src/main/resources/icons/run_anything.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#6E6E6E" d="M1 2h14v12H1V2zm1 3v8h12V5H2z"/><path fill="#59A869" d="M4 6l4.667 3L4 12z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/seq.svg b/src/main/resources/icons/seq.svg
new file mode 100644
index 0000000..bedff5e
--- /dev/null
+++ b/src/main/resources/icons/seq.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:none;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+ .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+ .st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+ .st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+ .st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+ .st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+ .st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+ .st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+ .st9{opacity:0.6;}
+</style>
+<rect id="frame_6_" x="0" class="st0" width="16" height="16"/>
+<path class="st6" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+ <path d="M1.8,14.7V14c0.4,0.2,0.9,0.3,1.4,0.3c0.6,0,0.9-0.3,0.9-0.6c0-0.4-0.2-0.5-0.7-0.7L3,12.8c-0.7-0.2-1.1-0.5-1.1-1.4
+ c0-0.8,0.5-1.4,1.7-1.4c0.7,0,1,0.1,1.3,0.3V11c-0.3-0.1-0.7-0.2-1.3-0.2c-0.5,0-0.8,0.2-0.8,0.6c0,0.3,0.2,0.5,0.6,0.6l0.4,0.1
+ c0.8,0.2,1.3,0.5,1.3,1.4c0,1-0.6,1.5-1.8,1.5C2.6,15,2.1,14.9,1.8,14.7z"/>
+ <path d="M5.8,10.1h3.1v0.8H6.8v1.2h1.7v0.8H6.8v1.3H9v0.8H5.8V10.1z"/>
+ <path d="M12.7,14.7l1.5,0.3L14,15.8L11.3,15c-1-0.3-1.7-0.9-1.7-2.4v-0.2c0-1.6,1-2.4,2.3-2.4c1.3,0,2.2,0.8,2.2,2.4v0.1
+ C14.1,13.7,13.6,14.4,12.7,14.7z M13,12.7v-0.2c0-1.1-0.3-1.7-1.2-1.7c-0.8,0-1.2,0.6-1.2,1.6v0.3c0,1.1,0.4,1.7,1.2,1.7
+ C12.6,14.3,13,13.7,13,12.7z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/sh.svg b/src/main/resources/icons/sh.svg
new file mode 100644
index 0000000..26473bf
--- /dev/null
+++ b/src/main/resources/icons/sh.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:none;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+ .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+ .st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+ .st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+ .st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+ .st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+ .st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+ .st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+ .st9{opacity:0.6;}
+</style>
+<rect id="frame_7_" x="0" class="st0" width="16" height="16"/>
+<path class="st5" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+ <path d="M1.8,14.7V14c0.4,0.2,0.9,0.3,1.4,0.3c0.6,0,0.9-0.3,0.9-0.6c0-0.4-0.2-0.5-0.7-0.7L3,12.8c-0.7-0.2-1.1-0.5-1.1-1.4
+ c0-0.8,0.5-1.4,1.7-1.4c0.7,0,1,0.1,1.3,0.3V11c-0.3-0.1-0.7-0.2-1.3-0.2c-0.5,0-0.8,0.2-0.8,0.6c0,0.3,0.2,0.5,0.6,0.6l0.4,0.1
+ c0.8,0.2,1.3,0.5,1.3,1.4c0,1-0.6,1.5-1.8,1.5C2.6,15,2.1,14.9,1.8,14.7z"/>
+ <path d="M5.8,10.1h1v2h1.9v-2h1v4.9h-1v-2.1H6.8v2.1h-1V10.1z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/ssh.svg b/src/main/resources/icons/ssh.svg
new file mode 100644
index 0000000..99c2add
--- /dev/null
+++ b/src/main/resources/icons/ssh.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#9AA7B0" fill-opacity=".8" fill-rule="evenodd" d="M7.064 7.933L4.76 10.066l1.272 1.178 3.564-3.3-.012-.01.012-.012-3.564-3.3L4.76 5.8l2.304 2.133zM2 3h12v10H2V3z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/yaml.svg b/src/main/resources/icons/yaml.svg
new file mode 100644
index 0000000..4a155c7
--- /dev/null
+++ b/src/main/resources/icons/yaml.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#F98B9E" fill-opacity=".7" d="M1 16h15V9H1z"/><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#231F20" fill-opacity=".7" d="M2.996 15v-1.971L1 10h1.05l1.446 2.207L4.95 10H6l-2 3.007V15z"/><path fill="#231F20" fill-opacity=".7" d="M6 10h.936L8.5 12l1.479-2H11v5h-1v-3.3l-1.5 1.8L7 11.7V15H6z"/><path fill="#231F20" fill-opacity=".7" d="M12 10h1v4h2.5v1H12z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/template/HOW-TO-DEPLOY.md b/src/main/resources/template/HOW-TO-DEPLOY.md
new file mode 100644
index 0000000..a63853b
--- /dev/null
+++ b/src/main/resources/template/HOW-TO-DEPLOY.md
@@ -0,0 +1,75 @@
+# What is wskdeploy
+
+`wskdeploy` is a utility to help you deploy any part of the OpenWhisk programming model using a Manifest file written in YAML. Use it to deploy all your OpenWhisk Packages, Actions, Triggers, and Rules!
+
+## Downloading wskdeploy
+
+Binaries of wskdeploy are available for download on the project's GitHub release page.
+https://github.com/apache/openwhisk-wskdeploy/releases
+
+## Getting started
+
+You can find various examples in the official documentation.
+https://github.com/apache/openwhisk-wskdeploy/blob/master/docs/programming_guide.md#getting-started
+
+
+# The "Hello World" sample wskdeploy project
+
+## Files included
+
+The following files are included in the sample project by default:
+- manifest.yaml : Manifest file for wskdeploy
+- src/index.js : Sample action code
+- src/index.test.js : Test code for the action code
+- src/package.json : package.json for NPM
+
+## How to deploy the project
+
+You can run the deploy command from the wskdeploy manifests explorer in the VSCode sidebar or run the following command in the terminal:
+
+```
+$ wskdeploy -m manifest.yaml
+```
+
+### Including dependencies
+
+The sample project deploys only the function code in the index.js file. But sometimes you need to add NPM or your dependencies.
+
+In this case, simply use a directory path as a function endpoint to compress and deploy all the files.
+
+```yaml
+packages:
+ hello_world_package:
+ actions:
+ hello_world:
+ # use src directory to deploy the action
+ # or deploy single code (function: index.js)
+ function: src
+ runtime: nodejs:default
+```
+
+Install NPM dependency modules and deploy wskdeploy project:
+(Note that wskdeploy does not automatically install npm dependencies.)
+
+```
+$ cd src && npm install --production
+$ wskdeploy -m manifest.yaml
+```
+
+## How to invoke the action
+
+You can invoke the action in the openwhisk explorer located in the activity bar or invoke it with the following command:
+
+```
+$ wsk action invoke hello_world_package/hello_world --blocking
+```
+
+## Test action code in your local environment
+
+The jest dependency is included by default. So you can test with `npm run test` command.
+
+```
+$ cd src
+$ npm install
+$ npm run test
+```
\ No newline at end of file
diff --git a/src/main/resources/template/manifest.yaml b/src/main/resources/template/manifest.yaml
new file mode 100644
index 0000000..4b6f5d0
--- /dev/null
+++ b/src/main/resources/template/manifest.yaml
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+packages:
+ hello_world_package:
+ version: 1.0
+ license: Apache-2.0
+ actions:
+ hello_world:
+ function: src/index.js
+ runtime: nodejs:default
+ inputs:
+ name: string
+ place: string
+ children: integer
+ height: float
+ outputs:
+ greeting: string
+ details: string
+
+ triggers:
+ meetPerson:
+ inputs:
+ name: Sam
+ place: the Shire
+ children: 13
+ height: 1.2
+
+ rules:
+ meetPersonRule:
+ trigger: meetPerson
+ action: hello_world
\ No newline at end of file
diff --git a/src/main/resources/template/src/index.js b/src/main/resources/template/src/index.js
new file mode 100644
index 0000000..fc7490a
--- /dev/null
+++ b/src/main/resources/template/src/index.js
@@ -0,0 +1,7 @@
+function main(params) {
+ const msg = 'Hello, ' + params.name + ' from ' + params.place;
+ const family = 'You have ' + params.children + ' children ';
+ const stats = 'and are ' + params.height + ' m. tall.';
+ return { greeting: msg, details: family + stats };
+}
+module.exports.main = main;
\ No newline at end of file
diff --git a/src/main/resources/template/src/index.test.js b/src/main/resources/template/src/index.test.js
new file mode 100644
index 0000000..8f7ac66
--- /dev/null
+++ b/src/main/resources/template/src/index.test.js
@@ -0,0 +1,12 @@
+const { main } = require('.');
+
+test('results have a greeting and details property', () => {
+ const result = main({
+ name: 'name',
+ place: 'place',
+ children: 'children',
+ height: 'height',
+ });
+ expect(result).toHaveProperty('greeting');
+ expect(result).toHaveProperty('details');
+});
diff --git a/src/main/resources/template/src/package.json b/src/main/resources/template/src/package.json
new file mode 100644
index 0000000..2f89b86
--- /dev/null
+++ b/src/main/resources/template/src/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "openwhisk-sample-app",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "engines": {
+ "node": ">=10.0.0",
+ "npm": ">=6.0.0"
+ },
+ "scripts": {
+ "test": "jest"
+ },
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "jest": "^26.1.0"
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/utils/json/EmptyJsonParserUtilsTests.java b/src/test/java/com/navercorp/openwhisk/intellij/common/utils/json/EmptyJsonParserUtilsTests.java
new file mode 100644
index 0000000..96c2406
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/utils/json/EmptyJsonParserUtilsTests.java
@@ -0,0 +1,225 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils.json;
+
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskRule;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.*;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+
+public class EmptyJsonParserUtilsTests {
+
+ @Test
+ public void parseEmptyWhiskActions() throws IOException {
+ // given
+ String empty = "";
+
+ List<WhiskAction> expected = new ArrayList<>();
+
+ // when
+ List<WhiskActionMetaData> actual = JsonParserUtils.parseWhiskActions(empty);
+
+ // then
+ assertEquals(actual, expected);
+ }
+
+ @Test
+ public void parseEmtpyWhiskAction() throws IOException {
+ // given
+ String empty = "";
+
+ // when
+ Optional<ExecutableWhiskAction> actual = JsonParserUtils.parseWhiskAction(empty);
+
+ // then
+ assertFalse(actual.isPresent());
+ }
+
+ @Test
+ public void parseEmptyWhiskPackages() throws IOException {
+ // given
+ String empty = "";
+
+ List<WhiskPackage> expected = new ArrayList<>();
+
+ // when
+ List<WhiskPackage> actual = JsonParserUtils.parseWhiskPackages(empty);
+
+ // then
+ assertEquals(actual, expected);
+ }
+
+ @Test
+ public void parseEmtpyWhiskPackage() throws IOException {
+ // given
+ String empty = "";
+
+ // when
+ Optional<WhiskPackageWithActions> actual = JsonParserUtils.parseWhiskPackage(empty);
+
+ // then
+ assertFalse(actual.isPresent());
+ }
+
+ @Test
+ public void parseEmptyWhiskEndpoints() throws IOException {
+ // given
+ String empty = "";
+
+ List<WhiskEndpoint> expected = new ArrayList<>();
+
+ // when
+ List<WhiskEndpoint> actual = JsonParserUtils.parseWhiskEndpoints(empty);
+
+ // then
+ assertEquals(actual, expected);
+ }
+
+ @Test
+ public void parseEmptyWhiskNamespaces() throws IOException {
+ // given
+ String empty = "";
+
+ // when
+ String[] actual = JsonParserUtils.parseWhiskNamespace(empty);
+
+ // then
+ assertEquals(actual.length, 0);
+
+ }
+
+ @Test
+ public void parseEmptyWhiskActivations() throws IOException {
+ // given
+ String empty = "";
+
+ List<WhiskActivationMetaData> expected = new ArrayList<>();
+
+ // when
+ List<WhiskActivationMetaData> actual = JsonParserUtils.parseWhiskActivations(empty);
+
+ // then
+ assertEquals(actual, expected);
+ }
+
+ @Test
+ public void parseEmtpyActivation() throws IOException {
+ // given
+ String empty = "";
+
+ // when
+ Optional<WhiskActivationWithLogs> actual = JsonParserUtils.parseWhiskActivation(empty);
+
+ // then
+ assertFalse(actual.isPresent());
+ }
+
+ @Test
+ public void parseEmptyWhiskTriggers() throws IOException {
+ // given
+ String empty = "";
+
+ List<WhiskTriggerMetaData> expected = new ArrayList<>();
+
+ // when
+ List<WhiskTriggerMetaData> actual = JsonParserUtils.parseWhiskTriggers(empty);
+
+ // then
+ assertEquals(actual, expected);
+ }
+
+ @Test
+ public void parseEmtpyTrigger() throws IOException {
+ // given
+ String empty = "";
+
+ // when
+ Optional<ExecutableWhiskTrigger> actual = JsonParserUtils.parseWhiskTrigger(empty);
+
+ // then
+ assertFalse(actual.isPresent());
+ }
+
+ @Test
+ public void parseEmtpyRule() throws IOException {
+ // given
+ String empty = "";
+
+ // when
+ Optional<WhiskRule> actual = JsonParserUtils.parseWhiskRule(empty);
+
+ // then
+ assertFalse(actual.isPresent());
+ }
+
+ @Test
+ public void parseEmptyListMap() throws IOException {
+ // given
+ String empty = "";
+
+ List<Map<String, Object>> expected = new ArrayList<>();
+
+ // when
+ List<Map<String, Object>> actual = JsonParserUtils.parseListMap(empty);
+
+ // then
+ assertEquals(actual, expected);
+ }
+
+ @Test
+ public void parseEmptyMap() throws IOException {
+ // given
+ String empty = "";
+
+ Map<String, Object> expected = new LinkedHashMap<>();
+
+ // when
+ Map<String, Object> actual = JsonParserUtils.parseMap(empty);
+
+ // then
+ assertEquals(actual, expected);
+ }
+
+ @Test
+ public void beautifyEmptyJson() throws IOException {
+ // given
+ String empty = "";
+
+ // when
+ String actual = JsonParserUtils.beautifyJson(empty);
+
+ // then
+ assertEquals(actual, "");
+ }
+
+
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/utils/whisk/WhiskUtilsTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/utils/whisk/WhiskUtilsTest.java
new file mode 100644
index 0000000..8562dab
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/utils/whisk/WhiskUtilsTest.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils.whisk;
+
+import com.navercorp.openwhisk.intellij.common.utils.WhiskUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class WhiskUtilsTest {
+ @Test
+ public void testGetApihHostWithProtocol() {
+ String expected1 = "https://foo.com";
+ String expected2 = "http://foo.com";
+
+ Assert.assertEquals(expected1, WhiskUtils.getApihHostWithProtocol("foo.com"));
+ assertEquals(expected2, WhiskUtils.getApihHostWithProtocol("http://foo.com"));
+ }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionJsonTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionJsonTest.java
new file mode 100644
index 0000000..aa44e5d
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionJsonTest.java
@@ -0,0 +1,239 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.CodeExec;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.ExecMetaData;
+import junit.framework.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.createActionAnnotation;
+import static com.navercorp.openwhisk.intellij.utils.FileHelper.readFile;
+
+public class WhiskActionJsonTest {
+
+ @Test
+ public void testParseWhiskActionList() throws IOException {
+ // given
+ String actions = readFile("actions.json");
+
+ List<WhiskAction> expected = new ArrayList<>();
+ expected.add(new WhiskActionMetaData("testAct1", "testNs/testPkg1", "0.0.1", 1585756253499L, false,
+ createActionAnnotation(true, false, false, true, "nodejs:12"),
+ new Limits(1, 1, 256, 60000),
+ new ExecMetaData(true)));
+ expected.add(new WhiskActionMetaData("testAct2", "testNs/testPkg2", "0.0.1", 1585756252393L, false,
+ createActionAnnotation(true, false, false, true, "nodejs:12"),
+ new Limits(1, 1, 256, 60000),
+ new ExecMetaData(true)));
+ expected.add(new WhiskActionMetaData("testAct3", "testNs/testPkg3", "0.0.1", 1585756251289L, false,
+ createActionAnnotation(true, false, false, true, "nodejs:12"),
+ new Limits(1, 1, 256, 60000), new ExecMetaData(true)));
+ expected.add(new WhiskActionMetaData("testAct4", "testNs/testPkg4", "0.0.2", 1582709807073L, false,
+ createActionAnnotation(false, false, false, false, "blackbox"),
+ new Limits(1, 1, 256, 60000), new ExecMetaData(false)));
+ expected.add(new WhiskActionMetaData("testAct5", "testNs/testPkg5", "0.0.1", 1581316522182L, false,
+ createActionAnnotation(false, false, false, false, "python:3"),
+ new Limits(1, 1, 256, 60000), new ExecMetaData(false)));
+
+ // when
+ List<WhiskActionMetaData> actual = JsonParserUtils.parseWhiskActions(actions);
+
+ // then
+ Assert.assertEquals(expected, actual);
+ }
+
+
+ @Test
+ public void testParseNormalWhiskAction() throws IOException {
+ // given
+ String action = readFile("action.json");
+
+ List<Map<String, Object>> parameters = new ArrayList<>();
+ Map<String, Object> param1 = new LinkedHashMap<>();
+ param1.put("key", "name");
+ param1.put("value", "test");
+ parameters.add(param1);
+
+ Map<String, Object> param2 = new LinkedHashMap<>();
+ param2.put("key", "count");
+ param2.put("value", 1);
+ parameters.add(param2);
+
+ ExecutableWhiskAction expected = new ExecutableWhiskAction(
+ "testAct",
+ "testNs/testPkg",
+ "0.0.1",
+ 1581316522182L,
+ false,
+ createActionAnnotation(false, false, false, false, "nodejs:12"),
+ new Limits(1, 1, 256, 60000),
+ new CodeExec(false, "nodejs:12", null, "function main(params) {}", null, new ArrayList<>()),
+ parameters);
+
+ // when
+ ExecutableWhiskAction actual = JsonParserUtils.parseWhiskAction(action).get();
+
+ // then
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testParseNormalBinaryAction() throws IOException {
+ // given
+ String action = readFile("binary-action.json");
+
+ List<Map<String, Object>> parameters = new ArrayList<>();
+ Map<String, Object> param1 = new LinkedHashMap<>();
+ param1.put("key", "name");
+ param1.put("value", "test");
+ parameters.add(param1);
+
+ Map<String, Object> param2 = new LinkedHashMap<>();
+ param2.put("key", "count");
+ param2.put("value", 1);
+ parameters.add(param2);
+
+ ExecutableWhiskAction expected = new ExecutableWhiskAction(
+ "testAct",
+ "testNs/testPkg",
+ "0.0.1",
+ 1585756253499L,
+ false,
+ createActionAnnotation(false, false, false, false, "nodejs:12"),
+ new Limits(1, 1, 256, 60000),
+ new CodeExec(true, "nodejs:12", null, "aaaaa", null, new ArrayList<>()),
+ parameters);
+
+ // when
+ ExecutableWhiskAction actual = JsonParserUtils.parseWhiskAction(action).get();
+
+ // then
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testParseJavaAction() throws IOException {
+ // given
+ String action = readFile("java-action.json");
+
+ List<Map<String, Object>> parameters = new ArrayList<>();
+ Map<String, Object> param1 = new LinkedHashMap<>();
+ param1.put("key", "name");
+ param1.put("value", "test");
+ parameters.add(param1);
+
+ Map<String, Object> param2 = new LinkedHashMap<>();
+ param2.put("key", "count");
+ param2.put("value", 1);
+ parameters.add(param2);
+
+ ExecutableWhiskAction expected = new ExecutableWhiskAction(
+ "testAct",
+ "testNs/testPkg",
+ "0.0.1",
+ 1585756253499L,
+ false,
+ createActionAnnotation(false, false, false, false, "java"),
+ new Limits(1, 1, 256, 60000),
+ new CodeExec(true, "java", "test", "aaaaa", null, new ArrayList<>()),
+ parameters);
+
+ // when
+ ExecutableWhiskAction actual = JsonParserUtils.parseWhiskAction(action).get();
+
+ // then
+ Assert.assertEquals(actual, expected);
+ }
+
+ @Test
+ public void testParseSequenceAction() throws IOException {
+ // given
+ String action = readFile("sequence-action.json");
+
+ List<String> components = new ArrayList<>();
+ components.add("/testNs/testAct1");
+ components.add("/testNs/testAct2");
+
+ List<Map<String, Object>> parameters = new ArrayList<>();
+ Map<String, Object> param1 = new LinkedHashMap<>();
+ param1.put("key", "_actions");
+ param1.put("components", components);
+ parameters.add(param1);
+
+ ExecutableWhiskAction expected = new ExecutableWhiskAction(
+ "testAct",
+ "testNs/testPkg",
+ "0.0.1",
+ 1585756253499L,
+ false,
+ createActionAnnotation(false, false, false, false, "sequence"),
+ new Limits(1, 1, 256, 60000),
+ new CodeExec(false, "sequence", null, null, null, components),
+ parameters);
+
+ // when
+ ExecutableWhiskAction actual = JsonParserUtils.parseWhiskAction(action).get();
+
+ // then
+ Assert.assertEquals(actual, expected);
+ }
+
+ @Test
+ public void testParseBlackboxAction() throws IOException {
+ // given
+ String action = readFile("blackbox-action.json");
+
+ List<Map<String, Object>> parameters = new ArrayList<>();
+ Map<String, Object> param1 = new LinkedHashMap<>();
+ param1.put("key", "name");
+ param1.put("value", "test");
+ parameters.add(param1);
+
+ Map<String, Object> param2 = new LinkedHashMap<>();
+ param2.put("key", "count");
+ param2.put("value", 1);
+ parameters.add(param2);
+
+ ExecutableWhiskAction expected = new ExecutableWhiskAction(
+ "testAct",
+ "testNs/testPkg",
+ "0.0.1",
+ 1585756253499L,
+ false,
+ createActionAnnotation(false, false, false, false, "blackbox"),
+ new Limits(1, 1, 256, 60000),
+ new CodeExec(true, "blackbox", null, null, "imageRepo/imageName", new ArrayList<>()),
+ parameters);
+
+ // when
+ ExecutableWhiskAction actual = JsonParserUtils.parseWhiskAction(action).get();
+
+ // then
+ Assert.assertEquals(expected, actual);
+ }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionTest.java
new file mode 100644
index 0000000..afce38c
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionTest.java
@@ -0,0 +1,220 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.navercorp.openwhisk.intellij.common.Icons;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.CodeExec;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.ExecMetaData;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityEntry;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityType;
+import org.junit.Test;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.createActionAnnotation;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class WhiskActionTest {
+ private String name = "testName";
+ private String namespace = "testNamespace";
+ private String pkg = "testPkg";
+ private String version = "1.0.0";
+ private long updated = 1581316522182L;
+ private boolean publish = false;
+ private List<Map<String, Object>> annotations = createActionAnnotation(false, false, false, false, "nodejs:12");
+ private Limits limits = new Limits(1, 1, 256, 60000);
+ private ExecMetaData exec = new ExecMetaData(false);
+
+ @Test
+ public void testGetWhiskPackage() {
+ String expectedPkg = pkg;
+
+ WhiskActionMetaData action = new WhiskActionMetaData(name, namespace, version, updated, publish, annotations, limits, exec);
+ WhiskActionMetaData actionWithPkg = new WhiskActionMetaData(name, namespace + "/" + pkg, version, updated, publish, annotations, limits, exec);
+
+ assertEquals(Optional.empty(), action.getWhiskPackage());
+ assertEquals(Optional.of(expectedPkg), actionWithPkg.getWhiskPackage());
+ }
+
+
+ @Test
+ public void testGetNamespacePath() {
+ String expectedNamespace = namespace;
+
+ WhiskActionMetaData action = new WhiskActionMetaData(name, namespace, version, updated, publish, annotations, limits, exec);
+ WhiskActionMetaData actionWithPkg = new WhiskActionMetaData(name, namespace + "/" + pkg, version, updated, publish, annotations, limits, exec);
+
+ assertEquals(expectedNamespace, action.getNamespacePath());
+ assertEquals(expectedNamespace, actionWithPkg.getNamespacePath());
+ }
+
+ @Test
+ public void testGetFullyQualifiedName() {
+ String expectedFQN = namespace + "/" + name;
+ String expectedPkgFQN = namespace + "/" + pkg + "/" + name;
+
+ WhiskActionMetaData action = new WhiskActionMetaData(name, namespace, version, updated, publish, annotations, limits, exec);
+ WhiskActionMetaData actionWithPkg = new WhiskActionMetaData(name, namespace + "/" + pkg, version, updated, publish, annotations, limits, exec);
+
+ assertEquals(expectedFQN, action.getFullyQualifiedName());
+ assertEquals(expectedPkgFQN, actionWithPkg.getFullyQualifiedName());
+ }
+
+
+ @Test
+ public void testGetKind() {
+ String expectedNodeKind = "nodejs:12";
+ String expectedRubyKind = "ruby:2.5";
+ String expectedPhpKind = "php:7.3";
+
+ List<Map<String, Object>> nodeKindAnnotations = createActionAnnotation(false, false, false, false, expectedNodeKind);
+ List<Map<String, Object>> rubyKindAnnotations = createActionAnnotation(false, false, false, false, expectedRubyKind);
+ List<Map<String, Object>> phpKindAnnotations = createActionAnnotation(false, false, false, false, expectedPhpKind);
+
+ WhiskActionMetaData nodeAction = new WhiskActionMetaData(name, namespace, version, updated, publish, nodeKindAnnotations, limits, exec);
+ WhiskActionMetaData rubyAction = new WhiskActionMetaData(name, namespace, version, updated, publish, rubyKindAnnotations, limits, exec);
+ WhiskActionMetaData phpAction = new WhiskActionMetaData(name, namespace, version, updated, publish, phpKindAnnotations, limits, exec);
+
+ assertEquals(expectedNodeKind, nodeAction.getKind());
+ assertEquals(expectedRubyKind, rubyAction.getKind());
+ assertEquals(expectedPhpKind, phpAction.getKind());
+ }
+
+ @Test
+ public void testGetKindExtension() {
+ String expectedNodeExtension = ".js";
+ String expectedRubyExtension = ".rb";
+ String expectedPhpExtension = ".php";
+
+ String nodeKind = "nodejs:12";
+ String rubyKind = "ruby:2.5";
+ String phpKind = "php:7.3";
+
+ List<Map<String, Object>> nodeKindAnnotations = createActionAnnotation(false, false, false, false, nodeKind);
+ List<Map<String, Object>> rubyKindAnnotations = createActionAnnotation(false, false, false, false, rubyKind);
+ List<Map<String, Object>> phpKindAnnotations = createActionAnnotation(false, false, false, false, phpKind);
+
+ WhiskActionMetaData nodeAction = new WhiskActionMetaData(name, namespace, version, updated, publish, nodeKindAnnotations, limits, exec);
+ WhiskActionMetaData rubyAction = new WhiskActionMetaData(name, namespace, version, updated, publish, rubyKindAnnotations, limits, exec);
+ WhiskActionMetaData phpAction = new WhiskActionMetaData(name, namespace, version, updated, publish, phpKindAnnotations, limits, exec);
+
+ assertEquals(expectedNodeExtension, nodeAction.getKindExtension());
+ assertEquals(expectedRubyExtension, rubyAction.getKindExtension());
+ assertEquals(expectedPhpExtension, phpAction.getKindExtension());
+ }
+
+
+ @Test
+ public void testGetKindIcon() {
+ Icon expectedNodeIcon = Icons.KIND_JS;
+ Icon expectedRubyIcon = Icons.KIND_RUBY;
+ Icon expectedPhpIcon = Icons.KIND_PHP;
+
+ String nodeKind = "nodejs:12";
+ String rubyKind = "ruby:2.5";
+ String phpKind = "php:7.3";
+
+ List<Map<String, Object>> nodeKindAnnotations = createActionAnnotation(false, false, false, false, nodeKind);
+ List<Map<String, Object>> rubyKindAnnotations = createActionAnnotation(false, false, false, false, rubyKind);
+ List<Map<String, Object>> phpKindAnnotations = createActionAnnotation(false, false, false, false, phpKind);
+
+ WhiskActionMetaData nodeAction = new WhiskActionMetaData(name, namespace, version, updated, publish, nodeKindAnnotations, limits, exec);
+ WhiskActionMetaData rubyAction = new WhiskActionMetaData(name, namespace, version, updated, publish, rubyKindAnnotations, limits, exec);
+ WhiskActionMetaData phpAction = new WhiskActionMetaData(name, namespace, version, updated, publish, phpKindAnnotations, limits, exec);
+
+ assertEquals(expectedNodeIcon, nodeAction.getKindIcon());
+ assertEquals(expectedRubyIcon, rubyAction.getKindIcon());
+ assertEquals(expectedPhpIcon, phpAction.getKindIcon());
+ }
+
+
+ @Test
+ public void testIsSequenceAction() {
+ List<Map<String, Object>> sequenceAnnotations = createActionAnnotation(false, false, false, false, "sequence");
+
+ List<Map<String, Object>> parameters = new ArrayList<>();
+
+ List<String> components = new ArrayList<>();
+ components.add("/testNs/testAction1");
+ components.add("/testNs/testAction2");
+ components.add("/testNs/testAction3");
+
+ CodeExec codeExec = new CodeExec(false, "sequence", null, null, null, components);
+
+ ExecutableWhiskAction sequenceAction = new ExecutableWhiskAction(name, namespace, version, updated, publish,
+ sequenceAnnotations, limits, codeExec, parameters);
+
+ assertTrue(sequenceAction.isSequenceAction());
+ }
+
+ @Test
+ public void testWhiskActionWebActionOptions() {
+ List<Map<String, Object>> webAnnotations = createActionAnnotation(true, false, false, false, "nodejs:12");
+ List<Map<String, Object>> rawHttpAnnotations = createActionAnnotation(true, true, false, false, "nodejs:12");
+ List<Map<String, Object>> customOptionsAnnotations = createActionAnnotation(true, true, true, false, "nodejs:12");
+ List<Map<String, Object>> finalAnnotations = createActionAnnotation(true, false, false, true, "nodejs:12");
+
+ WhiskActionMetaData webAction = new WhiskActionMetaData(name, namespace, version, updated, publish, webAnnotations, limits, exec);
+ WhiskActionMetaData rawHttpAction = new WhiskActionMetaData(name, namespace, version, updated, publish, rawHttpAnnotations, limits, exec);
+ WhiskActionMetaData customOptionsAction = new WhiskActionMetaData(name, namespace, version, updated, publish, customOptionsAnnotations, limits, exec);
+ WhiskActionMetaData finalAction = new WhiskActionMetaData(name, namespace, version, updated, publish, finalAnnotations, limits, exec);
+
+ assertTrue(webAction.isWebAction());
+
+ assertTrue(rawHttpAction.isWebAction());
+ assertTrue(rawHttpAction.isRawHttp());
+
+ assertTrue(customOptionsAction.isWebAction());
+ assertTrue(customOptionsAction.isRawHttp());
+
+ assertTrue(finalAction.isWebAction());
+ assertTrue(finalAction.isFinalDefaultParameter());
+ }
+
+ @Test
+ public void testWhiskActionMetaDataToString() {
+ String expected = name;
+
+ WhiskActionMetaData action = new WhiskActionMetaData(name, namespace, version, updated, publish, annotations, limits, exec);
+
+ assertEquals(expected, action.toString());
+ }
+
+ @Test
+ public void testWhiskActionMetaDataWithPackageToString() {
+ String expected = pkg + "/" + name;
+
+ WhiskActionMetaData actionWithPackage = new WhiskActionMetaData(name, namespace + "/" + pkg, version, updated, publish, annotations, limits, exec);
+
+ assertEquals(expected, actionWithPackage.toString());
+ }
+
+ @Test
+ public void testWhiskActionMetaDataToCombBoxEntityEntry() {
+ WhiskActionMetaData actionMetaData = new WhiskActionMetaData(name, namespace, version, updated, publish, annotations, limits, exec);
+
+ ComboBoxEntityEntry expectedEntry = new ComboBoxEntityEntry(actionMetaData.toString(), ComboBoxEntityType.ACTION);
+
+ assertEquals(expectedEntry, actionMetaData.toCombBoxEntityEntry());
+ }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActivationTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActivationTest.java
new file mode 100644
index 0000000..f57ce5e
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActivationTest.java
@@ -0,0 +1,124 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class WhiskActivationTest {
+ private String expectedActivationId = "969c2bf39e3b40599c2bf59e3b901911";
+ private String expectedName = "testName";
+ private String expectedNamespace = "testNamespace";
+ private String expectedVersion = "1.0.0";
+ private String expectedCause = "51adf3c3f875df3gadf3c0f875ef3eb9";
+ private long expectedStart = 1592555196109L;
+ private long expectedEnd = 1592555196153L;
+ private long expectedDuration = 50;
+ private boolean expectedPublish = false;
+
+ @Test
+ public void testWhiskActionMetaDataGetter() {
+
+ List<Map<String, Object>> expectedAnnotations = new ArrayList<>();
+ int expectedStatusCode = 0;
+
+ WhiskActivationMetaData activationMetaData = new WhiskActivationMetaData(
+ expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+ expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, expectedStatusCode);
+
+ assertEquals(expectedActivationId, activationMetaData.getActivationId());
+ assertEquals(expectedName, activationMetaData.getName());
+ assertEquals(expectedNamespace, activationMetaData.getNamespace());
+ assertEquals(expectedVersion, activationMetaData.getVersion());
+ assertEquals(expectedCause, activationMetaData.getCause());
+ assertEquals(expectedStart, activationMetaData.getStart());
+ assertEquals(expectedEnd, activationMetaData.getEnd());
+ assertEquals(expectedDuration, activationMetaData.getDuration());
+ assertEquals(expectedPublish, activationMetaData.isPublish());
+ assertEquals(expectedAnnotations, activationMetaData.getAnnotations());
+ assertEquals(expectedStatusCode, activationMetaData.getStatusCode());
+ }
+
+ @Test
+ public void testWhiskActionMetaDataGetStatus() {
+ List<Map<String, Object>> expectedAnnotations = new ArrayList<>();
+
+ int success = 0;
+ String expectedSuccessMessage0 = "success";
+ WhiskActivationMetaData activationMetaData0 = new WhiskActivationMetaData(
+ expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+ expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, success);
+ assertEquals(expectedSuccessMessage0, activationMetaData0.getStatus());
+
+
+ int applicationError = 1;
+ String expectedSuccessMessage1 = "application error";
+ WhiskActivationMetaData activationMetaData1 = new WhiskActivationMetaData(
+ expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+ expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, applicationError);
+ assertEquals(expectedSuccessMessage1, activationMetaData1.getStatus());
+
+ int developerError = 2;
+ String expectedSuccessMessage2 = "developer error";
+ WhiskActivationMetaData activationMetaData2 = new WhiskActivationMetaData(
+ expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+ expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, developerError);
+ assertEquals(expectedSuccessMessage2, activationMetaData2.getStatus());
+
+
+ int internalError = 3;
+ String expectedSuccessMessage3 = "internal error";
+ WhiskActivationMetaData activationMetaData3 = new WhiskActivationMetaData(
+ expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+ expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, internalError);
+ assertEquals(expectedSuccessMessage3, activationMetaData3.getStatus());
+ }
+
+ @Test
+ public void testWhiskActivationWithLogsGetter() {
+ List<Map<String, Object>> expectedAnnotations = new ArrayList<>();
+ String expectedSubject = "testNamespace";
+ List<String> expectedLogs = new ArrayList<>();
+ Map<String, Object> expectedResponse = new LinkedHashMap<>();
+
+ WhiskActivationWithLogs activationWithLogs = new WhiskActivationWithLogs(
+ expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+ expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, expectedSubject, expectedLogs, expectedResponse);
+
+ assertEquals(expectedActivationId, activationWithLogs.getActivationId());
+ assertEquals(expectedName, activationWithLogs.getName());
+ assertEquals(expectedNamespace, activationWithLogs.getNamespace());
+ assertEquals(expectedVersion, activationWithLogs.getVersion());
+ assertEquals(expectedCause, activationWithLogs.getCause());
+ assertEquals(expectedStart, activationWithLogs.getStart());
+ assertEquals(expectedEnd, activationWithLogs.getEnd());
+ assertEquals(expectedDuration, activationWithLogs.getDuration());
+ assertEquals(expectedPublish, activationWithLogs.isPublish());
+ assertEquals(expectedAnnotations, activationWithLogs.getAnnotations());
+ assertEquals(expectedSubject, activationWithLogs.getSubject());
+ assertEquals(expectedLogs, activationWithLogs.getLogs());
+ assertEquals(expectedResponse, activationWithLogs.getResponse());
+ }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthTest.java
new file mode 100644
index 0000000..b11f3fb
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthTest.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import org.junit.Test;
+
+import java.util.Base64;
+
+import static org.junit.Assert.assertEquals;
+
+public class WhiskAuthTest {
+
+ @Test
+ public void testGetAuth() {
+ String apiKey = "test-auth";
+ WhiskAuth auth = new WhiskAuth(apiKey, "test-host");
+
+ // It is seemingly meaningless but it would help to guarantee sanity when the underlying logic is changed.
+ assertEquals("Basic " + Base64.getEncoder().encodeToString(apiKey.getBytes()), auth.getBasicAuthHeader());
+ }
+
+ @Test
+ public void testGetApihost() {
+ String expected1 = "https://test-host";
+ String expected2 = "http://test-host";
+ WhiskAuth auth1 = new WhiskAuth("test-auth", "test-host");
+ WhiskAuth auth2 = new WhiskAuth("test-auth", "http://test-host");
+
+ // It is seemingly meaningless but it would help to guarantee sanity when the underlying logic is changed.
+ assertEquals(expected1, auth1.getApihost());
+ assertEquals(expected2, auth2.getApihost());
+ }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpointTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpointTest.java
new file mode 100644
index 0000000..afd5ee8
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpointTest.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertEquals;
+
+public class WhiskEndpointTest {
+
+ @Test
+ public void testWhiskEndpointWithConstructorGetApihost() {
+ String expected1 = "https://foo.com";
+ String expected2 = "http://foo.com";
+
+ WhiskEndpoint auth1 = new WhiskEndpoint("test-auth", "https://foo.com", new ArrayList<>());
+ WhiskEndpoint auth2 = new WhiskEndpoint("test-auth", "foo.com", new ArrayList<>());
+ WhiskEndpoint auth3 = new WhiskEndpoint("test-auth", "http://foo.com", new ArrayList<>());
+
+
+ assertEquals(expected1, auth1.getApihost());
+ assertEquals(expected1, auth2.getApihost());
+ assertEquals(expected2, auth3.getApihost());
+ }
+
+ @Test
+ public void testWhiskEndpointWithSetMethodGetApihost() {
+ String expected1 = "http://foo.com";
+ String expected2 = "https://bar.com";
+ String expected3 = "ftp://foo.com";
+
+ WhiskEndpoint auth1 = new WhiskEndpoint("test-auth", "test.com", new ArrayList<>());
+ WhiskEndpoint auth2 = new WhiskEndpoint("test-auth", "test.com", new ArrayList<>());
+ WhiskEndpoint auth3 = new WhiskEndpoint("test-auth", "test.com", new ArrayList<>());
+
+ auth1.setApihost("http://foo.com");
+ auth2.setApihost("bar.com");
+ auth3.setApihost("ftp://foo.com"); // the regular expression does not check the protocol itself but format.
+
+ assertEquals(expected1, auth1.getApihost());
+ assertEquals(expected2, auth2.getApihost());
+ assertEquals(expected3, auth3.getApihost());
+ }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageJsonTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageJsonTest.java
new file mode 100644
index 0000000..b223c97
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageJsonTest.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.*;
+import static com.navercorp.openwhisk.intellij.utils.FileHelper.readFile;
+import static junit.framework.Assert.assertEquals;
+
+public class WhiskPackageJsonTest {
+
+ @Test
+ public void parseWhiskPackages() throws IOException {
+ // given
+ String packages = readFile("packages.json");
+
+ List<WhiskPackage> expected = new ArrayList<>();
+ expected.add(new WhiskPackage("pkg1", "ns", false, 1586513535028L, "0.0.1", createPackageAnnotation("test", null), false));
+ expected.add(new WhiskPackage("pkg2", "ns", false, 1586513513922L, "0.0.1", createPackageAnnotation("test", null), false));
+ Map<String, String> binding = createBinding("sharedpackage", "whisk.system");
+ expected.add(new WhiskPackage("pkg3", "ns", false, 1557380799999L, "0.0.1", createPackageAnnotation("test", binding), binding));
+
+ // when
+ List<WhiskPackage> actual = JsonParserUtils.parseWhiskPackages(packages);
+
+ // then
+ assertEquals(expected, actual);
+ }
+
+
+ @Test
+ public void parseWhiskPackage() throws IOException {
+ // given
+ String pkg = readFile("package.json");
+
+ List<Map<String, Object>> parameters = new ArrayList<>();
+ Map<String, Object> param1 = new LinkedHashMap<>();
+ param1.put("key", "test");
+ param1.put("value", "test");
+ parameters.add(param1);
+
+ Map<String, Object> param2 = new LinkedHashMap<>();
+ param2.put("key", "test1");
+ param2.put("value", "test1");
+ parameters.add(param2);
+
+ List<CompactWhiskAction> actions = new ArrayList<>();
+ actions.add(new CompactWhiskAction("action1", "0.0.2", createActionAnnotation(false, false, false, false, "nodejs:10")));
+ actions.add(new CompactWhiskAction("action2", "0.0.2", createActionAnnotation(false, false, false, false, "nodejs:10")));
+ actions.add(new CompactWhiskAction("action3", "0.0.2", createActionAnnotation(false, false, false, false, "nodejs:10")));
+ WhiskPackageWithActions expected = new WhiskPackageWithActions("test", "testns", true, 1583828352890L, "0.0.6",
+ createPackageAnnotation("test", null), createEmptyBinding(), parameters, actions, new ArrayList<>());
+
+ // when
+ WhiskPackageWithActions actual = JsonParserUtils.parseWhiskPackage(pkg).get();
+
+ // then
+ assertEquals(expected, actual);
+ }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageTest.java
new file mode 100644
index 0000000..68230e1
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageTest.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.*;
+import static org.junit.Assert.*;
+
+public class WhiskPackageTest {
+ private String name = "testPkg";
+ private String namespace = "testNamespace";
+ private String version = "1.0.0";
+ private long updated = 1581316522182L;
+ private boolean publish = false;
+
+ @Test
+ public void testWhiskPackageGetBinding() {
+ List<Map<String, Object>> annotations = createPackageAnnotation("test-description", null);
+ WhiskPackage notBoundPackage = new WhiskPackage(name, namespace, publish, updated, version, annotations, createFalseBinding());
+
+ assertFalse(notBoundPackage.getBinding().isPresent());
+ }
+
+ @Test
+ public void testBoundWhiskPackageGetBinding() {
+ Binding binding = new Binding("boundPkg", "boundNamespace");
+
+ Map<String, String> bindingMap = createBinding(binding.getName(), binding.getNamespace());
+ List<Map<String, Object>> annotations = createPackageAnnotation("test-description", bindingMap);
+ WhiskPackage boundPackage = new WhiskPackage(name, namespace, publish, updated, version, annotations, bindingMap);
+
+ assertTrue(boundPackage.getBinding().isPresent());
+ assertEquals(binding, Optional.of(binding).get());
+ }
+
+
+ @Test
+ public void testWhiskPackageWithActionsGetBinding() {
+ List<Map<String, Object>> parameters = new ArrayList<>();
+ List<CompactWhiskAction> actions = new ArrayList<>();
+ List<Object> feeds = new ArrayList<>();
+
+ List<Map<String, Object>> annotations = createPackageAnnotation("test-description", null);
+ WhiskPackageWithActions notBoundPackage = new WhiskPackageWithActions(name, namespace, publish, updated, version, annotations,
+ createEmptyBinding(), parameters, actions, feeds);
+
+ assertFalse(notBoundPackage.getBinding().isPresent());
+ }
+
+ @Test
+ public void testBoundWhiskPackageWithActionsGetBinding() {
+ List<Map<String, Object>> parameters = new ArrayList<>();
+ List<CompactWhiskAction> actions = new ArrayList<>();
+ List<Object> feeds = new ArrayList<>();
+
+ Binding binding = new Binding("boundPkg", "boundNamespace");
+
+ Map<String, String> bindingMap = createBinding(binding.getName(), binding.getNamespace());
+ List<Map<String, Object>> annotations = createPackageAnnotation("test-description", bindingMap);
+ WhiskPackageWithActions boundPackage = new WhiskPackageWithActions(name, namespace, publish, updated, version,
+ annotations, bindingMap, parameters, actions, feeds);
+
+ assertTrue(boundPackage.getBinding().isPresent());
+ assertEquals(binding, Optional.of(binding).get());
+ }
+
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskTriggerJsonTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskTriggerJsonTest.java
new file mode 100644
index 0000000..49a0301
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskTriggerJsonTest.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.createRule;
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.createTriggerAlarmFeedAnnotation;
+import static com.navercorp.openwhisk.intellij.utils.FileHelper.readFile;
+import static junit.framework.Assert.assertEquals;
+
+public class WhiskTriggerJsonTest {
+
+ @Test
+ public void parseWhiskTriggers() throws IOException {
+ // given
+ String triggers = readFile("triggers.json");
+
+ List<WhiskTriggerMetaData> expected = new ArrayList<>();
+ expected.add(new WhiskTriggerMetaData("trigger1", "test", "0.0.2", 1586315510331L, false, createTriggerAlarmFeedAnnotation()));
+ expected.add(new WhiskTriggerMetaData("trigger2", "test", "0.0.2", 1586315245020L, false, createTriggerAlarmFeedAnnotation()));
+ expected.add(new WhiskTriggerMetaData("trigger3", "test", "0.0.2", 1582612773239L, false, new ArrayList<>()));
+
+ // when
+ List<WhiskTriggerMetaData> actual = JsonParserUtils.parseWhiskTriggers(triggers);
+
+ // then
+ assertEquals(actual, expected);
+ }
+
+ @Test
+ public void parseWhiskTrigger() throws IOException {
+ // given
+ String trigger = readFile("trigger.json");
+
+ ExecutableWhiskTrigger expected = new ExecutableWhiskTrigger(
+ "trigger1",
+ "test",
+ "0.0.2",
+ 1586315245020L,
+ false,
+ createTriggerAlarmFeedAnnotation(),
+ new ArrayList<>(),
+ createRule("rule1", "test", "action1"),
+ new LinkedHashMap<>());
+
+ // when
+ ExecutableWhiskTrigger actual = JsonParserUtils.parseWhiskTrigger(trigger).get();
+
+ // then
+ assertEquals(actual, expected);
+ }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/utils/AnnotationHelper.java b/src/test/java/com/navercorp/openwhisk/intellij/utils/AnnotationHelper.java
new file mode 100644
index 0000000..ebd0133
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/utils/AnnotationHelper.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.utils;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.SimplifiedEntityMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.SimplifiedWhiskRule;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class AnnotationHelper {
+
+ protected AnnotationHelper() {
+ throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+ }
+
+ public static List<Map<String, Object>> createActionAnnotation(boolean webExport, boolean rawHttp, boolean customOption, boolean finalEntry, String kind) {
+ List<Map<String, Object>> annotations = new ArrayList<>();
+ if (webExport) {
+ Map<String, Object> entry1 = new LinkedHashMap<>();
+ entry1.put("key", "web-export");
+ entry1.put("value", webExport);
+ annotations.add(entry1);
+
+ Map<String, Object> entry2 = new LinkedHashMap<>();
+ entry2.put("key", "raw-http");
+ entry2.put("value", rawHttp);
+ annotations.add(entry2);
+
+ if (customOption) {
+ Map<String, Object> entry3 = new LinkedHashMap<>();
+ entry3.put("key", "web-custom-options");
+ entry3.put("value", customOption);
+ annotations.add(entry3);
+ }
+
+ Map<String, Object> entry4 = new LinkedHashMap<>();
+ entry4.put("key", "final");
+ entry4.put("value", finalEntry);
+ annotations.add(entry4);
+ }
+
+ Map<String, Object> entry5 = new LinkedHashMap<>();
+ entry5.put("key", "exec");
+ entry5.put("value", kind);
+ annotations.add(entry5);
+
+ return annotations;
+ }
+
+ public static List<Map<String, Object>> createPackageAnnotation(String description, Map<String, String> binding) {
+ List<Map<String, Object>> annotations = new ArrayList<>();
+
+ Map<String, Object> entry1 = new LinkedHashMap<>();
+ entry1.put("key", "description");
+ entry1.put("value", description);
+ annotations.add(entry1);
+
+ if (binding != null) {
+ Map<String, Object> entry2 = new LinkedHashMap<>();
+ entry2.put("key", "binding");
+ entry2.put("value", binding);
+ annotations.add(entry2);
+ }
+
+ return annotations;
+ }
+
+ public static Map<String, String> createBinding(String name, String namespace) {
+ Map<String, String> binding = new LinkedHashMap<>();
+ binding.put("name", name);
+ binding.put("namespace", namespace);
+ return binding;
+ }
+
+ public static Map<String, String> createEmptyBinding() {
+ return new LinkedHashMap<>();
+ }
+
+ public static boolean createFalseBinding() {
+ return false;
+ }
+
+ public static List<Map<String, Object>> createTriggerAlarmFeedAnnotation() {
+ List<Map<String, Object>> annotations = new ArrayList<>();
+
+ Map<String, Object> entry = new LinkedHashMap<>();
+ entry.put("key", "feed");
+ entry.put("value", "/whisk.system/alarms/alarm");
+ annotations.add(entry);
+ return annotations;
+ }
+
+ public static Map<String, SimplifiedWhiskRule> createRule(String ruleName, String namespace, String actionName) {
+ Map<String, SimplifiedWhiskRule> rules = new LinkedHashMap<>();
+ rules.put(namespace + "/" + ruleName, new SimplifiedWhiskRule(new SimplifiedEntityMetaData(namespace, actionName), "active"));
+ return rules;
+ }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/utils/FileHelper.java b/src/test/java/com/navercorp/openwhisk/intellij/utils/FileHelper.java
new file mode 100644
index 0000000..c8c8f5e
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/utils/FileHelper.java
@@ -0,0 +1,22 @@
+package com.navercorp.openwhisk.intellij.utils;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.stream.Collectors;
+
+public class FileHelper {
+
+ protected FileHelper() {
+ throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+ }
+
+ public static String readFile(String filePath) {
+ InputStream inputStream = FileHelper.class.getClassLoader().getResourceAsStream(filePath);
+ return new BufferedReader(
+ new InputStreamReader(inputStream, StandardCharsets.UTF_8))
+ .lines()
+ .collect(Collectors.joining("\n"));
+ }
+}
diff --git a/src/test/resources/action.json b/src/test/resources/action.json
new file mode 100644
index 0000000..10c0886
--- /dev/null
+++ b/src/test/resources/action.json
@@ -0,0 +1,34 @@
+{
+ "annotations": [
+ {
+ "key": "exec",
+ "value": "nodejs:12"
+ }
+ ],
+ "exec": {
+ "kind": "nodejs:12",
+ "code": "function main(params) {}",
+ "binary": false
+ },
+ "limits": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 60000
+ },
+ "name": "testAct",
+ "namespace": "testNs/testPkg",
+ "parameters": [
+ {
+ "key": "name",
+ "value": "test"
+ },
+ {
+ "key": "count",
+ "value": 1
+ }
+ ],
+ "publish": false,
+ "updated": 1581316522182,
+ "version": "0.0.1"
+}
diff --git a/src/test/resources/actions.json b/src/test/resources/actions.json
new file mode 100644
index 0000000..a37da9f
--- /dev/null
+++ b/src/test/resources/actions.json
@@ -0,0 +1,148 @@
+[
+ {
+ "annotations": [
+ {
+ "key": "web-export",
+ "value": true
+ },
+ {
+ "key": "raw-http",
+ "value": false
+ },
+ {
+ "key": "final",
+ "value": true
+ },
+ {
+ "key": "exec",
+ "value": "nodejs:12"
+ }
+ ],
+ "exec": {
+ "binary": true
+ },
+ "limits": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 60000
+ },
+ "name": "testAct1",
+ "namespace": "testNs/testPkg1",
+ "publish": false,
+ "updated": 1585756253499,
+ "version": "0.0.1"
+ },
+ {
+ "annotations": [
+ {
+ "key": "web-export",
+ "value": true
+ },
+ {
+ "key": "raw-http",
+ "value": false
+ },
+ {
+ "key": "final",
+ "value": true
+ },
+ {
+ "key": "exec",
+ "value": "nodejs:12"
+ }
+ ],
+ "exec": {
+ "binary": true
+ },
+ "limits": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 60000
+ },
+ "name": "testAct2",
+ "namespace": "testNs/testPkg2",
+ "publish": false,
+ "updated": 1585756252393,
+ "version": "0.0.1"
+ },
+ {
+ "annotations": [
+ {
+ "key": "web-export",
+ "value": true
+ },
+ {
+ "key": "raw-http",
+ "value": false
+ },
+ {
+ "key": "final",
+ "value": true
+ },
+ {
+ "key": "exec",
+ "value": "nodejs:12"
+ }
+ ],
+ "exec": {
+ "binary": true
+ },
+ "limits": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 60000
+ },
+ "name": "testAct3",
+ "namespace": "testNs/testPkg3",
+ "publish": false,
+ "updated": 1585756251289,
+ "version": "0.0.1"
+ },
+ {
+ "annotations": [
+ {
+ "key": "exec",
+ "value": "blackbox"
+ }
+ ],
+ "exec": {
+ "binary": false
+ },
+ "limits": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 60000
+ },
+ "name": "testAct4",
+ "namespace": "testNs/testPkg4",
+ "publish": false,
+ "updated": 1582709807073,
+ "version": "0.0.2"
+ },
+ {
+ "annotations": [
+ {
+ "key": "exec",
+ "value": "python:3"
+ }
+ ],
+ "exec": {
+ "binary": false
+ },
+ "limits": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 60000
+ },
+ "name": "testAct5",
+ "namespace": "testNs/testPkg5",
+ "publish": false,
+ "updated": 1581316522182,
+ "version": "0.0.1"
+ }
+]
diff --git a/src/test/resources/activation_with_log.json b/src/test/resources/activation_with_log.json
new file mode 100644
index 0000000..f598265
--- /dev/null
+++ b/src/test/resources/activation_with_log.json
@@ -0,0 +1,55 @@
+{
+ "activationId": "26760d5e868f4223b60d5e868f02233c",
+ "annotations": [
+ {
+ "key": "path",
+ "value": "ns1/action1"
+ },
+ {
+ "key": "waitTime",
+ "value": 468
+ },
+ {
+ "key": "kind",
+ "value": "nodejs:6"
+ },
+ {
+ "key": "timeout",
+ "value": false
+ },
+ {
+ "key": "limits",
+ "value": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 60000
+ }
+ },
+ {
+ "key": "initTime",
+ "value": 241
+ }
+ ],
+ "duration": 253,
+ "end": 1591082866561,
+ "logs": [],
+ "name": "action1",
+ "namespace": "ns1",
+ "publish": false,
+ "response": {
+ "result": {
+ "payload": "Hello",
+ "test": {
+ "test1": "test1",
+ "test2": "test2"
+ }
+ },
+ "size": 72,
+ "status": "success",
+ "success": true
+ },
+ "start": 1591082866308,
+ "subject": "ns1",
+ "version": "0.0.10"
+}
diff --git a/src/test/resources/activations.json b/src/test/resources/activations.json
new file mode 100644
index 0000000..95dda46
--- /dev/null
+++ b/src/test/resources/activations.json
@@ -0,0 +1,86 @@
+[
+ {
+ "activationId": "969c2bf59e3b40199c2bf59e3b901940",
+ "annotations": [
+ {
+ "key": "path",
+ "value": "ns1/action1"
+ },
+ {
+ "key": "waitTime",
+ "value": 56
+ },
+ {
+ "key": "kind",
+ "value": "nodejs:10"
+ },
+ {
+ "key": "timeout",
+ "value": false
+ },
+ {
+ "key": "limits",
+ "value": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 50000
+ }
+ },
+ {
+ "key": "initTime",
+ "value": 37
+ }
+ ],
+ "duration": 44,
+ "end": 1592555196153,
+ "name": "action1",
+ "namespace": "ns1",
+ "publish": false,
+ "start": 1592555196109,
+ "statusCode": 0,
+ "version": "0.0.8"
+ },
+ {
+ "activationId": "51adf3c0f8754f3eadf3c0f875ef3eb9",
+ "annotations": [
+ {
+ "key": "path",
+ "value": "ns1/action1"
+ },
+ {
+ "key": "waitTime",
+ "value": 61
+ },
+ {
+ "key": "kind",
+ "value": "nodejs:10"
+ },
+ {
+ "key": "timeout",
+ "value": false
+ },
+ {
+ "key": "limits",
+ "value": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 50000
+ }
+ },
+ {
+ "key": "initTime",
+ "value": 23
+ }
+ ],
+ "duration": 28,
+ "end": 1592555184779,
+ "name": "action1",
+ "namespace": "ns1",
+ "publish": false,
+ "start": 1592555184751,
+ "statusCode": 0,
+ "version": "0.0.7"
+ }
+]
\ No newline at end of file
diff --git a/src/test/resources/binary-action.json b/src/test/resources/binary-action.json
new file mode 100644
index 0000000..87cc4c8
--- /dev/null
+++ b/src/test/resources/binary-action.json
@@ -0,0 +1,34 @@
+{
+ "annotations": [
+ {
+ "key": "exec",
+ "value": "nodejs:12"
+ }
+ ],
+ "exec": {
+ "kind": "nodejs:12",
+ "code": "aaaaa",
+ "binary": true
+ },
+ "limits": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 60000
+ },
+ "name": "testAct",
+ "namespace": "testNs/testPkg",
+ "parameters": [
+ {
+ "key": "name",
+ "value": "test"
+ },
+ {
+ "key": "count",
+ "value": 1
+ }
+ ],
+ "publish": false,
+ "updated": 1585756253499,
+ "version": "0.0.1"
+}
\ No newline at end of file
diff --git a/src/test/resources/blackbox-action.json b/src/test/resources/blackbox-action.json
new file mode 100644
index 0000000..a545b8e
--- /dev/null
+++ b/src/test/resources/blackbox-action.json
@@ -0,0 +1,34 @@
+{
+ "annotations": [
+ {
+ "key": "exec",
+ "value": "blackbox"
+ }
+ ],
+ "exec": {
+ "kind": "blackbox",
+ "image": "imageRepo/imageName",
+ "binary": true
+ },
+ "limits": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 60000
+ },
+ "name": "testAct",
+ "namespace": "testNs/testPkg",
+ "parameters": [
+ {
+ "key": "name",
+ "value": "test"
+ },
+ {
+ "key": "count",
+ "value": 1
+ }
+ ],
+ "publish": false,
+ "updated": 1585756253499,
+ "version": "0.0.1"
+}
\ No newline at end of file
diff --git a/src/test/resources/java-action.json b/src/test/resources/java-action.json
new file mode 100644
index 0000000..3e4e939
--- /dev/null
+++ b/src/test/resources/java-action.json
@@ -0,0 +1,35 @@
+{
+ "annotations": [
+ {
+ "key": "exec",
+ "value": "java"
+ }
+ ],
+ "exec": {
+ "kind": "java",
+ "main": "test",
+ "binary": true,
+ "code": "aaaaa"
+ },
+ "limits": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 60000
+ },
+ "name": "testAct",
+ "namespace": "testNs/testPkg",
+ "parameters": [
+ {
+ "key": "name",
+ "value": "test"
+ },
+ {
+ "key": "count",
+ "value": 1
+ }
+ ],
+ "publish": false,
+ "updated": 1585756253499,
+ "version": "0.0.1"
+}
\ No newline at end of file
diff --git a/src/test/resources/package.json b/src/test/resources/package.json
new file mode 100644
index 0000000..edd7b10
--- /dev/null
+++ b/src/test/resources/package.json
@@ -0,0 +1,57 @@
+{
+ "namespace": "testns",
+ "name": "test",
+ "version": "0.0.6",
+ "updated": 1583828352890,
+ "publish": true,
+ "annotations": [
+ {
+ "key": "description",
+ "value": "test"
+ }
+ ],
+ "parameters": [
+ {
+ "key": "test",
+ "value": "test"
+ },
+ {
+ "key": "test1",
+ "value": "test1"
+ }
+ ],
+ "binding": {},
+ "feeds": [],
+ "actions": [
+ {
+ "name": "action1",
+ "version": "0.0.2",
+ "annotations": [
+ {
+ "key": "exec",
+ "value": "nodejs:10"
+ }
+ ]
+ },
+ {
+ "name": "action2",
+ "version": "0.0.2",
+ "annotations": [
+ {
+ "key": "exec",
+ "value": "nodejs:10"
+ }
+ ]
+ },
+ {
+ "name": "action3",
+ "version": "0.0.2",
+ "annotations": [
+ {
+ "key": "exec",
+ "value": "nodejs:10"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/test/resources/packages.json b/src/test/resources/packages.json
new file mode 100644
index 0000000..8142167
--- /dev/null
+++ b/src/test/resources/packages.json
@@ -0,0 +1,54 @@
+[
+ {
+ "annotations": [
+ {
+ "key": "description",
+ "value": "test"
+ }
+ ],
+ "binding": false,
+ "name": "pkg1",
+ "namespace": "ns",
+ "publish": false,
+ "updated": 1586513535028,
+ "version": "0.0.1"
+ },
+ {
+ "annotations": [
+ {
+ "key": "description",
+ "value": "test"
+ }
+ ],
+ "binding": false,
+ "name": "pkg2",
+ "namespace": "ns",
+ "publish": false,
+ "updated": 1586513513922,
+ "version": "0.0.1"
+ },
+ {
+ "annotations": [
+ {
+ "key": "description",
+ "value": "test"
+ },
+ {
+ "key": "binding",
+ "value": {
+ "name": "sharedpackage",
+ "namespace": "whisk.system"
+ }
+ }
+ ],
+ "binding": {
+ "name": "sharedpackage",
+ "namespace": "whisk.system"
+ },
+ "name": "pkg3",
+ "namespace": "ns",
+ "publish": false,
+ "updated": 1557380799999,
+ "version": "0.0.1"
+ }
+]
diff --git a/src/test/resources/sequence-action.json b/src/test/resources/sequence-action.json
new file mode 100644
index 0000000..7fd66d9
--- /dev/null
+++ b/src/test/resources/sequence-action.json
@@ -0,0 +1,35 @@
+{
+ "annotations": [
+ {
+ "key": "exec",
+ "value": "sequence"
+ }
+ ],
+ "exec": {
+ "kind": "sequence",
+ "components": [
+ "/testNs/testAct1",
+ "/testNs/testAct2"
+ ]
+ },
+ "limits": {
+ "concurrency": 1,
+ "logs": 1,
+ "memory": 256,
+ "timeout": 60000
+ },
+ "name": "testAct",
+ "namespace": "testNs/testPkg",
+ "parameters": [
+ {
+ "key": "_actions",
+ "components": [
+ "/testNs/testAct1",
+ "/testNs/testAct2"
+ ]
+ }
+ ],
+ "publish": false,
+ "updated": 1585756253499,
+ "version": "0.0.1"
+}
\ No newline at end of file
diff --git a/src/test/resources/trigger.json b/src/test/resources/trigger.json
new file mode 100644
index 0000000..9efe83f
--- /dev/null
+++ b/src/test/resources/trigger.json
@@ -0,0 +1,24 @@
+{
+ "annotations": [
+ {
+ "key": "feed",
+ "value": "/whisk.system/alarms/alarm"
+ }
+ ],
+ "limits": {},
+ "name": "trigger1",
+ "namespace": "test",
+ "parameters": [],
+ "publish": false,
+ "rules": {
+ "test/rule1": {
+ "action": {
+ "name": "action1",
+ "path": "test"
+ },
+ "status": "active"
+ }
+ },
+ "updated": 1586315245020,
+ "version": "0.0.2"
+}
\ No newline at end of file
diff --git a/src/test/resources/triggers.json b/src/test/resources/triggers.json
new file mode 100644
index 0000000..6860863
--- /dev/null
+++ b/src/test/resources/triggers.json
@@ -0,0 +1,36 @@
+[
+ {
+ "annotations": [
+ {
+ "key": "feed",
+ "value": "/whisk.system/alarms/alarm"
+ }
+ ],
+ "name": "trigger1",
+ "namespace": "test",
+ "publish": false,
+ "updated": 1586315510331,
+ "version": "0.0.2"
+ },
+ {
+ "annotations": [
+ {
+ "key": "feed",
+ "value": "/whisk.system/alarms/alarm"
+ }
+ ],
+ "name": "trigger2",
+ "namespace": "test",
+ "publish": false,
+ "updated": 1586315245020,
+ "version": "0.0.2"
+ },
+ {
+ "annotations": [],
+ "name": "trigger3",
+ "namespace": "test",
+ "publish": false,
+ "updated": 1582612773239,
+ "version": "0.0.2"
+ }
+]
\ No newline at end of file