diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 903fb5a3..21119908 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,8 @@ "version": "none", "installMaven": "true", "installGradle": "false" - } + }, + "ghcr.io/devcontainers/features/github-cli:1": {} } } diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index f54929bc..2745430e 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -10,6 +10,7 @@ on: permissions: contents: write + pull-requests: write jobs: @@ -17,6 +18,7 @@ jobs: runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, 'skip ci')" steps: + - name: Checkout code uses: actions/checkout@v3 @@ -35,6 +37,7 @@ jobs: if: "!contains(github.event.head_commit.message, 'skip ci') && github.ref == 'refs/heads/main'" runs-on: ubuntu-latest steps: + - name: Checkout code uses: actions/checkout@v3 @@ -62,4 +65,67 @@ jobs: - name: Release env: GITHUB_TOKEN: ${{ secrets.NEFARIOUS_GITHUB_TOKEN }} - run: chmod +x ./scripts/prepare-release.sh && npx semantic-release \ No newline at end of file + run: chmod +x ./scripts/prepare-release.sh && npx semantic-release + + update-website: + needs: release + if: "!contains(github.event.head_commit.message, 'skip ci') && github.ref == 'refs/heads/main'" + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.NEFARIOUS_GITHUB_TOKEN }} + steps: + + - name: Checkout code + uses: actions/checkout@v3 + + - name: Get the latest release tag + id: get_release + run: | + LATEST_TAG=$(gh release view --json tagName -q ".tagName") + echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV + + - name: Configure Git + run: | + git config --global user.name "nefarious-developer-bot" + git config --global user.email "nefarious-developer-bot@users.noreply.github.com" + + - name: Clone the target repository + run: | + git clone https://nefarious-developer-bot:${GH_TOKEN}@github.com/The-Nefarious-Developer/zjoule-website.git website-repo + + - name: Create a new branch + working-directory: website-repo + run: | + git checkout -b update-to-$LATEST_TAG + + - name: Run the script to update the website + working-directory: website-repo + run: | + chmod +x ../scripts/update-website.sh + ../scripts/update-website.sh $LATEST_TAG + + - name: Commit the changes + working-directory: website-repo + run: | + git add . + git commit -m "feat: update to $LATEST_TAG" + + - name: Push the changes + working-directory: website-repo + run: | + git push https://nefarious-developer-bot:${GH_TOKEN}@github.com/The-Nefarious-Developer/zjoule-website.git update-to-$LATEST_TAG + + - name: Create a pull request + working-directory: website-repo + run: | + gh pr create \ + --title "Update to $LATEST_TAG" \ + --body "This PR updates the website to the latest release $LATEST_TAG." \ + --head update-to-$LATEST_TAG \ + --base main + + - name: Automatically merge the pull request + working-directory: website-repo + run: | + PR_URL=$(gh pr view update-to-$LATEST_TAG --json url -q ".url") + gh pr merge $PR_URL --squash --admin --delete-branch \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7507c104..57119875 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store .scannerwork -docs/ \ No newline at end of file +docs/ +website-repo/ \ No newline at end of file diff --git a/.project b/.project index 2ff476d5..c445b61a 100644 --- a/.project +++ b/.project @@ -14,4 +14,15 @@ org.eclipse.m2e.core.maven2Nature + + + 1737318973879 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/README.md b/README.md index 16e80571..3cf7205c 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,14 @@ [![Latest Test](https://github.com/The-Nefarious-Developer/zjoule/actions/workflows/main.yaml/badge.svg)](https://github.com/The-Nefarious-Developer/zjoule/actions/workflows/main.yaml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=The-Nefarious-Developer_zjoule&metric=alert_status&token=e2a58dc0706342532d8d14ae0badfe72fd08ff28)](https://sonarcloud.io/summary/new_code?id=The-Nefarious-Developer_zjoule) +[![CodeQL](https://github.com/The-Nefarious-Developer/zjoule/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/The-Nefarious-Developer/zjoule/actions/workflows/github-code-scanning/codeql) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Javadoc](https://img.shields.io/badge/JavaDoc-Online-green)](https://zjoule.com/v1.1.2/doc/) [![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release) -**zJoule** is an Open Source AI assistant plugin for Eclipse designed to integrate the **SAP AI Core** Generative AI Hub foundation-models into your development environment. +**zJoule** is an Open Source AI assistant plugin for Eclipse designed to integrate Generative AI into your development environment. ## Documentation Content - [Requirements](#requirements) @@ -25,12 +26,25 @@ - [License](#license) ## Requirements -- SAP AI Core with the `Extended` service plan (for the Generative AI Hub enablement). +- A local **Ollama** installation or **SAP AI Core** with the `Extended` service plan (for the Generative AI Hub enablement). - Eclipse 2024-09 (4.33.0) or latest. *Compatibility with older versions is not guaranteed.* ## Compatibility -The following table outlines the Generative AI models currently compatible with this plugin, as well as the next models that will soon be available: +The following table outlines the Generative AI models currently compatible with this plugin, as well as the next models that will soon be available. + +> **Privacy Information**
+We know how privacy is important and ensure that all communication handled by the plugin is secure **is a must**. Here's how your data is managed: +> - **Data Flow:** All communication facilitated by the plugin occurs exclusively between Eclipse and the SAP AI Core model APIs. No external intermediaries are involved. +> - **Data Storage:** Any data processed or generated during plugin usage is stored locally within Eclipse's memory mechanism, ensuring it remains private and confined to your development environment.
+> +> By keeping all operations local and secure, the plugin should provide a trustworthy and seamless experience. + +### Ollama + +This plugin supports Ollama models through a local installation. For more information regarding the models available, please, check [this link](). + +### SAP AI Core Generative AI Hub | Executable ID | Model | Version | Compatibility | |---------------|--------------------------------|---------------------------|:------------------:| @@ -52,16 +66,9 @@ LLM models that are not compatible may be selected during the login process; how Additional model compatibilities may be introduced in the near future :) -> **Privacy Information**
-We know how privacy is important and ensure that all communication handled by the plugin is secure **is a must**. Here's how your data is managed: -> - **Data Flow:** All communication facilitated by the plugin occurs exclusively between Eclipse and the SAP AI Core model APIs. No external intermediaries are involved. -> - **Data Storage:** Any data processed or generated during plugin usage is stored locally within Eclipse's memory mechanism, ensuring it remains private and confined to your development environment.
-> -> By keeping all operations local and secure, the plugin should provide a trustworthy and seamless experience. - ## Motivation -The official Joule version for ADT is being rolled out exclusively for S/4HANA Private Cloud and SAP BTP, ABAP Environment at the end of Q1 of 2025. But don’t worry, this plugin was created to bridges the gap! It introduces an AI-powered chat interface that seamlessly connects other types of SAP systems to large language models (LLMs) through SAP AI Core. +The official Joule version for ADT is being rolled out exclusively for S/4HANA Private Cloud and SAP BTP, ABAP Environment at the end of Q1 of 2025. But don’t worry, this plugin was created to bridges the gap! It introduces an AI-powered chat interface that seamlessly connects other types of SAP systems to large language models (LLMs) through different AI providers. The idea is to open the door to integrate SAP ABAP development with new models and innovative features that could emerge from the expanding possibilities of AI. @@ -88,25 +95,54 @@ All from the cozy confines of your Eclipse ADT =) #### 7. Enhanced Development Experience: - Enable faster learning for beginners or new team members. - Provide a smoother workflow for experienced developers needing quick solutions. +#### 8. Privacy and Security: +- If using a local Ollama installation, all data processing occurs entirely on your machine, without any internet dependency. +- This ensures maximum privacy, as sensitive code or project details never leave your local environment. ## Getting Started 1. Download and install **zJoule** in your Eclipse instance following the procedure described [right here](https://zjoule.com). +2. If the plugin view is not currently open in your Eclipse Editor, go to `Window > Show View > Other..` and search for **zJoule** inside the **ABAP Copilot** folder. + +
+ Intro GIF +
+ +3. Click on the authentication button (lock icon) at the top of the view and select the AI provider you want to work with. + +
+ Intro GIF +
+ +### Connecting to an Ollama local installation +4. Enter the localhost and port where your Ollama instance is available. -2. Login using your SAP AI Core `Service Key`. +
+ Intro GIF +
+ +5. Select the model you have installed and want to work with.
- Intro GIF + Intro GIF +
+ +### Connecting to an SAP AI Core instance + +4. Login using your SAP AI Core `Service Key`. + +
+ Intro GIF
You can find it in the *Instances an Subscriptions* area of your SAP BTP subaccount. -3. Select the `resource group` and the `deployment` model you want to use. +5. Select the `resource group` and the `deployment` model you want to use.
- Intro GIF + Intro GIF
Resource Groups are essentially a project workspace in the context of SAP AI Core, and contains all components a specific ML or AI solution might use to attend a specific requirement. [More Information](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/resource-groups?q=resource+groups). @@ -160,16 +196,19 @@ Please follow the [contribution guidelines](CONTRIBUTING.md) for more details. ## Reference +[SAP Note: 3437766 (Availability of Generative AI Models)](https://me.sap.com/notes/3437766) + [How this project consumes generative AI models.](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/consume-generative-ai-models-using-sap-ai-core) -[SAP Note: 3437766 (Availability of Generative AI Models)](https://me.sap.com/notes/3437766) +[The spinner "Ellipsis" is provided by loading.io](https://loading.io/icon/) [zJoule - ABAP Copilot website repository](https://github.com/The-Nefarious-Developer/zjoule-website) [zJoule - ABAP Copilot website](https://zjoule.com) -[The spinner "Ellipsis" is provided by loading.io](https://loading.io/icon/) +[Ollama API documentation](https://github.com/ollama/ollama/blob/main/docs/api.md) +[Ollama website](https://ollama.com) ## License Copyright (c) 2024 Nicholas Coutinho Checan.
diff --git a/assets/login_0.png b/assets/login_0.png new file mode 100644 index 00000000..d3b26802 Binary files /dev/null and b/assets/login_0.png differ diff --git a/assets/login_ollama_1.png b/assets/login_ollama_1.png new file mode 100644 index 00000000..decf2e4c Binary files /dev/null and b/assets/login_ollama_1.png differ diff --git a/assets/login_ollama_2.png b/assets/login_ollama_2.png new file mode 100644 index 00000000..87b1dfde Binary files /dev/null and b/assets/login_ollama_2.png differ diff --git a/assets/login_1.png b/assets/login_sap_1.png similarity index 100% rename from assets/login_1.png rename to assets/login_sap_1.png diff --git a/assets/login_2.png b/assets/login_sap_2.png similarity index 100% rename from assets/login_2.png rename to assets/login_sap_2.png diff --git a/assets/view.png b/assets/view.png new file mode 100644 index 00000000..0f4bebdd Binary files /dev/null and b/assets/view.png differ diff --git a/com.developer.nefarious.zjoule.feature/.project b/com.developer.nefarious.zjoule.feature/.project index 4264f769..9251b5a2 100644 --- a/com.developer.nefarious.zjoule.feature/.project +++ b/com.developer.nefarious.zjoule.feature/.project @@ -10,8 +10,25 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature org.eclipse.pde.FeatureNature + + + 1737318973864 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/com.developer.nefarious.zjoule.plugin/.project b/com.developer.nefarious.zjoule.plugin/.project index 071cd857..7f7ef35c 100644 --- a/com.developer.nefarious.zjoule.plugin/.project +++ b/com.developer.nefarious.zjoule.plugin/.project @@ -31,4 +31,15 @@ org.eclipse.pde.PluginNature org.eclipse.jdt.core.javanature + + + 1737318973867 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/com.developer.nefarious.zjoule.plugin/META-INF/MANIFEST.MF b/com.developer.nefarious.zjoule.plugin/META-INF/MANIFEST.MF index 9082351b..6710aa18 100644 --- a/com.developer.nefarious.zjoule.plugin/META-INF/MANIFEST.MF +++ b/com.developer.nefarious.zjoule.plugin/META-INF/MANIFEST.MF @@ -25,11 +25,13 @@ Export-Package: com.developer.nefarious.zjoule.plugin.chat, com.developer.nefarious.zjoule.plugin.chat.memory, com.developer.nefarious.zjoule.plugin.chat.models, + com.developer.nefarious.zjoule.plugin.chat.ollama, com.developer.nefarious.zjoule.plugin.chat.openai, com.developer.nefarious.zjoule.plugin.chat.utils, com.developer.nefarious.zjoule.plugin.core, com.developer.nefarious.zjoule.plugin.core.events, com.developer.nefarious.zjoule.plugin.core.functions, + com.developer.nefarious.zjoule.plugin.core.preferences, com.developer.nefarious.zjoule.plugin.core.ui, com.developer.nefarious.zjoule.plugin.core.utils, com.developer.nefarious.zjoule.plugin.login, diff --git a/com.developer.nefarious.zjoule.plugin/plugin.xml b/com.developer.nefarious.zjoule.plugin/plugin.xml index b75d00aa..566632df 100644 --- a/com.developer.nefarious.zjoule.plugin/plugin.xml +++ b/com.developer.nefarious.zjoule.plugin/plugin.xml @@ -2,8 +2,7 @@ - + @@ -17,16 +16,23 @@ inject="true"> - - + + + - - - + + + + + + + + diff --git a/com.developer.nefarious.zjoule.plugin/resources/views/scripts.js b/com.developer.nefarious.zjoule.plugin/resources/views/scripts.js index a0331bb2..1d30d2f7 100644 --- a/com.developer.nefarious.zjoule.plugin/resources/views/scripts.js +++ b/com.developer.nefarious.zjoule.plugin/resources/views/scripts.js @@ -164,4 +164,10 @@ function finishLoading() { chatBox.removeChild(loadingIndicator); } unblockInput(); -} \ No newline at end of file +} + +document.addEventListener('keydown', function(event) { + if (event.key === 'Enter') { + sendMessage(); + } +}); \ No newline at end of file diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/auth/SessionManager.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/auth/SessionManager.java index bbca244b..a0e34037 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/auth/SessionManager.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/auth/SessionManager.java @@ -6,6 +6,8 @@ import com.developer.nefarious.zjoule.plugin.memory.EclipseMemory; import com.developer.nefarious.zjoule.plugin.memory.MemoryAccessToken; import com.developer.nefarious.zjoule.plugin.memory.MemoryDeployment; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaEndpoint; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaModel; import com.developer.nefarious.zjoule.plugin.memory.MemoryResourceGroup; import com.developer.nefarious.zjoule.plugin.memory.MemoryServiceKey; @@ -17,19 +19,12 @@ */ public abstract class SessionManager { - /** - * Checks if the user is currently logged in. - * A user is considered logged in if the following memory components are not empty: - * - * - * @return {@code true} if the user is logged in, {@code false} otherwise. - */ + public static boolean isUserLoggedIn() { + return (isSapSession() || isOllamaSession()) ? true : false; + } + + public static boolean isSapSession() { MemoryAccessToken memoryAccessToken = MemoryAccessToken.getInstance(); MemoryServiceKey memoryServiceKey = MemoryServiceKey.getInstance(); MemoryResourceGroup memoryResourceGroup = MemoryResourceGroup.getInstance(); @@ -38,6 +33,13 @@ public static boolean isUserLoggedIn() { return (memoryAccessToken.isEmpty() || memoryServiceKey.isEmpty() || memoryResourceGroup.isEmpty() || memoryDeployment.isEmpty()) ? false : true; } + + public static boolean isOllamaSession() { + MemoryOllamaEndpoint memoryOllamaEndpoint = MemoryOllamaEndpoint.getInstance(); + MemoryOllamaModel memoryOllamaModel = MemoryOllamaModel.getInstance(); + + return (memoryOllamaEndpoint.isEmpty() || memoryOllamaModel.isEmpty()) ? false : true; + } /** * Executes the login process using the specified browser. @@ -66,6 +68,23 @@ public static void logout(final Browser browser, final EclipseMemory eclipseMemo browser.execute("logout();"); } } + + public static void clearAllSessions() { + clearSapSession(); + clearOllamaSession(); + } + + private static void clearSapSession() { + MemoryAccessToken.getInstance().clear(); + MemoryServiceKey.getInstance().clear(); + MemoryResourceGroup.getInstance().clear(); + MemoryDeployment.getInstance().clear(); + } + + private static void clearOllamaSession() { + MemoryOllamaEndpoint.getInstance().clear(); + MemoryOllamaModel.getInstance().clear(); + } /** * Private constructor to prevent instantiation of this utility class. diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/AIClientFactory.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/AIClientFactory.java index 3f3ea8a3..12d80029 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/AIClientFactory.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/AIClientFactory.java @@ -4,14 +4,23 @@ import com.developer.nefarious.zjoule.plugin.auth.AuthClient; import com.developer.nefarious.zjoule.plugin.auth.AuthClientHelper; +import com.developer.nefarious.zjoule.plugin.auth.SessionManager; import com.developer.nefarious.zjoule.plugin.chat.memory.MemoryMessageHistory; +import com.developer.nefarious.zjoule.plugin.chat.ollama.OllamaClient; +import com.developer.nefarious.zjoule.plugin.chat.ollama.OllamaClientHelper; import com.developer.nefarious.zjoule.plugin.chat.openai.OpenAIClient; import com.developer.nefarious.zjoule.plugin.chat.openai.OpenAIClientHelper; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; import com.developer.nefarious.zjoule.plugin.memory.MemoryAccessToken; import com.developer.nefarious.zjoule.plugin.memory.MemoryDeployment; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaEndpoint; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaModel; import com.developer.nefarious.zjoule.plugin.memory.MemoryResourceGroup; import com.developer.nefarious.zjoule.plugin.memory.MemoryServiceKey; +import com.developer.nefarious.zjoule.plugin.models.AccessToken; import com.developer.nefarious.zjoule.plugin.models.Deployment; +import com.developer.nefarious.zjoule.plugin.models.OllamaModel; +import com.developer.nefarious.zjoule.plugin.models.ServiceKey; /** * A factory class responsible for creating instances of {@link IAIClient}. @@ -29,13 +38,22 @@ public abstract class AIClientFactory { * @return an instance of {@link IAIClient} for the corresponding model, or {@code null} if unsupported. */ public static IAIClient getClient() { - - // Load memory components for access token, service key, resource group, deployment, and message history. - MemoryAccessToken memoryAccessToken = MemoryAccessToken.getInstance(); - MemoryServiceKey memoryServiceKey = MemoryServiceKey.getInstance(); - MemoryResourceGroup memoryResourceGroup = MemoryResourceGroup.getInstance(); - MemoryDeployment memoryDeployment = MemoryDeployment.getInstance(); - MemoryMessageHistory memoryMessageHistory = MemoryMessageHistory.getInstance(); + if (SessionManager.isSapSession()) { + return getClientForSapAiCore(); + } else if (SessionManager.isOllamaSession()) { + return getClientForOllama(); + } else { + return null; + } + } + + private static IAIClient getClientForSapAiCore() { + // Load memory components for access token, service key, resource group, deployment, and message history. + MemoryMessageHistory memoryMessageHistory = MemoryMessageHistory.getInstance(); + IMemoryObject memoryAccessToken = MemoryAccessToken.getInstance(); + IMemoryObject memoryServiceKey = MemoryServiceKey.getInstance(); + IMemoryObject memoryResourceGroup = MemoryResourceGroup.getInstance(); + IMemoryObject memoryDeployment = MemoryDeployment.getInstance(); // Initialize authentication helpers and the authentication client. AuthClientHelper authHelper = new AuthClientHelper(); @@ -52,6 +70,16 @@ public static IAIClient getClient() { return null; } } + + private static IAIClient getClientForOllama() { + MemoryMessageHistory memoryMessageHistory = MemoryMessageHistory.getInstance(); + IMemoryObject memoryOllamaEndpoint = MemoryOllamaEndpoint.getInstance(); + IMemoryObject memoryOllamaModel = MemoryOllamaModel.getInstance(); + + OllamaClientHelper helper = new OllamaClientHelper(memoryOllamaModel); + return new OllamaClient(memoryMessageHistory, memoryOllamaEndpoint, helper); + + } /** * Checks if the given model name corresponds to an OpenAI model. diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ChatOrchestrator.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ChatOrchestrator.java index 11221a6f..1f0af429 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ChatOrchestrator.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ChatOrchestrator.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; +import com.developer.nefarious.zjoule.plugin.core.preferences.Instruction; import com.developer.nefarious.zjoule.plugin.models.Role; /** diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/IAIClient.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/IAIClient.java index 553854b4..1091927f 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/IAIClient.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/IAIClient.java @@ -49,8 +49,8 @@ public interface IAIClient { /** * Updates the chat message history with the provided list of messages. * - * @param messages the list of {@link IChatMessage} objects to save as the updated chat history. + * @param chatMessages the list of {@link IChatMessage} objects to save as the updated chat history. */ - void setMessageHistory(final List messages); + void setMessageHistory(final List chatMessages); } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/Instruction.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/Instruction.java deleted file mode 100644 index c579e2fc..00000000 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/Instruction.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.developer.nefarious.zjoule.plugin.chat; - -/** - * Provides system-level instructions for AI interactions. - * The {@code Instruction} class contains predefined instructions used - * to guide the AI's behavior and ensure responses adhere to best practices. - * This is a utility class and cannot be instantiated. - */ -public abstract class Instruction { - - /** - * The predefined system message text used to guide the AI's behavior. - * The message encourages the AI to act as a friendly and informative SAP ABAP developer, - * adhering to best practices and clean-code guidelines. - */ - private static final String SYSTEM_MESSAGE_TEXT = - "Act as a friendly and informative SAP ABAP " - + "Developer expert, always considering the " - + "best practices and clean-code guidelines " - + "when providing answers."; - - /** - * Retrieves the predefined system message text. - * - * @return the system message text as a {@link String}. - */ - public static String getMessage() { - return SYSTEM_MESSAGE_TEXT; - } - - /** - * Private constructor to prevent instantiation of this utility class. - */ - private Instruction() { } - -} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/memory/MemoryMessageHistory.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/memory/MemoryMessageHistory.java index c3329f14..ec1c4254 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/memory/MemoryMessageHistory.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/memory/MemoryMessageHistory.java @@ -13,7 +13,7 @@ */ public class MemoryMessageHistory implements IMemoryMessageHistory { - /** Singleton instance of {@code MemoryMessageHistory}. */ + /** Singleton instance of {@code IMemoryMessageHistory}. */ private static MemoryMessageHistory instance; /** Serializer for converting objects to and from serialized formats. */ @@ -23,9 +23,9 @@ public class MemoryMessageHistory implements IMemoryMessageHistory { private IEclipseMemory eclipseMemory; /** - * Retrieves the singleton instance of {@code MemoryMessageHistory}. + * Retrieves the singleton instance of {@code IMemoryMessageHistory}. * - * @return the singleton instance of {@code MemoryMessageHistory}. + * @return the singleton instance of {@code IMemoryMessageHistory}. * @throws IllegalStateException if the instance is not initialized. */ public static MemoryMessageHistory getInstance() { diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/IOllamaClientHelper.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/IOllamaClientHelper.java new file mode 100644 index 00000000..cf1a93b2 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/IOllamaClientHelper.java @@ -0,0 +1,38 @@ +package com.developer.nefarious.zjoule.plugin.chat.ollama; + +import java.net.http.HttpRequest.BodyPublisher; +import java.util.List; + +import com.developer.nefarious.zjoule.plugin.chat.IChatMessage; + +/** + * Defines helper methods for interacting with the Ollama service. + * This interface provides functionality for processing chat completion requests + * and parsing responses returned by Ollama. + */ +public interface IOllamaClientHelper { + + /** + * Converts a serialized JSON response body from the Ollama service into an {@link IChatMessage}. + * The method deserializes the response and extracts the first message returned by the AI. + * + * @param serializedResponseBody the JSON response body as a {@link String}. + * @return the first {@link IChatMessage} extracted from the response. + */ + IChatMessage convertResponseToObject(final String serializedResponseBody); + + /** + * Creates an HTTP request body for a chat completion request to the Ollama service. + * The request body includes parameters such as: + *
    + *
  • Chat messages providing the context for the conversation.
  • + *
  • Maximum number of tokens allowed for the response.
  • + *
  • Temperature, frequency penalty, and presence penalty settings.
  • + *
  • Stop sequences for terminating the response.
  • + *
+ * + * @param messages a list of {@link IChatMessage} objects representing the chat context. + * @return a {@link BodyPublisher} object containing the serialized request body. + */ + BodyPublisher createRequestBody(final List messages); +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaChatMessage.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaChatMessage.java new file mode 100644 index 00000000..c2f125b4 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaChatMessage.java @@ -0,0 +1,85 @@ +package com.developer.nefarious.zjoule.plugin.chat.ollama; + +import com.developer.nefarious.zjoule.plugin.chat.IChatMessage; +import com.developer.nefarious.zjoule.plugin.models.Role; + +/** + * Represents a chat message exchanged with the Ollama model. + * The {@code OllamaChatMessage} class implements {@link IChatMessage} and + * encapsulates the role and content of a message in the chat context. + */ +public class OllamaChatMessage implements IChatMessage { + + + /** The role of the message (e.g., user, system, assistant). */ + private Role role; + + /** The content of the message. */ + private String content; + + /** + * Default constructor for creating an empty {@code OllamaChatMessage}. + */ + public OllamaChatMessage() { } + + /** + * Constructs a new {@code OllamaChatMessage} with the specified role and content. + * + * @param role the role of the message (e.g., {@link Role#USER}, {@link Role#SYSTEM}). + * @param content the content of the message. + */ + public OllamaChatMessage(final Role role, final String content) { + this.role = role; + this.content = content; + } + + /** + * Retrieves the content of the message. + * + * @return the content of the message as a {@link String}. + */ + public String getContent() { + return content; + } + + /** + * Retrieves the message content. + * This is the implementation of the {@link IChatMessage#getMessage()} method. + * + * @return the message content as a {@link String}. + */ + @Override + public String getMessage() { + return content; + } + + /** + * Retrieves the role associated with the message. + * This is the implementation of the {@link IChatMessage#getRole()} method. + * + * @return the role of the message as a {@link Role}. + */ + @Override + public Role getRole() { + return role; + } + + /** + * Sets the content of the message. + * + * @param content the new content of the message. + */ + public void setContent(final String content) { + this.content = content; + } + + /** + * Sets the role of the message. + * + * @param role the new role of the message (e.g., {@link Role#USER}, {@link Role#SYSTEM}). + */ + public void setRole(final Role role) { + this.role = role; + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaClient.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaClient.java new file mode 100644 index 00000000..b4e92914 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaClient.java @@ -0,0 +1,94 @@ +package com.developer.nefarious.zjoule.plugin.chat.ollama; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpResponse; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import com.developer.nefarious.zjoule.plugin.chat.IAIClient; +import com.developer.nefarious.zjoule.plugin.chat.IChatMessage; +import com.developer.nefarious.zjoule.plugin.chat.memory.IMemoryMessageHistory; +import com.developer.nefarious.zjoule.plugin.chat.models.Message; +import com.developer.nefarious.zjoule.plugin.chat.models.MessageHistory; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; +import com.developer.nefarious.zjoule.plugin.models.Role; + +public class OllamaClient implements IAIClient { + + private HttpClient httpClient; + + private IMemoryMessageHistory memoryMessageHistory; + + private IMemoryObject memoryOllamaEndpoint; + + private IOllamaClientHelper helper; + + public OllamaClient( + final IMemoryMessageHistory memoryMessageHistory, + final IMemoryObject memoryOllamaEndpoint, + final IOllamaClientHelper ollamaClientHelper) { + this.httpClient = HttpClient.newHttpClient(); + this.memoryMessageHistory = memoryMessageHistory; + this.memoryOllamaEndpoint = memoryOllamaEndpoint; + helper = ollamaClientHelper; + } + + @Override + public IChatMessage chatCompletion(final List messages) throws IOException, InterruptedException { + URI endpoint = createChatEndpoint(); + + BodyPublisher requestBody = helper.createRequestBody(messages); + + HttpRequest request = HttpRequest.newBuilder() + .uri(endpoint) + .POST(requestBody) + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + return helper.convertResponseToObject(response.body()); + } + + @Override + public IChatMessage createMessage(final Role role, final String userPrompt) { + return new OllamaChatMessage(role, userPrompt); + } + + @Override + public List getMessageHistory() { + MessageHistory messageHistory = memoryMessageHistory.load(); + if (messageHistory == null) { + return Collections.emptyList(); + } + + List messages = messageHistory.getMessages(); + if (messages == null || messages.isEmpty()) { + return Collections.emptyList(); + } + + return messages.stream().map(message -> + new OllamaChatMessage(message.getRole(), message.getContent())) + .collect(Collectors.toList()); + } + + @Override + public void setMessageHistory(final List chatMessages) { + MessageHistory newMessageHistory = new MessageHistory(); + newMessageHistory.setMessages(chatMessages.stream().map( + chatMessage -> new Message(chatMessage.getRole(), chatMessage.getMessage())) + .collect(Collectors.toList())); + memoryMessageHistory.save(newMessageHistory); + } + + private URI createChatEndpoint() { + String endpoint = memoryOllamaEndpoint.load(); + String endpointInStringFormat = endpoint + "/api/chat"; + return URI.create(endpointInStringFormat); + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaClientHelper.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaClientHelper.java new file mode 100644 index 00000000..968f4682 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaClientHelper.java @@ -0,0 +1,44 @@ +package com.developer.nefarious.zjoule.plugin.chat.ollama; + +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.util.List; + +import com.developer.nefarious.zjoule.plugin.chat.IChatMessage; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; +import com.developer.nefarious.zjoule.plugin.models.OllamaModel; +import com.google.gson.Gson; + +public class OllamaClientHelper implements IOllamaClientHelper { + + private IMemoryObject memoryOllamaModel; + + public OllamaClientHelper(final IMemoryObject memoryOllamaModel) { + this.memoryOllamaModel = memoryOllamaModel; + } + + @Override + public IChatMessage convertResponseToObject(final String serializedResponseBody) { + Gson gson = new Gson(); + OllamaRequestResponse deserializedResponseBody = gson.fromJson(serializedResponseBody, OllamaRequestResponse.class); + return deserializedResponseBody.getMessage(); + } + + @Override + public BodyPublisher createRequestBody(final List messages) { + OllamaRequestBody requestBody = new OllamaRequestBody(); + + requestBody.setModel(getSelectedModel()); + requestBody.setMessages(messages); + requestBody.setStream(false); + + BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.ofString(requestBody.toString()); + return HttpRequest.BodyPublishers.fromPublisher(bodyPublisher); + } + + private String getSelectedModel() { + OllamaModel ollamaModel = memoryOllamaModel.load(); + return ollamaModel.getName(); + } + +} \ No newline at end of file diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaRequestBody.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaRequestBody.java new file mode 100644 index 00000000..362bcb14 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaRequestBody.java @@ -0,0 +1,58 @@ +package com.developer.nefarious.zjoule.plugin.chat.ollama; + +import java.util.List; + +import com.developer.nefarious.zjoule.plugin.chat.IChatMessage; +import com.developer.nefarious.zjoule.plugin.models.Role; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class OllamaRequestBody { + + private String model; + + private boolean stream; + + private List messages; + + public String getModel() { + return model; + } + + public void setModel(final String model) { + this.model = model; + } + + public boolean isStream() { + return stream; + } + + public void setStream(final boolean stream) { + this.stream = stream; + } + + public List getMessages() { + return messages; + } + + public void setMessages(final List messages) { + this.messages = messages; + } + + /** + * Serializes this object to its JSON representation using {@link Gson}. + * The {@link Role.RoleSerializer} is registered to handle role serialization. + * + * @return the JSON representation of this request body as a {@link String}. + */ + @Override + public String toString() { + // @formatter:off + Gson gson = new GsonBuilder() + .registerTypeAdapter(Role.class, new Role.RoleSerializer()) + .create(); + // @formatter:on + return gson.toJson(this); + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaRequestResponse.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaRequestResponse.java new file mode 100644 index 00000000..a476d854 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/ollama/OllamaRequestResponse.java @@ -0,0 +1,124 @@ +package com.developer.nefarious.zjoule.plugin.chat.ollama; + +import com.google.gson.annotations.SerializedName; + +public class OllamaRequestResponse { + + private String model; + + @SerializedName("created_at") + private String createdAt; + + private OllamaChatMessage message; + + @SerializedName("done_reason") + private String doneReason; + + private boolean done; + + @SerializedName("total_duration") + private long totalDuration; + + @SerializedName("load_duration") + private long loadDuration; + + @SerializedName("prompt_eval_count") + private int promptEvalCount; + + @SerializedName("prompt_eval_duration") + private long promptEvalDuration; + + @SerializedName("eval_count") + private int evalCount; + + @SerializedName("eval_duration") + private long evalDuration; + + public String getModel() { + return model; + } + + public void setModel(final String model) { + this.model = model; + } + + public String getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(final String createdAt) { + this.createdAt = createdAt; + } + + public OllamaChatMessage getMessage() { + return message; + } + + public void setMessage(final OllamaChatMessage message) { + this.message = message; + } + + public String getDoneReason() { + return doneReason; + } + + public void setDoneReason(final String doneReason) { + this.doneReason = doneReason; + } + + public boolean isDone() { + return done; + } + + public void setDone(final boolean done) { + this.done = done; + } + + public long getTotalDuration() { + return totalDuration; + } + + public void setTotalDuration(final long totalDuration) { + this.totalDuration = totalDuration; + } + + public long getLoadDuration() { + return loadDuration; + } + + public void setLoadDuration(final long loadDuration) { + this.loadDuration = loadDuration; + } + + public int getPromptEvalCount() { + return promptEvalCount; + } + + public void setPromptEvalCount(final int promptEvalCount) { + this.promptEvalCount = promptEvalCount; + } + + public long getPromptEvalDuration() { + return promptEvalDuration; + } + + public void setPromptEvalDuration(final long promptEvalDuration) { + this.promptEvalDuration = promptEvalDuration; + } + + public int getEvalCount() { + return evalCount; + } + + public void setEvalCount(final int evalCount) { + this.evalCount = evalCount; + } + + public long getEvalDuration() { + return evalDuration; + } + + public void setEvalDuration(final long evalDuration) { + this.evalDuration = evalDuration; + } +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/openai/OpenAIClient.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/openai/OpenAIClient.java index 2f964cca..39491bdb 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/openai/OpenAIClient.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/chat/openai/OpenAIClient.java @@ -152,8 +152,8 @@ public List getMessageHistory() { @Override public void setMessageHistory(final List chatMessages) { MessageHistory newMessageHistory = new MessageHistory(); - newMessageHistory.setMessages( - chatMessages.stream().map(chatMessage -> new Message(chatMessage.getRole(), chatMessage.getMessage())) + newMessageHistory.setMessages(chatMessages.stream().map( + chatMessage -> new Message(chatMessage.getRole(), chatMessage.getMessage())) .collect(Collectors.toList())); memoryMessageHistory.save(newMessageHistory); } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/Activator.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/Activator.java index 3b56237f..a0f4a300 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/Activator.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/Activator.java @@ -1,16 +1,21 @@ package com.developer.nefarious.zjoule.plugin.core; +import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; import com.developer.nefarious.zjoule.plugin.chat.memory.MemoryMessageHistory; import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryAccessToken; import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryDeployment; +import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryOllamaEndpoint; +import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryOllamaModel; import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryResourceGroup; import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryServiceKey; import com.developer.nefarious.zjoule.plugin.memory.EclipseMemory; import com.developer.nefarious.zjoule.plugin.memory.MemoryAccessToken; import com.developer.nefarious.zjoule.plugin.memory.MemoryDeployment; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaEndpoint; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaModel; import com.developer.nefarious.zjoule.plugin.memory.MemoryResourceGroup; import com.developer.nefarious.zjoule.plugin.memory.MemoryServiceKey; import com.developer.nefarious.zjoule.plugin.memory.utils.ObjectSerializer; @@ -62,12 +67,16 @@ private void initialize() { MemoryResourceGroup.initialize(eclipseMemory); MemoryDeployment.initialize(objectSerializer, eclipseMemory); MemoryMessageHistory.initialize(objectSerializer, eclipseMemory); + MemoryOllamaEndpoint.initialize(eclipseMemory); + MemoryOllamaModel.initialize(objectSerializer, eclipseMemory); // Initialize memory resources for login operation TemporaryMemoryAccessToken.initialize(objectSerializer, eclipseMemory); TemporaryMemoryServiceKey.initialize(objectSerializer, eclipseMemory); TemporaryMemoryResourceGroup.initialize(eclipseMemory); TemporaryMemoryDeployment.initialize(objectSerializer, eclipseMemory); + TemporaryMemoryOllamaEndpoint.initialize(eclipseMemory); + TemporaryMemoryOllamaModel.initialize(objectSerializer, eclipseMemory); } /** @@ -94,4 +103,10 @@ public void stop(final BundleContext context) throws Exception { plugin = null; super.stop(context); } + + @Override + protected void initializeDefaultPreferences(final IPreferenceStore store) { + store.setDefault("instructions", "Be a friendly SAP ABAP expert, providing concise answers aligned with best practices and clean-code principles."); + + } } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/functions/LoginHandler.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/functions/LoginHandler.java index baf22270..6b2f4f52 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/functions/LoginHandler.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/functions/LoginHandler.java @@ -13,12 +13,14 @@ import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; +import com.developer.nefarious.zjoule.plugin.login.LoginOptionsWizardDialog; import com.developer.nefarious.zjoule.plugin.login.LoginWizard; +import com.developer.nefarious.zjoule.plugin.login.SapLoginWizard; /** * Handles the "Login" action for connecting to a BTP subaccount. *

- * This class extends {@link Action} and launches a {@link LoginWizard} in a + * This class extends {@link Action} and launches a {@link SapLoginWizard} in a * {@link WizardDialog} when the action is triggered. It also manages the icon for the action. */ public class LoginHandler extends Action { @@ -65,8 +67,8 @@ private LoginHandler(final Shell shell, final Browser browser) { */ @Override public void run() { - LoginWizard wizard = new LoginWizard(browser); - WizardDialog dialog = new WizardDialog(shell, wizard); + LoginWizard wizard = new LoginWizard(shell, browser); + LoginOptionsWizardDialog dialog = new LoginOptionsWizardDialog(shell, wizard); dialog.open(); } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/functions/PreferencesHandler.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/functions/PreferencesHandler.java new file mode 100644 index 00000000..19639399 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/functions/PreferencesHandler.java @@ -0,0 +1,50 @@ +package com.developer.nefarious.zjoule.plugin.core.functions; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.preference.PreferenceDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.dialogs.PreferencesUtil; + +public class PreferencesHandler extends Action { + + private static final String ICON = "platform:/plugin/org.eclipse.egit.ui/icons/obj16/settings.png"; + + private static final String PREFERENCES_PAGE_ID = "com.developer.nefarious.zjoule.plugin.core.preferences.PluginPreferencesPage"; + + public static PreferencesHandler create() { + return new PreferencesHandler(); + } + + private PreferencesHandler() { + setText("Preferences"); + setToolTipText("User settings."); + setIcon(); + } + + @Override + public void run() { + PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null, PREFERENCES_PAGE_ID, null, null); + if (dialog != null) { + dialog.open(); + } + } + + private void setIcon() { + try { + URI iconURI = new URI(ICON); + URL iconURL = iconURI.toURL(); + setImageDescriptor(ImageDescriptor.createFromURL(iconURL)); + } catch (URISyntaxException | MalformedURLException e) { + e.printStackTrace(); + setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK)); + } + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/InputField.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/InputField.java new file mode 100644 index 00000000..15ecba30 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/InputField.java @@ -0,0 +1,32 @@ +package com.developer.nefarious.zjoule.plugin.core.preferences; + +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; + +public class InputField extends StringFieldEditor { + + private static final int WIDTH_IN_CHARS = 10; + + private static final int HEIGTH_IN_CHARS = 2; + + private static final int MINIMUM_WIDTH = 300; + + private static final int MINIMUM_HEIGHT = 50; + + public InputField(final String key, final String labelText, final Composite parent) { + super(key, labelText, WIDTH_IN_CHARS, HEIGTH_IN_CHARS, VALIDATE_ON_FOCUS_LOST, parent); + setFixedWidth(parent); + } + + private void setFixedWidth(final Composite parent) { + if (getTextControl(parent) != null) { + GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); + gridData.widthHint = MINIMUM_WIDTH; + gridData.heightHint = MINIMUM_HEIGHT; + getTextControl(parent).setLayoutData(gridData); + } + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/Instruction.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/Instruction.java new file mode 100644 index 00000000..ccfe7f4a --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/Instruction.java @@ -0,0 +1,32 @@ +package com.developer.nefarious.zjoule.plugin.core.preferences; + +import org.eclipse.jface.preference.IPreferenceStore; + +import com.developer.nefarious.zjoule.plugin.core.Activator; + +/** + * Provides system-level instructions for AI interactions. + * The {@code Instruction} class contains the operation to retrieve instructions + * used to guide the AI's behavior and ensure responses adhere to best practices. + * This is a utility class and cannot be instantiated. + */ +public abstract class Instruction { + + public static String KEY = "instructions"; + + /** + * Retrieves the preferred system message text. + * + * @return the system message text as a {@link String}. + */ + public static String getMessage() { + IPreferenceStore store = Activator.getDefault().getPreferenceStore(); + return store.getString(KEY); + } + + /** + * Private constructor to prevent instantiation of this utility class. + */ + private Instruction() { } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/OutputField.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/OutputField.java new file mode 100644 index 00000000..134ffc2f --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/OutputField.java @@ -0,0 +1,67 @@ +package com.developer.nefarious.zjoule.plugin.core.preferences; + +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; + +public class OutputField extends StringFieldEditor { + + private String currentValue = ""; // To hold the temporary value + + private static final int MINIMUM_WIDTH = 300; + + public OutputField(final String labelText, final String value, final Composite parent) { + super("local", labelText, parent); + setStringValue(value); + setFixedWidth(parent); + } + + @Override + protected void doLoadDefault() { + // Do nothing to prevent the value from being reset + } + + @Override + protected void createControl(final Composite parent) { + super.createControl(parent); + + // Set the Text widget to read-only + Text textField = getTextControl(); + if (textField != null) { + textField.setEditable(false); + } + } + + @Override + protected void doStore() { + // Do nothing, as we don't want to store the value + } + + @Override + protected void doLoad() { + // Do nothing, as we don't want to load from the preference store + } + + @Override + public void setStringValue(final String value) { + super.setStringValue(value); + this.currentValue = value; // Keep the value locally + } + + @Override + public String getStringValue() { + return this.currentValue; // Return the locally stored value + } + + private void setFixedWidth(final Composite parent) { + // Retrieve the text control from the StringFieldEditor + if (getTextControl(parent) != null) { + GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); + gridData.widthHint = MINIMUM_WIDTH; + getTextControl(parent).setLayoutData(gridData); + } + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/PluginPreferencesPage.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/PluginPreferencesPage.java new file mode 100644 index 00000000..c630642f --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/preferences/PluginPreferencesPage.java @@ -0,0 +1,144 @@ +package com.developer.nefarious.zjoule.plugin.core.preferences; + +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +import com.developer.nefarious.zjoule.plugin.auth.SessionManager; +import com.developer.nefarious.zjoule.plugin.core.Activator; +import com.developer.nefarious.zjoule.plugin.memory.MemoryDeployment; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaEndpoint; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaModel; +import com.developer.nefarious.zjoule.plugin.memory.MemoryResourceGroup; + +public class PluginPreferencesPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + private static final int SPACER_SIZE = 10; + + private static final int SPACER_WIDTH = 10; + + private static final int SPACER_HEIGHT = 10; + + public PluginPreferencesPage() { + super(GRID); + } + + @Override + public void init(final IWorkbench workbench) { + setPreferenceStore(Activator.getDefault().getPreferenceStore()); + } + + @Override + protected void createFieldEditors() { + + displayInstructionsInput(); + + createSpacer(); + + if (SessionManager.isSapSession()) { + displaySapSettings(); + } else if (SessionManager.isOllamaSession()) { + displayOllamaSettings(); + } + + } + + private void displayInstructionsInput() { + Group group = new Group(getFieldEditorParent(), SWT.NONE); + group.setText("Chat Settings"); + group.setLayout(new GridLayout(1, false)); + group.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + + Composite groupContent = new Composite(group, SWT.NONE); + groupContent.setLayout(new GridLayout(1, false)); + groupContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + addField(new InputField(Instruction.KEY, "Instructions:", groupContent)); + } + + private void createSpacer() { + Composite spacer = new Composite(getFieldEditorParent(), SWT.NONE); + GridData gridData = new GridData(SPACER_WIDTH,SPACER_HEIGHT); + gridData.heightHint = SPACER_SIZE; + spacer.setLayoutData(gridData); + } + + private Composite createContentContainer(final String title) { + Group group = new Group(getFieldEditorParent(), SWT.NONE); + group.setText(title); + group.setLayout(new GridLayout(1, false)); + group.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + + Composite groupContent = new Composite(group, SWT.NONE); + groupContent.setLayout(new GridLayout(1, false)); + groupContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + return groupContent; + } + + private void displaySapSettings() { + Composite contentContainer = createContentContainer("SAP AI Core Configuration Parameters"); + + class OutputFieldFactory { + StringFieldEditor create(final String key, final String value) { + return new OutputField(key, value, contentContainer); + } + } + + OutputFieldFactory outputFieldFactory = new OutputFieldFactory(); + + String resourceGroup = MemoryResourceGroup.getInstance().load(); + addField(outputFieldFactory.create("Resource Group:", resourceGroup)); + + String configurationName = MemoryDeployment.getInstance().load().getConfigurationName(); + addField(outputFieldFactory.create("Configuration Name:", configurationName)); + + String model = MemoryDeployment.getInstance().load().getModelName(); + addField(outputFieldFactory.create("Model:", model)); + + String version = MemoryDeployment.getInstance().load().getModelVersion(); + addField(outputFieldFactory.create("Version:", version)); + + String deploymentUrl = MemoryDeployment.getInstance().load().getDeploymentUrl(); + addField(outputFieldFactory.create("Deployment Url:", deploymentUrl)); + } + + private void displayOllamaSettings() { + Composite contentContainer = createContentContainer("Ollama Configuration Parameters"); + + class OutputFieldFactory { + StringFieldEditor create(final String key, final String value) { + return new OutputField(key, value, contentContainer); + } + } + + OutputFieldFactory outputFieldFactory = new OutputFieldFactory(); + + String endpoint = MemoryOllamaEndpoint.getInstance().load(); + addField(outputFieldFactory.create("Endpoint:", endpoint)); + + String name = MemoryOllamaModel.getInstance().load().getName(); + addField(outputFieldFactory.create("Name:", name)); + + String model = MemoryOllamaModel.getInstance().load().getModel(); + addField(outputFieldFactory.create("Model:", model)); + + String format = MemoryOllamaModel.getInstance().load().getFormat(); + addField(outputFieldFactory.create("Format:", format)); + + String family = MemoryOllamaModel.getInstance().load().getFamily(); + addField(outputFieldFactory.create("Family:", family)); + + String parameterSize = MemoryOllamaModel.getInstance().load().getParameterSize(); + addField(outputFieldFactory.create("Parameter Size:", parameterSize)); + + String quantizationLevel = MemoryOllamaModel.getInstance().load().getQuantizationLevel(); + addField(outputFieldFactory.create("Quantization Level:", quantizationLevel)); + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/ui/ViewListener.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/ui/ViewListener.java index b0f30e3e..90b03d34 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/ui/ViewListener.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/core/ui/ViewListener.java @@ -18,6 +18,7 @@ import com.developer.nefarious.zjoule.plugin.core.functions.ClearHandler; import com.developer.nefarious.zjoule.plugin.core.functions.LoginHandler; import com.developer.nefarious.zjoule.plugin.core.functions.LogoutHandler; +import com.developer.nefarious.zjoule.plugin.core.functions.PreferencesHandler; import com.developer.nefarious.zjoule.plugin.core.functions.PromptHandler; import com.developer.nefarious.zjoule.plugin.core.utils.SystemProvider; @@ -131,6 +132,7 @@ private void setUpToolbar() { toolbar.add(LoginHandler.create(shell, browser)); IMenuManager menu = getMenu(); menu.add(ClearHandler.create(browser)); + menu.add(PreferencesHandler.create()); menu.add(new Separator()); menu.add(LogoutHandler.create(browser)); } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/LoginOptionsWizardDialog.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/LoginOptionsWizardDialog.java new file mode 100644 index 00000000..c73574fe --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/LoginOptionsWizardDialog.java @@ -0,0 +1,18 @@ +package com.developer.nefarious.zjoule.plugin.login; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.swt.widgets.Shell; + +public class LoginOptionsWizardDialog extends WizardDialog { + + public LoginOptionsWizardDialog(final Shell parentShell, final LoginWizard wizard) { + super(parentShell, wizard); + } + + @Override + public void updateButtons() { + super.updateButtons(); + getButton(IDialogConstants.FINISH_ID).setText("Select"); + } +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/LoginWizard.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/LoginWizard.java index 32b1484d..a05e6d6b 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/LoginWizard.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/LoginWizard.java @@ -1,102 +1,57 @@ package com.developer.nefarious.zjoule.plugin.login; import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.widgets.Shell; -import com.developer.nefarious.zjoule.plugin.auth.AuthClient; -import com.developer.nefarious.zjoule.plugin.auth.AuthClientHelper; -import com.developer.nefarious.zjoule.plugin.auth.IAuthClient; -import com.developer.nefarious.zjoule.plugin.auth.SessionManager; -import com.developer.nefarious.zjoule.plugin.login.api.ILoginClient; -import com.developer.nefarious.zjoule.plugin.login.api.LoginClient; -import com.developer.nefarious.zjoule.plugin.login.api.LoginClientHelper; -import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryAccessToken; -import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryDeployment; -import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryResourceGroup; -import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryServiceKey; -import com.developer.nefarious.zjoule.plugin.login.pages.FirstLoginWizardPage; -import com.developer.nefarious.zjoule.plugin.login.pages.SecondLoginWizardPage; +import com.developer.nefarious.zjoule.plugin.login.pages.LoginOptionsPage; -/** - * A wizard for logging into SAP AI Core. - *

- * The {@code LoginWizard} class manages a multi-page wizard that guides the user - * through the login process, including entering credentials and selecting configurations. - *

- * It integrates with temporary memory components and client objects to handle authentication - * and session management. - */ public class LoginWizard extends Wizard { - - /** The browser instance used for login-related UI updates. */ + + private Shell shell; + private Browser browser; - - /** The client responsible for managing the login process. */ - private ILoginClient loginClient; - - /** - * Constructs a new {@code LoginWizard} instance. - * - * @param browser the {@link Browser} instance used for login-related UI updates. - */ - public LoginWizard(final Browser browser) { + + public LoginWizard(final Shell shell, final Browser browser) { + this.shell = shell; this.browser = browser; - setWindowTitle("Login to SAP AI Core"); - loginClient = createLoginClient(); + setWindowTitle("Setup AI Provider"); } - - /** - * Adds the wizard pages to the login process. - *

- * This method initializes: - *

    - *
  • The {@link FirstLoginWizardPage} for entering credentials.
  • - *
  • The {@link SecondLoginWizardPage} for selecting resources and configurations.
  • - *
- */ + @Override public void addPages() { - addPage(new FirstLoginWizardPage(loginClient)); - addPage(new SecondLoginWizardPage(loginClient, TemporaryMemoryResourceGroup.getInstance(), TemporaryMemoryDeployment.getInstance())); + addPage(new LoginOptionsPage()); } - /** - * Creates and initializes the {@link ILoginClient} used for the login process. - * - * @return a new {@link ILoginClient} instance. - */ - private ILoginClient createLoginClient() { - TemporaryMemoryAccessToken tmpMemoryAccessToken = TemporaryMemoryAccessToken.getInstance(); - TemporaryMemoryServiceKey tmpMemoryServiceKey = TemporaryMemoryServiceKey.getInstance(); - - IAuthClient tmpAuthClient = new AuthClient(tmpMemoryAccessToken, tmpMemoryServiceKey, new AuthClientHelper()); - return new LoginClient(new LoginClientHelper(), tmpAuthClient); - } - - /** - * Completes the login process and persists the temporary memory. - *

- * This method persists the following temporary memory components: - *

    - *
  • {@link TemporaryMemoryAccessToken}
  • - *
  • {@link TemporaryMemoryServiceKey}
  • - *
  • {@link TemporaryMemoryResourceGroup}
  • - *
  • {@link TemporaryMemoryDeployment}
  • - *
- * It also logs in the user by invoking {@link SessionManager#login(Browser)}. - * - * @return {@code true} if the wizard completes successfully. - */ - @Override - public boolean performFinish() { - TemporaryMemoryAccessToken.getInstance().persist(); - TemporaryMemoryServiceKey.getInstance().persist(); - TemporaryMemoryResourceGroup.getInstance().persist(); - TemporaryMemoryDeployment.getInstance().persist(); - - SessionManager.login(browser); - return true; - } - -} \ No newline at end of file + @Override + public boolean performFinish() { + LoginOptionsPage loginOptionsPage = (LoginOptionsPage) getPage(LoginOptionsPage.PAGE_ID); + + if (loginOptionsPage.isOption1Selected()) { + startSapAiCoreLogin(); + return true; + } + + if (loginOptionsPage.isOption2Selected()) { + startOllamaLogin(); + return true; + } + + return false; + } + + private void startSapAiCoreLogin() { + SapLoginWizard wizard = new SapLoginWizard(browser); + WizardDialog dialog = new WizardDialog(shell, wizard); + dialog.open(); + } + + private void startOllamaLogin() { + OllamaLoginWizard wizard = new OllamaLoginWizard(browser); + WizardDialog dialog = new WizardDialog(shell, wizard); + dialog.open(); + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/OllamaLoginWizard.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/OllamaLoginWizard.java new file mode 100644 index 00000000..173500ae --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/OllamaLoginWizard.java @@ -0,0 +1,45 @@ +package com.developer.nefarious.zjoule.plugin.login; + +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.swt.browser.Browser; + +import com.developer.nefarious.zjoule.plugin.auth.SessionManager; +import com.developer.nefarious.zjoule.plugin.login.api.IOllamaLoginClient; +import com.developer.nefarious.zjoule.plugin.login.api.OllamaLoginClient; +import com.developer.nefarious.zjoule.plugin.login.api.OllamaLoginClientHelper; +import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryOllamaEndpoint; +import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryOllamaModel; +import com.developer.nefarious.zjoule.plugin.login.pages.FirstOllamaLoginWizardPage; +import com.developer.nefarious.zjoule.plugin.login.pages.SecondOllamaLoginWizardPage; + +public class OllamaLoginWizard extends Wizard { + + private Browser browser; + + private IOllamaLoginClient ollamaLoginClient; + + public OllamaLoginWizard(final Browser browser) { + this.browser = browser; + + setWindowTitle("Login to Ollama"); + ollamaLoginClient = new OllamaLoginClient(new OllamaLoginClientHelper()); + } + + @Override + public void addPages() { + addPage(new FirstOllamaLoginWizardPage(ollamaLoginClient, TemporaryMemoryOllamaEndpoint.getInstance())); + addPage(new SecondOllamaLoginWizardPage(TemporaryMemoryOllamaModel.getInstance())); + } + + @Override + public boolean performFinish() { + SessionManager.clearAllSessions(); + + TemporaryMemoryOllamaEndpoint.getInstance().persist(); + TemporaryMemoryOllamaModel.getInstance().persist(); + + SessionManager.login(browser); + return true; + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/SapLoginWizard.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/SapLoginWizard.java new file mode 100644 index 00000000..08652e7e --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/SapLoginWizard.java @@ -0,0 +1,104 @@ +package com.developer.nefarious.zjoule.plugin.login; + +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.swt.browser.Browser; + +import com.developer.nefarious.zjoule.plugin.auth.AuthClient; +import com.developer.nefarious.zjoule.plugin.auth.AuthClientHelper; +import com.developer.nefarious.zjoule.plugin.auth.IAuthClient; +import com.developer.nefarious.zjoule.plugin.auth.SessionManager; +import com.developer.nefarious.zjoule.plugin.login.api.ISapLoginClient; +import com.developer.nefarious.zjoule.plugin.login.api.SapLoginClient; +import com.developer.nefarious.zjoule.plugin.login.api.SapLoginClientHelper; +import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryAccessToken; +import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryDeployment; +import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryResourceGroup; +import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryServiceKey; +import com.developer.nefarious.zjoule.plugin.login.pages.FirstSapLoginWizardPage; +import com.developer.nefarious.zjoule.plugin.login.pages.SecondSapLoginWizardPage; + +/** + * A wizard for logging into SAP AI Core. + *

+ * The {@code LoginWizard} class manages a multi-page wizard that guides the user + * through the login process, including entering credentials and selecting configurations. + *

+ * It integrates with temporary memory components and client objects to handle authentication + * and session management. + */ +public class SapLoginWizard extends Wizard { + + /** The browser instance used for login-related UI updates. */ + private Browser browser; + + /** The client responsible for managing the login process. */ + private ISapLoginClient sapLoginClient; + + /** + * Constructs a new {@code LoginWizard} instance. + * + * @param browser the {@link Browser} instance used for login-related UI updates. + */ + public SapLoginWizard(final Browser browser) { + this.browser = browser; + + setWindowTitle("Login to SAP AI Core"); + sapLoginClient = createSapLoginClient(); + } + + /** + * Adds the wizard pages to the login process. + *

+ * This method initializes: + *

    + *
  • The {@link FirstSapLoginWizardPage} for entering credentials.
  • + *
  • The {@link SecondSapLoginWizardPage} for selecting resources and configurations.
  • + *
+ */ + @Override + public void addPages() { + addPage(new FirstSapLoginWizardPage(sapLoginClient)); + addPage(new SecondSapLoginWizardPage(sapLoginClient, TemporaryMemoryResourceGroup.getInstance(), TemporaryMemoryDeployment.getInstance())); + } + + /** + * Creates and initializes the {@link ISapLoginClient} used for the login process. + * + * @return a new {@link ISapLoginClient} instance. + */ + private ISapLoginClient createSapLoginClient() { + TemporaryMemoryAccessToken tmpMemoryAccessToken = TemporaryMemoryAccessToken.getInstance(); + TemporaryMemoryServiceKey tmpMemoryServiceKey = TemporaryMemoryServiceKey.getInstance(); + + IAuthClient tmpAuthClient = new AuthClient(tmpMemoryAccessToken, tmpMemoryServiceKey, new AuthClientHelper()); + return new SapLoginClient(new SapLoginClientHelper(), tmpAuthClient); + } + + /** + * Completes the login process and persists the temporary memory. + *

+ * This method persists the following temporary memory components: + *

    + *
  • {@link TemporaryMemoryAccessToken}
  • + *
  • {@link TemporaryMemoryServiceKey}
  • + *
  • {@link TemporaryMemoryResourceGroup}
  • + *
  • {@link TemporaryMemoryDeployment}
  • + *
+ * It also logs in the user by invoking {@link SessionManager#login(Browser)}. + * + * @return {@code true} if the wizard completes successfully. + */ + @Override + public boolean performFinish() { + SessionManager.clearAllSessions(); + + TemporaryMemoryAccessToken.getInstance().persist(); + TemporaryMemoryServiceKey.getInstance().persist(); + TemporaryMemoryResourceGroup.getInstance().persist(); + TemporaryMemoryDeployment.getInstance().persist(); + + SessionManager.login(browser); + return true; + } + +} \ No newline at end of file diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/GetOllamaModelsResponse.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/GetOllamaModelsResponse.java new file mode 100644 index 00000000..4586e145 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/GetOllamaModelsResponse.java @@ -0,0 +1,20 @@ +package com.developer.nefarious.zjoule.plugin.login.api; + +import java.util.List; + +import com.developer.nefarious.zjoule.plugin.models.OllamaModel; + +public class GetOllamaModelsResponse { + + private List models; + + // Getters and Setters + public List getModels() { + return models; + } + + public void setModels(final List models) { + this.models = models; + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/IOllamaLoginClient.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/IOllamaLoginClient.java new file mode 100644 index 00000000..b15c6da2 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/IOllamaLoginClient.java @@ -0,0 +1,9 @@ +package com.developer.nefarious.zjoule.plugin.login.api; + +import java.io.IOException; + +public interface IOllamaLoginClient { + + GetOllamaModelsResponse getModels(final String endpoint) throws IOException, InterruptedException; + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/IOllamaLoginClientHelper.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/IOllamaLoginClientHelper.java new file mode 100644 index 00000000..2a9ac1c0 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/IOllamaLoginClientHelper.java @@ -0,0 +1,11 @@ +package com.developer.nefarious.zjoule.plugin.login.api; + +import java.net.URI; + +public interface IOllamaLoginClientHelper { + + URI createUri(final String endpoint); + + GetOllamaModelsResponse parseOllamaModelsResponseToObject(final String responseBody); + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ILoginClient.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ISapLoginClient.java similarity index 97% rename from com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ILoginClient.java rename to com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ISapLoginClient.java index ea459166..8e8fa0b5 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ILoginClient.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ISapLoginClient.java @@ -10,7 +10,7 @@ * The {@code ILoginClient} defines methods for retrieving deployments and resource groups * by interacting with the SAP AI Core API. */ -public interface ILoginClient { +public interface ISapLoginClient { /** * Retrieves a list of deployments from the SAP AI Core API. diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ILoginClientHelper.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ISapLoginClientHelper.java similarity index 96% rename from com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ILoginClientHelper.java rename to com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ISapLoginClientHelper.java index df801b77..3fc70e7d 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ILoginClientHelper.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/ISapLoginClientHelper.java @@ -8,7 +8,7 @@ * The {@code ILoginClientHelper} defines methods for creating API request URIs * and parsing JSON response bodies into their corresponding Java objects. */ -public interface ILoginClientHelper { +public interface ISapLoginClientHelper { /** * Creates a URI for the given API endpoint. diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/OllamaLoginClient.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/OllamaLoginClient.java new file mode 100644 index 00000000..757ab935 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/OllamaLoginClient.java @@ -0,0 +1,35 @@ +package com.developer.nefarious.zjoule.plugin.login.api; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +public class OllamaLoginClient implements IOllamaLoginClient { + + private HttpClient httpClient; + + private IOllamaLoginClientHelper ollamaLoginClientHelper; + + public OllamaLoginClient(final IOllamaLoginClientHelper ollamaLoginClientHelper) { + httpClient = HttpClient.newHttpClient(); + this.ollamaLoginClientHelper = ollamaLoginClientHelper; + } + + @Override + public GetOllamaModelsResponse getModels(final String endpoint) + throws IOException, InterruptedException { + URI endpointUri = ollamaLoginClientHelper.createUri(endpoint + "/api/tags"); + + HttpRequest request = HttpRequest.newBuilder() + .uri(endpointUri) + .GET() + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + return ollamaLoginClientHelper.parseOllamaModelsResponseToObject(response.body()); + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/OllamaLoginClientHelper.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/OllamaLoginClientHelper.java new file mode 100644 index 00000000..a711cce1 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/OllamaLoginClientHelper.java @@ -0,0 +1,25 @@ +package com.developer.nefarious.zjoule.plugin.login.api; + +import java.net.URI; + +import com.google.gson.Gson; + +public class OllamaLoginClientHelper implements IOllamaLoginClientHelper { + + private Gson gson; + + public OllamaLoginClientHelper() { + gson = new Gson(); + } + + @Override + public URI createUri(final String endpoint) { + return URI.create(endpoint); + } + + @Override + public GetOllamaModelsResponse parseOllamaModelsResponseToObject(final String responseBody) { + return gson.fromJson(responseBody, GetOllamaModelsResponse.class); + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/LoginClient.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/SapLoginClient.java similarity index 71% rename from com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/LoginClient.java rename to com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/SapLoginClient.java index b9c7a399..98a7ee26 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/LoginClient.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/SapLoginClient.java @@ -10,19 +10,19 @@ import com.developer.nefarious.zjoule.plugin.models.ServiceKey; /** - * Implements the {@link ILoginClient} interface for managing API interactions related to login operations. + * Implements the {@link ISapLoginClient} interface for managing API interactions related to login operations. *

* The {@code LoginClient} class communicates with SAP AI Core APIs to retrieve deployments - * and resource groups, leveraging an {@link IAuthClient} for authentication and an {@link ILoginClientHelper} + * and resource groups, leveraging an {@link IAuthClient} for authentication and an {@link ISapLoginClientHelper} * for building requests and parsing responses. */ -public class LoginClient implements ILoginClient { +public class SapLoginClient implements ISapLoginClient { /** The HTTP client used for making API requests. */ private HttpClient httpClient; /** Helper class for constructing requests and parsing responses. */ - private ILoginClientHelper loginClientHelper; + private ISapLoginClientHelper sapLoginClientHelper; /** The authentication client used for retrieving access tokens. */ private IAuthClient authClient; @@ -30,12 +30,12 @@ public class LoginClient implements ILoginClient { /** * Constructs a new {@code LoginClient} instance. * - * @param loginClientHelper the helper for constructing requests and parsing responses. + * @param sapLoginClientHelper the helper for constructing requests and parsing responses. * @param authClient the authentication client for retrieving access tokens. */ - public LoginClient(final ILoginClientHelper loginClientHelper, final IAuthClient authClient) { + public SapLoginClient(final ISapLoginClientHelper sapLoginClientHelper, final IAuthClient authClient) { httpClient = HttpClient.newHttpClient(); - this.loginClientHelper = loginClientHelper; + this.sapLoginClientHelper = sapLoginClientHelper; this.authClient = authClient; } @@ -45,7 +45,7 @@ public LoginClient(final ILoginClientHelper loginClientHelper, final IAuthClient @Override public GetDeploymentsResponse getDeployments(final ServiceKey serviceKey, final String resourceGroup) throws IOException, InterruptedException { - URI endpoint = loginClientHelper.createAuthUri(serviceKey.getServiceURL() + "/lm/deployments"); + URI endpoint = sapLoginClientHelper.createAuthUri(serviceKey.getServiceURL() + "/lm/deployments"); HttpRequest request = HttpRequest.newBuilder() .uri(endpoint) @@ -56,7 +56,7 @@ public GetDeploymentsResponse getDeployments(final ServiceKey serviceKey, final HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - return loginClientHelper.parseDeploymentsResponseToObject(response.body()); + return sapLoginClientHelper.parseDeploymentsResponseToObject(response.body()); } /** @@ -65,7 +65,7 @@ public GetDeploymentsResponse getDeployments(final ServiceKey serviceKey, final @Override public GetResourceGroupsResponse getResourceGroups(final ServiceKey serviceKey) throws IOException, InterruptedException { - URI endpoint = loginClientHelper.createAuthUri(serviceKey.getServiceURL() + "/admin/resourceGroups"); + URI endpoint = sapLoginClientHelper.createAuthUri(serviceKey.getServiceURL() + "/admin/resourceGroups"); HttpRequest request = HttpRequest.newBuilder() .uri(endpoint) @@ -75,6 +75,6 @@ public GetResourceGroupsResponse getResourceGroups(final ServiceKey serviceKey) HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - return loginClientHelper.parseResourceGroupsResponseToObject(response.body()); + return sapLoginClientHelper.parseResourceGroupsResponseToObject(response.body()); } } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/LoginClientHelper.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/SapLoginClientHelper.java similarity index 88% rename from com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/LoginClientHelper.java rename to com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/SapLoginClientHelper.java index 458bf401..0df69a71 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/LoginClientHelper.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/api/SapLoginClientHelper.java @@ -9,9 +9,9 @@ *

* The {@code LoginClientHelper} provides utility methods for creating URIs * and parsing API responses into their corresponding Java objects. - * Implements the {@link ILoginClientHelper} interface. + * Implements the {@link ISapLoginClientHelper} interface. */ -public class LoginClientHelper implements ILoginClientHelper { +public class SapLoginClientHelper implements ISapLoginClientHelper { /** The {@link Gson} instance for parsing JSON responses. */ private Gson gson; @@ -21,7 +21,7 @@ public class LoginClientHelper implements ILoginClientHelper { *

* Initializes a {@link Gson} instance for JSON parsing. */ - public LoginClientHelper() { + public SapLoginClientHelper() { gson = new Gson(); } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/DeploymentSelectionAdapter.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/DeploymentSelectionAdapter.java index 80b68c91..0d9e382d 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/DeploymentSelectionAdapter.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/DeploymentSelectionAdapter.java @@ -5,7 +5,7 @@ import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; -import com.developer.nefarious.zjoule.plugin.login.pages.SecondLoginWizardPage; +import com.developer.nefarious.zjoule.plugin.login.pages.SecondSapLoginWizardPage; import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; import com.developer.nefarious.zjoule.plugin.models.Deployment; @@ -18,7 +18,7 @@ public class DeploymentSelectionAdapter extends SelectionAdapter { /** The second login wizard page associated with this adapter. */ - private SecondLoginWizardPage secondLoginWizardPage; + private SecondSapLoginWizardPage secondLoginWizardPage; /** The memory manager for storing the selected deployment. */ private IMemoryObject memoryDeployment; @@ -26,12 +26,12 @@ public class DeploymentSelectionAdapter extends SelectionAdapter { /** * Constructs a new {@code DeploymentSelectionAdapter}. * - * @param secondLoginWizardPage the {@link SecondLoginWizardPage} containing the deployment dropdown. + * @param secondLoginWizardPage the {@link SecondSapLoginWizardPage} containing the deployment dropdown. * @param memoryDeployment the {@link IMemoryObject} used to store the selected deployment. */ // @formatter:off public DeploymentSelectionAdapter( - final SecondLoginWizardPage secondLoginWizardPage, + final SecondSapLoginWizardPage secondLoginWizardPage, final IMemoryObject memoryDeployment) { // @formatter:on this.secondLoginWizardPage = secondLoginWizardPage; diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/OllamaModelSelectionAdapter.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/OllamaModelSelectionAdapter.java new file mode 100644 index 00000000..057a6044 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/OllamaModelSelectionAdapter.java @@ -0,0 +1,47 @@ +package com.developer.nefarious.zjoule.plugin.login.events; + +import java.util.List; + +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; + +import com.developer.nefarious.zjoule.plugin.login.pages.SecondOllamaLoginWizardPage; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; +import com.developer.nefarious.zjoule.plugin.models.OllamaModel; + +public class OllamaModelSelectionAdapter extends SelectionAdapter { + + private SecondOllamaLoginWizardPage secondOllamaLoginWizardPage; + + private IMemoryObject memoryOllamaModel; + + // @formatter:off + public OllamaModelSelectionAdapter( + final SecondOllamaLoginWizardPage secondOllamaLoginWizardPage, + final IMemoryObject memoryOllamaModel) { + // @formatter:on + this.secondOllamaLoginWizardPage = secondOllamaLoginWizardPage; + this.memoryOllamaModel = memoryOllamaModel; + } + + private OllamaModel getSelectedOllamaModelObject(final String selectedOllamaModelName) { + List ollamaModelForSelection = secondOllamaLoginWizardPage.getOllamaModelsForSelection(); + + return ollamaModelForSelection.stream() + .filter(ollamaModel -> selectedOllamaModelName.equals(ollamaModel.getName())) + .findFirst() + .orElse(null); + } + + @Override + public void widgetSelected(final SelectionEvent e) { + String selectedOllamaModelName = secondOllamaLoginWizardPage.getOllamaModelDropdown().getText(); + if (selectedOllamaModelName.isEmpty()) { + secondOllamaLoginWizardPage.setPageComplete(false); + } else { + OllamaModel selectedOllamaModelObject = getSelectedOllamaModelObject(selectedOllamaModelName); + memoryOllamaModel.save(selectedOllamaModelObject); + secondOllamaLoginWizardPage.setPageComplete(true); + } + } +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/ResourceGroupSelectionAdapter.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/ResourceGroupSelectionAdapter.java index a16c6b8f..adce5f94 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/ResourceGroupSelectionAdapter.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/ResourceGroupSelectionAdapter.java @@ -6,8 +6,8 @@ import org.eclipse.swt.events.SelectionEvent; import com.developer.nefarious.zjoule.plugin.login.api.GetDeploymentsResponse; -import com.developer.nefarious.zjoule.plugin.login.api.ILoginClient; -import com.developer.nefarious.zjoule.plugin.login.pages.SecondLoginWizardPage; +import com.developer.nefarious.zjoule.plugin.login.api.ISapLoginClient; +import com.developer.nefarious.zjoule.plugin.login.pages.SecondSapLoginWizardPage; import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; import com.developer.nefarious.zjoule.plugin.models.ServiceKey; @@ -21,10 +21,10 @@ public class ResourceGroupSelectionAdapter extends SelectionAdapter { /** The second login wizard page associated with this adapter. */ - private SecondLoginWizardPage secondLoginWizardPage; + private SecondSapLoginWizardPage secondLoginWizardPage; /** The login client for retrieving deployments. */ - private ILoginClient loginClient; + private ISapLoginClient sapLoginClient; /** The memory manager for storing the selected resource group. */ private IMemoryObject memoryResourceGroup; @@ -32,18 +32,18 @@ public class ResourceGroupSelectionAdapter extends SelectionAdapter { /** * Constructs a new {@code ResourceGroupSelectionAdapter}. * - * @param secondLoginWizardPage the {@link SecondLoginWizardPage} containing the resource group dropdown. - * @param loginClient the {@link ILoginClient} for retrieving deployments. + * @param secondLoginWizardPage the {@link SecondSapLoginWizardPage} containing the resource group dropdown. + * @param sapLoginClient the {@link ISapLoginClient} for retrieving deployments. * @param memoryResourceGroup the {@link IMemoryObject} used to store the selected resource group. */ // @formatter:off public ResourceGroupSelectionAdapter( - final SecondLoginWizardPage secondLoginWizardPage, - final ILoginClient loginClient, + final SecondSapLoginWizardPage secondLoginWizardPage, + final ISapLoginClient sapLoginClient, final IMemoryObject memoryResourceGroup) { // @formatter:on this.secondLoginWizardPage = secondLoginWizardPage; - this.loginClient = loginClient; + this.sapLoginClient = sapLoginClient; this.memoryResourceGroup = memoryResourceGroup; } @@ -71,7 +71,7 @@ private void enableTheDeploymentSelection() { private void handleAvailableDeployments() throws IOException, InterruptedException { String selectedResourceGroup = secondLoginWizardPage.getResourceGroupDropdown().getText(); ServiceKey serviceKey = secondLoginWizardPage.getServiceKey(); - GetDeploymentsResponse getDeploymentsResponse = loginClient.getDeployments(serviceKey, selectedResourceGroup); + GetDeploymentsResponse getDeploymentsResponse = sapLoginClient.getDeployments(serviceKey, selectedResourceGroup); secondLoginWizardPage.setDeploymentsForSelection(getDeploymentsResponse.getDeployments()); memoryResourceGroup.save(selectedResourceGroup); enableTheDeploymentSelection(); diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/ServiceKeyModifyListener.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/ServiceKeyModifyListener.java index bdad579d..c62695ac 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/ServiceKeyModifyListener.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/events/ServiceKeyModifyListener.java @@ -6,8 +6,8 @@ import org.eclipse.swt.events.ModifyListener; import com.developer.nefarious.zjoule.plugin.login.api.GetResourceGroupsResponse; -import com.developer.nefarious.zjoule.plugin.login.api.ILoginClient; -import com.developer.nefarious.zjoule.plugin.login.pages.FirstLoginWizardPage; +import com.developer.nefarious.zjoule.plugin.login.api.ISapLoginClient; +import com.developer.nefarious.zjoule.plugin.login.pages.FirstSapLoginWizardPage; import com.developer.nefarious.zjoule.plugin.login.utils.JsonValidator; import com.developer.nefarious.zjoule.plugin.models.ServiceKey; import com.google.gson.Gson; @@ -21,10 +21,10 @@ public class ServiceKeyModifyListener implements ModifyListener { /** The first page of the login wizard associated with this listener. */ - private FirstLoginWizardPage firstLoginWizardPage; + private FirstSapLoginWizardPage firstLoginWizardPage; /** The login client used for validating the service key and retrieving resource groups. */ - private ILoginClient loginClient; + private ISapLoginClient sapLoginClient; /** A {@link Gson} instance for parsing the service key from JSON. */ private Gson gson; @@ -32,18 +32,18 @@ public class ServiceKeyModifyListener implements ModifyListener { /** * Constructs a new {@code ServiceKeyModifyListener}. * - * @param firstLoginWizardPage the {@link FirstLoginWizardPage} containing the service key input field. - * @param loginClient the {@link ILoginClient} for validating the service key and retrieving resource groups. + * @param firstLoginWizardPage the {@link FirstSapLoginWizardPage} containing the service key input field. + * @param sapLoginClient the {@link ISapLoginClient} for validating the service key and retrieving resource groups. * @param gson the {@link Gson} instance for parsing the service key JSON. */ // @formatter:off public ServiceKeyModifyListener( - final FirstLoginWizardPage firstLoginWizardPage, - final ILoginClient loginClient, + final FirstSapLoginWizardPage firstLoginWizardPage, + final ISapLoginClient sapLoginClient, final Gson gson) { // @formatter:on this.firstLoginWizardPage = firstLoginWizardPage; - this.loginClient = loginClient; + this.sapLoginClient = sapLoginClient; this.gson = gson; } @@ -76,7 +76,7 @@ private void enableNextButton() { * @throws InterruptedException if the resource group retrieval is interrupted. */ private void handleValidServiceKey(final ServiceKey serviceKey) throws IOException, InterruptedException { - GetResourceGroupsResponse getResourceGroupsResponse = loginClient.getResourceGroups(serviceKey); + GetResourceGroupsResponse getResourceGroupsResponse = sapLoginClient.getResourceGroups(serviceKey); firstLoginWizardPage.setResourceGroupsOnTheSecondPage(getResourceGroupsResponse); firstLoginWizardPage.setServiceKey(serviceKey); clearMessageLog(); diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryAccessToken.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryAccessToken.java index acf8bf37..703cd46a 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryAccessToken.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryAccessToken.java @@ -113,4 +113,10 @@ public void save(final AccessToken accessToken) { String serializedObject = objectSerializer.serialize(accessToken); eclipseMemory.saveOnEclipsePreferences(KEY, serializedObject); } + + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } + } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryDeployment.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryDeployment.java index 38de45b5..f3e8576e 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryDeployment.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryDeployment.java @@ -113,4 +113,10 @@ public void save(final Deployment deployment) { String serializedObject = objectSerializer.serialize(deployment); eclipseMemory.saveOnEclipsePreferences(KEY, serializedObject); } + + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } + } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryOllamaEndpoint.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryOllamaEndpoint.java new file mode 100644 index 00000000..ff704a82 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryOllamaEndpoint.java @@ -0,0 +1,66 @@ +package com.developer.nefarious.zjoule.plugin.login.memory; + +import com.developer.nefarious.zjoule.plugin.memory.IEclipseMemory; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaEndpoint; + +public class TemporaryMemoryOllamaEndpoint implements IMemoryObject, ITemporaryMemoryObject { + + private static TemporaryMemoryOllamaEndpoint instance; + + public static final String KEY = "tmp-" + MemoryOllamaEndpoint.KEY; + + private IEclipseMemory eclipseMemory; + + public static TemporaryMemoryOllamaEndpoint getInstance() { + if (instance == null) { + throw new IllegalStateException("TemporaryMemoryOllamaEndpoint not initialized. Call initialize() first."); + } + return instance; + } + + public static void initialize(final IEclipseMemory eclipseMemory) { + if (instance == null) { + instance = new TemporaryMemoryOllamaEndpoint(eclipseMemory); + } + } + + public static void resetInstance() { + instance = null; + } + + private TemporaryMemoryOllamaEndpoint(final IEclipseMemory eclipseMemory) { + this.eclipseMemory = eclipseMemory; + } + + @Override + public Boolean isEmpty() { + String ollamaEndpoint = load(); + if ((ollamaEndpoint == null) || ollamaEndpoint.isEmpty() || ollamaEndpoint.isBlank()) { + return true; + } + return false; + } + + @Override + public String load() { + return eclipseMemory.loadFromEclipsePreferences(KEY); + } + + @Override + public void persist() { + String ollamaEndpoint = eclipseMemory.loadFromEclipsePreferences(KEY); + eclipseMemory.saveOnEclipsePreferences(MemoryOllamaEndpoint.KEY, ollamaEndpoint); + } + + @Override + public void save(final String ollamaEndpoint) { + eclipseMemory.saveOnEclipsePreferences(KEY, ollamaEndpoint); + } + + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryOllamaModel.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryOllamaModel.java new file mode 100644 index 00000000..04a9a571 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryOllamaModel.java @@ -0,0 +1,122 @@ +package com.developer.nefarious.zjoule.plugin.login.memory; + +import com.developer.nefarious.zjoule.plugin.memory.IEclipseMemory; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaModel; +import com.developer.nefarious.zjoule.plugin.memory.utils.IObjectSerializer; +import com.developer.nefarious.zjoule.plugin.models.OllamaModel; + +/** + * Manages temporary storage and retrieval of ollamaModel information during the login process. + *

+ * The {@code TemporaryMemoryOllamaModel} class provides methods to save, load, and persist + * temporary ollamaModel data using Eclipse preferences. It implements {@link IMemoryObject} + * and {@link ITemporaryMemoryObject}. + */ +public class TemporaryMemoryOllamaModel implements IMemoryObject, ITemporaryMemoryObject { + + /** Singleton instance of {@code TemporaryMemoryOllamaModel}. */ + private static TemporaryMemoryOllamaModel instance; + + /** Key used for storing and retrieving the temporary ollamaModel in Eclipse preferences. */ + public static final String KEY = "tmp-" + MemoryOllamaModel.KEY; + + /** Serializer for converting objects to and from serialized formats. */ + private IObjectSerializer objectSerializer; + + /** Manages interactions with Eclipse's preferences storage. */ + private IEclipseMemory eclipseMemory; + + /** + * Retrieves the singleton instance of {@code TemporaryMemoryOllamaModel}. + * + * @return the singleton instance. + * @throws IllegalStateException if the instance has not been initialized. + */ + public static TemporaryMemoryOllamaModel getInstance() { + if (instance == null) { + throw new IllegalStateException("TemporaryMemoryOllamaModel not initialized. Call initialize() first."); + } + return instance; + } + + /** + * Initializes the {@code TemporaryMemoryOllamaModel} singleton with the specified dependencies. + * + * @param objectSerializer the serializer for handling object serialization and deserialization. + * @param eclipseMemory the manager for Eclipse preferences storage. + */ + public static void initialize(final IObjectSerializer objectSerializer, final IEclipseMemory eclipseMemory) { + if (instance == null) { + instance = new TemporaryMemoryOllamaModel(objectSerializer, eclipseMemory); + } + } + + /** + * Resets the singleton instance. Useful for testing or reinitialization. + */ + public static void resetInstance() { + instance = null; + } + + /** + * Private constructor to enforce singleton behavior. + * + * @param objectSerializer the serializer for handling object serialization and deserialization. + * @param eclipseMemory the manager for Eclipse preferences storage. + */ + private TemporaryMemoryOllamaModel(final IObjectSerializer objectSerializer, final IEclipseMemory eclipseMemory) { + this.objectSerializer = objectSerializer; + this.eclipseMemory = eclipseMemory; + } + + /** + * {@inheritDoc} + */ + @Override + public Boolean isEmpty() { + OllamaModel ollamaModel = load(); + if ((ollamaModel == null) || (ollamaModel.getName() == null) || ollamaModel.getName().isEmpty() + || ollamaModel.getName().isBlank()) { + return true; + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public OllamaModel load() { + try { + String serializedObject = eclipseMemory.loadFromEclipsePreferences(KEY); + return objectSerializer.deserialize(serializedObject, OllamaModel.class); + } catch (Exception e) { + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void persist() { + String serializedObject = eclipseMemory.loadFromEclipsePreferences(KEY); + eclipseMemory.saveOnEclipsePreferences(MemoryOllamaModel.KEY, serializedObject); + } + + /** + * {@inheritDoc} + */ + @Override + public void save(final OllamaModel ollamaModel) { + String serializedObject = objectSerializer.serialize(ollamaModel); + eclipseMemory.saveOnEclipsePreferences(KEY, serializedObject); + } + + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryResourceGroup.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryResourceGroup.java index d739a73d..c39f80e6 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryResourceGroup.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryResourceGroup.java @@ -98,4 +98,10 @@ public void persist() { public void save(final String resourceGroup) { eclipseMemory.saveOnEclipsePreferences(KEY, resourceGroup); } + + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } + } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryServiceKey.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryServiceKey.java index ecdec58f..af4b098b 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryServiceKey.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/memory/TemporaryMemoryServiceKey.java @@ -113,4 +113,10 @@ public void save(final ServiceKey serviceKey) { String serializedObject = objectSerializer.serialize(serviceKey); eclipseMemory.saveOnEclipsePreferences(KEY, serializedObject); } + + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } + } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/FirstOllamaLoginWizardPage.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/FirstOllamaLoginWizardPage.java new file mode 100644 index 00000000..df585271 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/FirstOllamaLoginWizardPage.java @@ -0,0 +1,101 @@ +package com.developer.nefarious.zjoule.plugin.login.pages; + +import java.io.IOException; + +import org.eclipse.jface.wizard.IWizardPage; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +import com.developer.nefarious.zjoule.plugin.login.api.GetOllamaModelsResponse; +import com.developer.nefarious.zjoule.plugin.login.api.IOllamaLoginClient; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; + +public class FirstOllamaLoginWizardPage extends WizardPage { + + public static final String PAGE_ID = "Ollama Login First Page"; + + private IOllamaLoginClient ollamaLoginClient; + + private IMemoryObject memoryOllamaEndpoint; + + private Text endpointText; + + private Text errorText; + + public FirstOllamaLoginWizardPage( + final IOllamaLoginClient ollamaLoginClient, + final IMemoryObject memoryOllamaEndpoint) { + super(PAGE_ID); + setTitle("Ollama Setup"); + setDescription("Enter the host and port for the local Ollama instance."); + setPageComplete(true); + this.ollamaLoginClient = ollamaLoginClient; + this.memoryOllamaEndpoint = memoryOllamaEndpoint; + } + + @Override + public void createControl(final Composite parent) { + Composite container = new Composite(parent, SWT.NONE); + container.setLayout(new GridLayout(2, false)); + + Label inputLabel = new Label(container, SWT.NONE); + inputLabel.setText("Ollama Endpoint:"); + + endpointText = new Text(container, SWT.BORDER); + endpointText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + endpointText.setText("http://localhost:11434"); + + // Hidden error text widget + errorText = new Text(container, SWT.READ_ONLY | SWT.MULTI | SWT.WRAP); + GridData errorTextGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1); + errorText.setLayoutData(errorTextGridData); + errorText.setForeground(container.getDisplay().getSystemColor(SWT.COLOR_RED)); + errorText.setVisible(false); // Initially hidden + + setControl(container); + } + + @Override + public boolean canFlipToNextPage() { + return true; + } + + @Override + public IWizardPage getNextPage() { + + String ollamaEndpoint = endpointText.getText(); + + if (ollamaEndpoint == null || ollamaEndpoint.isEmpty() || ollamaEndpoint.isBlank()) { + displayErrorMessage("Please, enter a local Ollama endpoint to proceed."); + return null; + } + + try { + setAvailableModelsFor(ollamaEndpoint); + } catch (IllegalArgumentException | IOException | InterruptedException e) { + displayErrorMessage("Local instance of Ollama invalid or doesn't exist in the informed address."); + return null; + } + + errorText.setVisible(false); + memoryOllamaEndpoint.save(ollamaEndpoint); + return super.getNextPage(); // Proceed to the next wizard page + } + + private void setAvailableModelsFor(final String ollamaEndpoint) throws IOException, InterruptedException { + GetOllamaModelsResponse getOllamaModelsResponse = ollamaLoginClient.getModels(ollamaEndpoint); + SecondOllamaLoginWizardPage secondPage = (SecondOllamaLoginWizardPage) getWizard().getPage(SecondOllamaLoginWizardPage.PAGE_ID); + secondPage.setOllamaModelsForSelection(getOllamaModelsResponse); + } + + private void displayErrorMessage(final String message) { + errorText.setText(message); + errorText.setVisible(true); + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/FirstLoginWizardPage.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/FirstSapLoginWizardPage.java similarity index 90% rename from com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/FirstLoginWizardPage.java rename to com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/FirstSapLoginWizardPage.java index dcf385a3..a918b94e 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/FirstLoginWizardPage.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/FirstSapLoginWizardPage.java @@ -10,7 +10,7 @@ import org.eclipse.swt.widgets.Text; import com.developer.nefarious.zjoule.plugin.login.api.GetResourceGroupsResponse; -import com.developer.nefarious.zjoule.plugin.login.api.ILoginClient; +import com.developer.nefarious.zjoule.plugin.login.api.ISapLoginClient; import com.developer.nefarious.zjoule.plugin.login.events.ServiceKeyModifyListener; import com.developer.nefarious.zjoule.plugin.login.utils.ResourceGroupIdExtractor; import com.developer.nefarious.zjoule.plugin.models.ServiceKey; @@ -26,7 +26,7 @@ *

  • Logic to propagate resource group information to subsequent wizard pages.
  • * */ -public class FirstLoginWizardPage extends WizardPage { +public class FirstSapLoginWizardPage extends WizardPage { /** The unique identifier for this wizard page. */ public static final String PAGE_ID = "First Page"; @@ -47,19 +47,19 @@ public class FirstLoginWizardPage extends WizardPage { private ServiceKey serviceKey; /** The login client for handling API interactions. */ - private ILoginClient loginClient; + private ISapLoginClient sapLoginClient; /** * Constructs a new {@code FirstLoginWizardPage}. * - * @param loginClient the {@link ILoginClient} used for API interactions during the login process. + * @param sapLoginClient the {@link ISapLoginClient} used for API interactions during the login process. */ - public FirstLoginWizardPage(final ILoginClient loginClient) { + public FirstSapLoginWizardPage(final ISapLoginClient sapLoginClient) { super(PAGE_ID); setTitle("Provide credentials"); setDescription("Attach the Service Key json file content for the SAP AI Core service."); setPageComplete(false); // Initially set the page as incomplete - this.loginClient = loginClient; + this.sapLoginClient = sapLoginClient; } /** @@ -80,7 +80,7 @@ public void createControl(final Composite parent) { textField.setLayoutData(gridData); // Add a ModifyListener to monitor textField changes - textField.addModifyListener(new ServiceKeyModifyListener(this, loginClient, new Gson())); + textField.addModifyListener(new ServiceKeyModifyListener(this, sapLoginClient, new Gson())); // Hidden error text widget errorText = new Text(container, SWT.READ_ONLY | SWT.MULTI | SWT.WRAP); @@ -116,7 +116,7 @@ public ServiceKey getServiceKey() { * @param getResourceGroupsResponse the response containing available resource groups. */ public void setResourceGroupsOnTheSecondPage(final GetResourceGroupsResponse getResourceGroupsResponse) { - SecondLoginWizardPage secondPage = (SecondLoginWizardPage) getWizard().getPage(SecondLoginWizardPage.PAGE_ID); + SecondSapLoginWizardPage secondPage = (SecondSapLoginWizardPage) getWizard().getPage(SecondSapLoginWizardPage.PAGE_ID); List resourceGroupsAvailableForSelection = ResourceGroupIdExtractor.extractResourceGroupIds(getResourceGroupsResponse); secondPage.setResourceGroupsForSelection(resourceGroupsAvailableForSelection); } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/LoginOptionsPage.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/LoginOptionsPage.java new file mode 100644 index 00000000..4a097341 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/LoginOptionsPage.java @@ -0,0 +1,53 @@ +package com.developer.nefarious.zjoule.plugin.login.pages; + +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; + +public class LoginOptionsPage extends WizardPage { + + public static final String PAGE_ID = "Login Options"; + + private Button option1; + + private Button option2; + + public LoginOptionsPage() { + super(PAGE_ID); + setTitle("Login Options"); + setDescription("Choose the AI provider."); + setPageComplete(false); // Initially set the page as incomplete + } + + public boolean isOption1Selected() { + return option1.getSelection(); + } + + public boolean isOption2Selected() { + return option2.getSelection(); + } + + @Override + public void createControl(final Composite parent) { + Composite container = new Composite(parent, SWT.NONE); + container.setLayout(new GridLayout(1, false)); + + option1 = new Button(container, SWT.RADIO); + option1.setText("SAP AI Core"); + option1.setToolTipText("Select model from the SAP AI Core Generative AI Hub."); + option1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + option1.addListener(SWT.Selection, event -> setPageComplete(true)); + + option2 = new Button(container, SWT.RADIO); + option2.setText("Ollama (Local)"); + option2.setToolTipText("Select a local Ollama model."); + option2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + option2.addListener(SWT.Selection, event -> setPageComplete(true)); + + setControl(container); + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/SecondOllamaLoginWizardPage.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/SecondOllamaLoginWizardPage.java new file mode 100644 index 00000000..09a573e5 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/SecondOllamaLoginWizardPage.java @@ -0,0 +1,77 @@ +package com.developer.nefarious.zjoule.plugin.login.pages; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +import com.developer.nefarious.zjoule.plugin.login.api.GetOllamaModelsResponse; +import com.developer.nefarious.zjoule.plugin.login.events.OllamaModelSelectionAdapter; +import com.developer.nefarious.zjoule.plugin.login.utils.OllamaModelNamesExtractor; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; +import com.developer.nefarious.zjoule.plugin.models.OllamaModel; + +public class SecondOllamaLoginWizardPage extends WizardPage { + + public static final String PAGE_ID = "Ollama Login Second Page"; + + private Combo ollamaModelDropdown; + + private List ollamaModelsForSelection = new ArrayList<>(); + + private IMemoryObject memoryOllamaModel; + + public SecondOllamaLoginWizardPage(final IMemoryObject memoryOllamaModel) { + super(PAGE_ID); + setTitle("Ollama Setup"); + setDescription("Select the Ollama model."); + setPageComplete(false); // Initially set the page as incomplete + this.memoryOllamaModel = memoryOllamaModel; + } + + @Override + public void createControl(final Composite parent) { + Composite container = new Composite(parent, SWT.NONE); + container.setLayout(new GridLayout(2, false)); + + Label modelLabel = new Label(container, SWT.NONE); + modelLabel.setText("Select the Model:"); + + ollamaModelDropdown = new Combo(container, SWT.DROP_DOWN | SWT.READ_ONLY); + ollamaModelDropdown.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + ollamaModelDropdown.addSelectionListener(new OllamaModelSelectionAdapter(this, memoryOllamaModel)); + + setControl(container); + } + + public List getOllamaModelsForSelection() { + return ollamaModelsForSelection; + } + + public void setOllamaModelsForSelection(final GetOllamaModelsResponse getOllamaModelsResponse) { + this.ollamaModelsForSelection = getOllamaModelsResponse.getModels(); + List ollamaModelNames = OllamaModelNamesExtractor.extractModelNames(getOllamaModelsResponse); + ollamaModelDropdown.setItems(ollamaModelNames.toArray(new String[0])); + } + + public Combo getOllamaModelDropdown() { + return ollamaModelDropdown; + } + + @Override + public void setVisible(final boolean visible) { + super.setVisible(visible); + if (!visible) { + ollamaModelDropdown.deselectAll(); + setPageComplete(false); + } + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/SecondLoginWizardPage.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/SecondSapLoginWizardPage.java similarity index 88% rename from com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/SecondLoginWizardPage.java rename to com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/SecondSapLoginWizardPage.java index f2421c6a..1c628dc8 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/SecondLoginWizardPage.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/pages/SecondSapLoginWizardPage.java @@ -11,7 +11,7 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; -import com.developer.nefarious.zjoule.plugin.login.api.ILoginClient; +import com.developer.nefarious.zjoule.plugin.login.api.ISapLoginClient; import com.developer.nefarious.zjoule.plugin.login.events.DeploymentSelectionAdapter; import com.developer.nefarious.zjoule.plugin.login.events.ResourceGroupSelectionAdapter; import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; @@ -28,9 +28,9 @@ *
  • A dropdown for selecting a resource group.
  • *
  • A dropdown for selecting a deployment ID (enabled after selecting a resource group).
  • * - * It also communicates with {@link FirstLoginWizardPage} to retrieve service key data. + * It also communicates with {@link FirstSapLoginWizardPage} to retrieve service key data. */ -public class SecondLoginWizardPage extends WizardPage { +public class SecondSapLoginWizardPage extends WizardPage { /** The unique identifier for this wizard page. */ public static final String PAGE_ID = "Second Page"; @@ -48,7 +48,7 @@ public class SecondLoginWizardPage extends WizardPage { private List deploymentsForSelection = new ArrayList<>(); /** Client for handling API interactions. */ - private ILoginClient loginClient; + private ISapLoginClient sapLoginClient; /** Memory interface for managing resource group data. */ private IMemoryObject memoryResourceGroup; @@ -59,13 +59,13 @@ public class SecondLoginWizardPage extends WizardPage { /** * Constructs a new {@code SecondLoginWizardPage}. * - * @param loginClient the {@link ILoginClient} used for API interactions during the login process. + * @param sapLoginClient the {@link ISapLoginClient} used for API interactions during the login process. * @param memoryResourceGroup the {@link IMemoryObject} for resource group memory management. * @param memoryDeployment the {@link IMemoryObject} for deployment memory management. */ // @formatter:off - public SecondLoginWizardPage( - final ILoginClient loginClient, + public SecondSapLoginWizardPage( + final ISapLoginClient sapLoginClient, final IMemoryObject memoryResourceGroup, final IMemoryObject memoryDeployment) { // @formatter:on @@ -73,7 +73,7 @@ public SecondLoginWizardPage( setTitle("Select the model"); setDescription("Choose the Resource Group and the Deployment ID."); setPageComplete(false); // Initially set the page as incomplete - this.loginClient = loginClient; + this.sapLoginClient = sapLoginClient; this.memoryResourceGroup = memoryResourceGroup; this.memoryDeployment = memoryDeployment; } @@ -96,8 +96,8 @@ public void createControl(final Composite parent) { resourceGroupDropdown.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); // Add a SelectionListener to enable the deployment dropdown when a valid resource group is selected - resourceGroupDropdown - .addSelectionListener(new ResourceGroupSelectionAdapter(this, loginClient, memoryResourceGroup)); + resourceGroupDropdown.addSelectionListener( + new ResourceGroupSelectionAdapter(this, sapLoginClient, memoryResourceGroup)); // Create label and dropdown for deployment ID selection Label deploymentLabel = new Label(container, SWT.NONE); @@ -146,7 +146,7 @@ public Combo getResourceGroupDropdown() { * @return the {@link ServiceKey} provided on the first page. */ public ServiceKey getServiceKey() { - FirstLoginWizardPage firstPage = (FirstLoginWizardPage) getWizard().getPage(FirstLoginWizardPage.PAGE_ID); + FirstSapLoginWizardPage firstPage = (FirstSapLoginWizardPage) getWizard().getPage(FirstSapLoginWizardPage.PAGE_ID); return firstPage.getServiceKey(); } @@ -181,7 +181,7 @@ public void setVisible(final boolean visible) { super.setVisible(visible); if (visible) { // Retrieve data from the first page - FirstLoginWizardPage firstPage = (FirstLoginWizardPage) getWizard().getPage(FirstLoginWizardPage.PAGE_ID); + FirstSapLoginWizardPage firstPage = (FirstSapLoginWizardPage) getWizard().getPage(FirstSapLoginWizardPage.PAGE_ID); String data = firstPage.getInputText(); // Dynamically populate resource group dropdown based on first page's data diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/utils/OllamaModelNamesExtractor.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/utils/OllamaModelNamesExtractor.java new file mode 100644 index 00000000..fe2d0903 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/login/utils/OllamaModelNamesExtractor.java @@ -0,0 +1,38 @@ +package com.developer.nefarious.zjoule.plugin.login.utils; + +import java.util.ArrayList; +import java.util.List; + +import com.developer.nefarious.zjoule.plugin.login.api.GetOllamaModelsResponse; +import com.developer.nefarious.zjoule.plugin.models.OllamaModel; + +public abstract class OllamaModelNamesExtractor { + + public static List extractModelNames(final GetOllamaModelsResponse response) { + // Step 0: Check if response is null + if (response == null) { + return null; + } + + // Step 1: Get the list of Model objects from the response + List ollamaModels = response.getModels(); + + // Step 2: Create an ArrayList to store the model names + List ollamaModelNames = new ArrayList<>(); + + // Step 3 & 4: Loop through each Model and extract its name + for (OllamaModel ollamaModel : ollamaModels) { + String ollamaModelName = ollamaModel.getName(); + ollamaModelNames.add(ollamaModelName); + } + + // Return the list of model names + return ollamaModelNames; + } + + /** + * Private constructor to prevent instantiation of this utility class. + */ + private OllamaModelNamesExtractor() { } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/IMemoryObject.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/IMemoryObject.java index 7eecc49f..bf99c63b 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/IMemoryObject.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/IMemoryObject.java @@ -30,4 +30,6 @@ public interface IMemoryObject { * @param data the object of type {@code T} to save. */ void save(final T data); + + void clear(); } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryAccessToken.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryAccessToken.java index fee2d975..cfdf4ac0 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryAccessToken.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryAccessToken.java @@ -100,4 +100,9 @@ public void save(final AccessToken accesstoken) { String serializedObject = objectSerializer.serialize(accesstoken); eclipseMemory.saveOnEclipsePreferences(KEY, serializedObject); } + + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryDeployment.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryDeployment.java index 982bac96..04f30c5e 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryDeployment.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryDeployment.java @@ -100,4 +100,9 @@ public void save(final Deployment deployment) { String serializedObject = objectSerializer.serialize(deployment); eclipseMemory.saveOnEclipsePreferences(KEY, serializedObject); } + + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryOllamaEndpoint.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryOllamaEndpoint.java new file mode 100644 index 00000000..6486904d --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryOllamaEndpoint.java @@ -0,0 +1,56 @@ +package com.developer.nefarious.zjoule.plugin.memory; + +public class MemoryOllamaEndpoint implements IMemoryObject { + + public static final String KEY = "ollama-endpoint"; + + private static MemoryOllamaEndpoint instance; + + private IEclipseMemory eclipseMemory; + + public static MemoryOllamaEndpoint getInstance() { + if (instance == null) { + throw new IllegalStateException("MemoryOllamaEndpoint not initialized. Call initialize() first."); + } + return instance; + } + + public static void initialize(final IEclipseMemory eclipseMemory) { + if (instance == null) { + instance = new MemoryOllamaEndpoint(eclipseMemory); + } + } + + public static void resetInstance() { + instance = null; + } + + private MemoryOllamaEndpoint(final IEclipseMemory eclipseMemory) { + this.eclipseMemory = eclipseMemory; + } + + @Override + public Boolean isEmpty() { + String ollamaEndpoint = load(); + if ((ollamaEndpoint == null) || ollamaEndpoint.isEmpty() || ollamaEndpoint.isBlank()) { + return true; + } + return false; + } + + @Override + public String load() { + return eclipseMemory.loadFromEclipsePreferences(KEY); + } + + @Override + public void save(final String ollamaEndpoint) { + eclipseMemory.saveOnEclipsePreferences(KEY, ollamaEndpoint); + } + + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } + +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryOllamaModel.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryOllamaModel.java new file mode 100644 index 00000000..14a68529 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryOllamaModel.java @@ -0,0 +1,108 @@ +package com.developer.nefarious.zjoule.plugin.memory; + +import com.developer.nefarious.zjoule.plugin.memory.utils.IObjectSerializer; +import com.developer.nefarious.zjoule.plugin.models.OllamaModel; + +/** + * Manages the storage and retrieval of ollamaModel information in memory. + *

    + * The {@code MemoryOllamaModel} class provides methods to save, load, and check + * the validity of ollamaModel data stored in Eclipse preferences. It implements {@link IMemoryObject}. + */ +public class MemoryOllamaModel implements IMemoryObject { + + /** Key used for storing and retrieving ollamaModel information in memory. */ + public static final String KEY = "ollama-model"; + + /** Singleton instance of {@code MemoryOllamaModel}. */ + private static MemoryOllamaModel instance; + + /** Serializer for converting objects to and from serialized formats. */ + private IObjectSerializer objectSerializer; + + /** Manages interactions with Eclipse's preferences storage. */ + private IEclipseMemory eclipseMemory; + + /** + * Retrieves the singleton instance of {@code MemoryOllamaModel}. + * + * @return the singleton instance. + * @throws IllegalStateException if the instance has not been initialized. + */ + public static MemoryOllamaModel getInstance() { + if (instance == null) { + throw new IllegalStateException("MemoryOllamaModel not initialized. Call initialize() first."); + } + return instance; + } + + /** + * Initializes the {@code MemoryOllamaModel} singleton with the specified dependencies. + * + * @param objectSerializer the serializer for handling object serialization and deserialization. + * @param eclipseMemory the manager for Eclipse preferences storage. + */ + public static void initialize(final IObjectSerializer objectSerializer, final IEclipseMemory eclipseMemory) { + if (instance == null) { + instance = new MemoryOllamaModel(objectSerializer, eclipseMemory); + } + } + + /** + * Resets the singleton instance. Useful for testing or reinitialization. + */ + public static void resetInstance() { + instance = null; + } + + /** + * Private constructor to enforce singleton behavior. + * + * @param objectSerializer the serializer for handling object serialization and deserialization. + * @param eclipseMemory the manager for Eclipse preferences storage. + */ + private MemoryOllamaModel(final IObjectSerializer objectSerializer, final IEclipseMemory eclipseMemory) { + this.objectSerializer = objectSerializer; + this.eclipseMemory = eclipseMemory; + } + + /** + * {@inheritDoc} + */ + @Override + public Boolean isEmpty() { + OllamaModel ollamaModel = load(); + if ((ollamaModel == null) || (ollamaModel.getName() == null) || ollamaModel.getName().isEmpty() + || ollamaModel.getName().isBlank()) { + return true; + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public OllamaModel load() { + try { + String serializedObject = eclipseMemory.loadFromEclipsePreferences(KEY); + return objectSerializer.deserialize(serializedObject, OllamaModel.class); + } catch (Exception e) { + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void save(final OllamaModel ollamaModel) { + String serializedObject = objectSerializer.serialize(ollamaModel); + eclipseMemory.saveOnEclipsePreferences(KEY, serializedObject); + } + + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } +} diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryResourceGroup.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryResourceGroup.java index 60064704..5b83c465 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryResourceGroup.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryResourceGroup.java @@ -85,4 +85,9 @@ public void save(final String resourceGroup) { eclipseMemory.saveOnEclipsePreferences(KEY, resourceGroup); } + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } + } \ No newline at end of file diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryServiceKey.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryServiceKey.java index 31e4850f..1bd46f85 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryServiceKey.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/memory/MemoryServiceKey.java @@ -100,4 +100,9 @@ public void save(final ServiceKey serviceKey) { String serializedObject = objectSerializer.serialize(serviceKey); eclipseMemory.saveOnEclipsePreferences(KEY, serializedObject); } + + @Override + public void clear() { + eclipseMemory.deleteFromEclipsePreferences(KEY); + } } diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/models/Deployment.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/models/Deployment.java index d56063e8..272293f4 100644 --- a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/models/Deployment.java +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/models/Deployment.java @@ -173,6 +173,15 @@ public void setTargetStatus(final String targetStatus) { public String getModelName() { return getDetails().getResources().get("backendDetails").getModel().getName(); } + + /** + * Retrieves the version of the model associated with this deployment. + * + * @return the version of the model. + */ + public String getModelVersion() { + return getDetails().getResources().get("backendDetails").getModel().getVersion(); + } } /** diff --git a/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/models/OllamaModel.java b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/models/OllamaModel.java new file mode 100644 index 00000000..734a0fe9 --- /dev/null +++ b/com.developer.nefarious.zjoule.plugin/src/com/developer/nefarious/zjoule/plugin/models/OllamaModel.java @@ -0,0 +1,154 @@ +package com.developer.nefarious.zjoule.plugin.models; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +public class OllamaModel { + + private String name; + + private String model; + + @SerializedName("modified_at") + private String modifiedAt; + + private long size; + + private String digest; + + private OllamaModelDetails details; + + // Getters and Setters + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getModel() { + return model; + } + + public void setModel(final String model) { + this.model = model; + } + + public String getModifiedAt() { + return modifiedAt; + } + + public void setModifiedAt(final String modifiedAt) { + this.modifiedAt = modifiedAt; + } + + public long getSize() { + return size; + } + + public void setSize(final long size) { + this.size = size; + } + + public String getDigest() { + return digest; + } + + public void setDigest(final String digest) { + this.digest = digest; + } + + public OllamaModelDetails getDetails() { + return details; + } + + public void setDetails(final OllamaModelDetails details) { + this.details = details; + } + + public String getFormat() { + return details.getFormat(); + } + + public String getFamily() { + return details.getFamily(); + } + + public String getParameterSize() { + return details.getParameterSize(); + } + + public String getQuantizationLevel() { + return details.getQuantizationLevel(); + } + +} + +class OllamaModelDetails { + + @SerializedName("parent_model") + private String parentModel; + + private String format; + + private String family; + + private List families; + + @SerializedName("parameter_size") + private String parameterSize; + + @SerializedName("quantization_level") + private String quantizationLevel; + + // Getters and Setters + public String getParentModel() { + return parentModel; + } + + public void setParentModel(final String parentModel) { + this.parentModel = parentModel; + } + + public String getFormat() { + return format; + } + + public void setFormat(final String format) { + this.format = format; + } + + public String getFamily() { + return family; + } + + public void setFamily(final String family) { + this.family = family; + } + + public List getFamilies() { + return families; + } + + public void setFamilies(final List families) { + this.families = families; + } + + public String getParameterSize() { + return parameterSize; + } + + public void setParameterSize(final String parameterSize) { + this.parameterSize = parameterSize; + } + + public String getQuantizationLevel() { + return quantizationLevel; + } + + public void setQuantizationLevel(final String quantizationLevel) { + this.quantizationLevel = quantizationLevel; + } +} diff --git a/com.developer.nefarious.zjoule.test/.project b/com.developer.nefarious.zjoule.test/.project index a5d4ff68..eed56845 100644 --- a/com.developer.nefarious.zjoule.test/.project +++ b/com.developer.nefarious.zjoule.test/.project @@ -31,4 +31,15 @@ org.eclipse.pde.PluginNature org.eclipse.jdt.core.javanature + + + 1737318973870 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AiClientFactoryOllamaTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AiClientFactoryOllamaTest.java new file mode 100644 index 00000000..9c04783b --- /dev/null +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AiClientFactoryOllamaTest.java @@ -0,0 +1,67 @@ +package com.developer.nefarious.zjoule.test.chat; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mockStatic; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; + +import com.developer.nefarious.zjoule.plugin.auth.SessionManager; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaEndpoint; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaModel; + +public class AiClientFactoryOllamaTest { + + private MockedStatic mockStaticSessionManager; + + private MockedStatic mockStaticMemoryOllamaEndpoint; + + private MockedStatic mockStaticMemoryOllamaModel; + + @Mock + private MemoryOllamaEndpoint memoryOllamaEndpoint; + + @Mock + private MemoryOllamaModel memoryOllamaModel; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + + mockStaticSessionManager = mockStatic(SessionManager.class); + mockStaticMemoryOllamaEndpoint = mockStatic(MemoryOllamaEndpoint.class); + mockStaticMemoryOllamaModel = mockStatic(MemoryOllamaModel.class); + + mockStaticMemoryOllamaEndpoint.when(MemoryOllamaEndpoint::getInstance).thenReturn(memoryOllamaEndpoint); + mockStaticMemoryOllamaModel.when(MemoryOllamaModel::getInstance).thenReturn(memoryOllamaModel); + + mockStaticSessionManager.when(SessionManager::isOllamaSession).thenReturn(true); + mockStaticSessionManager.when(SessionManager::isSapSession).thenReturn(false); + } + + @AfterEach + public void tearDown() { + if (mockStaticSessionManager != null) { + mockStaticSessionManager.close(); + } + if (mockStaticMemoryOllamaEndpoint != null) { + mockStaticMemoryOllamaEndpoint.close(); + } + if (mockStaticMemoryOllamaModel != null) { + mockStaticMemoryOllamaModel.close(); + } + } + + @Test + public void shouldReturnNullForOthers() { + // Arrange + // Act + // Assert + assertTrue(true); + } + +} diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AiClientFactoryOthersTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AiClientFactoryOthersTest.java new file mode 100644 index 00000000..81d7e262 --- /dev/null +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AiClientFactoryOthersTest.java @@ -0,0 +1,70 @@ +package com.developer.nefarious.zjoule.test.chat; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.mockStatic; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; + +import com.developer.nefarious.zjoule.plugin.auth.SessionManager; +import com.developer.nefarious.zjoule.plugin.chat.AIClientFactory; +import com.developer.nefarious.zjoule.plugin.chat.IAIClient; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaEndpoint; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaModel; + +public class AiClientFactoryOthersTest { + + private MockedStatic mockStaticSessionManager; + + private MockedStatic mockStaticMemoryOllamaEndpoint; + + private MockedStatic mockStaticMemoryOllamaModel; + + @Mock + private MemoryOllamaEndpoint memoryOllamaEndpoint; + + @Mock + private MemoryOllamaModel memoryOllamaModel; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + + mockStaticSessionManager = mockStatic(SessionManager.class); + mockStaticMemoryOllamaEndpoint = mockStatic(MemoryOllamaEndpoint.class); + mockStaticMemoryOllamaModel = mockStatic(MemoryOllamaModel.class); + + mockStaticMemoryOllamaEndpoint.when(MemoryOllamaEndpoint::getInstance).thenReturn(memoryOllamaEndpoint); + mockStaticMemoryOllamaModel.when(MemoryOllamaModel::getInstance).thenReturn(memoryOllamaModel); + + mockStaticSessionManager.when(SessionManager::isOllamaSession).thenReturn(false); + mockStaticSessionManager.when(SessionManager::isSapSession).thenReturn(false); + } + + @AfterEach + public void tearDown() { + if (mockStaticSessionManager != null) { + mockStaticSessionManager.close(); + } + if (mockStaticMemoryOllamaEndpoint != null) { + mockStaticMemoryOllamaEndpoint.close(); + } + if (mockStaticMemoryOllamaModel != null) { + mockStaticMemoryOllamaModel.close(); + } + } + + @Test + public void shouldReturnNullForOthers() { + // Arrange + // Act + IAIClient returnValue = AIClientFactory.getClient(); + // Assert + assertNull(returnValue); + } + +} diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AIClientFactoryTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AiClientFactorySapTest.java similarity index 90% rename from com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AIClientFactoryTest.java rename to com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AiClientFactorySapTest.java index 26fa5f2e..2733bebc 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AIClientFactoryTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/AiClientFactorySapTest.java @@ -12,6 +12,7 @@ import org.mockito.MockedStatic; import org.mockito.MockitoAnnotations; +import com.developer.nefarious.zjoule.plugin.auth.SessionManager; import com.developer.nefarious.zjoule.plugin.chat.AIClientFactory; import com.developer.nefarious.zjoule.plugin.chat.IAIClient; import com.developer.nefarious.zjoule.plugin.chat.memory.MemoryMessageHistory; @@ -22,8 +23,10 @@ import com.developer.nefarious.zjoule.plugin.memory.MemoryServiceKey; import com.developer.nefarious.zjoule.plugin.models.Deployment; -public class AIClientFactoryTest { - +public class AiClientFactorySapTest { + + private MockedStatic mockStaticSessionManager; + private MockedStatic mockStaticMemoryAccessToken; private MockedStatic mockStaticMemoryServiceKey; @@ -61,12 +64,15 @@ public void setUp() { mockStaticMemoryResourceGroup = mockStatic(MemoryResourceGroup.class); mockStaticMemoryDeployment = mockStatic(MemoryDeployment.class); mockStaticMemoryMessageHistory = mockStatic(MemoryMessageHistory.class); + mockStaticSessionManager = mockStatic(SessionManager.class); mockStaticMemoryAccessToken.when(MemoryAccessToken::getInstance).thenReturn(mockMemoryAccessToken); mockStaticMemoryServiceKey.when(MemoryServiceKey::getInstance).thenReturn(mockMemoryServiceKey); mockStaticMemoryResourceGroup.when(MemoryResourceGroup::getInstance).thenReturn(mockMemoryResourceGroup); mockStaticMemoryDeployment.when(MemoryDeployment::getInstance).thenReturn(mockMemoryDeployment); mockStaticMemoryMessageHistory.when(MemoryMessageHistory::getInstance).thenReturn(mockMemoryMessageHistory); + mockStaticSessionManager.when(SessionManager::isSapSession).thenReturn(true); + mockStaticSessionManager.when(SessionManager::isOllamaSession).thenReturn(false); when(mockMemoryDeployment.load()).thenReturn(mockDeployment); } @@ -138,6 +144,9 @@ public void tearDown() { if (mockStaticMemoryMessageHistory != null) { mockStaticMemoryMessageHistory.close(); } + if (mockStaticSessionManager != null) { + mockStaticSessionManager.close(); + } } } diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/ChatOrchestratorTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/ChatOrchestratorTest.java index 536347c7..7d11c267 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/ChatOrchestratorTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/ChatOrchestratorTest.java @@ -25,8 +25,8 @@ import com.developer.nefarious.zjoule.plugin.chat.IAIClient; import com.developer.nefarious.zjoule.plugin.chat.IChatMessage; import com.developer.nefarious.zjoule.plugin.chat.IChatOrchestrator; -import com.developer.nefarious.zjoule.plugin.chat.Instruction; import com.developer.nefarious.zjoule.plugin.chat.utils.EditorContentReader; +import com.developer.nefarious.zjoule.plugin.core.preferences.Instruction; import com.developer.nefarious.zjoule.plugin.models.Role; public class ChatOrchestratorTest { diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/ollama/OllamaClientTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/ollama/OllamaClientTest.java new file mode 100644 index 00000000..1efa2adc --- /dev/null +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/ollama/OllamaClientTest.java @@ -0,0 +1,212 @@ +package com.developer.nefarious.zjoule.test.chat.ollama; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpResponse; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; + +import com.developer.nefarious.zjoule.plugin.chat.IChatMessage; +import com.developer.nefarious.zjoule.plugin.chat.memory.IMemoryMessageHistory; +import com.developer.nefarious.zjoule.plugin.chat.models.Message; +import com.developer.nefarious.zjoule.plugin.chat.models.MessageHistory; +import com.developer.nefarious.zjoule.plugin.chat.ollama.IOllamaClientHelper; +import com.developer.nefarious.zjoule.plugin.chat.ollama.OllamaChatMessage; +import com.developer.nefarious.zjoule.plugin.chat.ollama.OllamaClient; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; +import com.developer.nefarious.zjoule.plugin.models.Role; + +public class OllamaClientTest { + + private OllamaClient cut; + + MockedStatic mockStaticHttpClient; + + MockedStatic mockHttpRequest; + + MockedStatic mockURI; + + @Mock + private HttpClient mockHttpClient; + + @Mock + private IMemoryObject mockMemoryOllamaEndpoint; + + @Mock + private IMemoryMessageHistory mockMemoryMessageHistory; + + @Mock + private IOllamaClientHelper mockOllamaClientHelper; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + + mockHttpRequest = mockStatic(HttpRequest.class); + mockURI = mockStatic(URI.class); + mockStaticHttpClient = mockStatic(HttpClient.class); + + mockStaticHttpClient.when(HttpClient::newHttpClient).thenReturn(mockHttpClient); + + // @formatter:off + cut = new OllamaClient( + mockMemoryMessageHistory, + mockMemoryOllamaEndpoint, + mockOllamaClientHelper); + // @formatter:on + } + + @Test + public void shouldCreateMessage() { + // Arrange + String expectedValue = "some-random-user-prompt"; + Role randomRole = Role.USER; + // Act + IChatMessage returnValue = cut.createMessage(randomRole, expectedValue); + // Assert + assertEquals(returnValue.getMessage(), expectedValue); + assertInstanceOf(OllamaChatMessage.class, returnValue); + } + + @Test + public void shouldGetMessageHistory() { + // Arrange + Role expectedRole1 = Role.ASSISTANT; + Role expectedRole2 = Role.USER; + String expectedMessageContent1 = "value-1"; + String expectedMessageContent2 = "value-2"; + + MessageHistory mockMessageHistory = new MessageHistory(); + mockMessageHistory.setMessages(Arrays.asList( + new Message(expectedRole1, expectedMessageContent1), + new Message(expectedRole2, expectedMessageContent2))); + + when(mockMemoryMessageHistory.load()).thenReturn(mockMessageHistory); + + // Act + List returnValue = cut.getMessageHistory(); + + // Assert + IChatMessage returnMessage1 = returnValue.getFirst(); + assertEquals(returnMessage1.getRole(), expectedRole1); + assertEquals(returnMessage1.getMessage(), expectedMessageContent1); + + IChatMessage returnMessage2 = returnValue.getLast(); + assertEquals(returnMessage2.getRole(), expectedRole2); + assertEquals(returnMessage2.getMessage(), expectedMessageContent2); + } + + @Test + public void shouldNotThrowErrorIfThereIsNoMessageHistory() { + // Arrange + when(mockMemoryMessageHistory.load()).thenReturn(null); + // Act + List returnValue = cut.getMessageHistory(); + // Assert + assertTrue(returnValue.isEmpty()); + } + + // CHECKSTYLE:OFF: MethodLength + @Test + public void shouldPlumbChatCompletion() throws IOException, InterruptedException { + // CHECKSTYLE:ON + // Arrange + HttpRequest.Builder mockHttpRequestBuilder = mock(HttpRequest.Builder.class); + mockHttpRequest.when(HttpRequest::newBuilder).thenReturn(mockHttpRequestBuilder); + + String mockEndpoint = "https://something.com"; + when(mockMemoryOllamaEndpoint.load()).thenReturn(mockEndpoint); + + String mockEndpointInStringFormat = mockEndpoint + "/api/chat"; + URI mockEndpointInURIFormat = mock(URI.class); + mockURI.when(() -> URI.create(mockEndpointInStringFormat)).thenReturn(mockEndpointInURIFormat); + + List messages = mock(List.class); + BodyPublisher mockRequestBody = mock(BodyPublisher.class); + when(mockOllamaClientHelper.createRequestBody(messages)).thenReturn(mockRequestBody); + + HttpRequest mockHttpRequest = mock(HttpRequest.class); + when(mockHttpRequestBuilder.build()).thenReturn(mockHttpRequest); + + HttpResponse mockHttpResponse = mock(HttpResponse.class); + when(mockHttpClient.send(eq(mockHttpRequest), eq(HttpResponse.BodyHandlers.ofString()))).thenReturn(mockHttpResponse); + + String mockResponseBody = "response-body-in-string-format"; + when(mockHttpResponse.body()).thenReturn(mockResponseBody); + + OllamaChatMessage expectedValue = mock(OllamaChatMessage.class); + when(mockOllamaClientHelper.convertResponseToObject(mockResponseBody)).thenReturn(expectedValue); + + when(mockHttpRequestBuilder.uri(any())).thenReturn(mockHttpRequestBuilder); + when(mockHttpRequestBuilder.POST(any())).thenReturn(mockHttpRequestBuilder); + + // Act + IChatMessage returnValue = cut.chatCompletion(messages); + + // Assert + verify(mockHttpRequestBuilder).uri(mockEndpointInURIFormat); + verify(mockHttpRequestBuilder).POST(mockRequestBody); + assertEquals(returnValue, expectedValue); + } + // CHECKSTYLE:ON: MethodLength + + @Test + public void shouldSetMessageHistory() { + // Arrange + Role expectedRole1 = Role.ASSISTANT; + Role expectedRole2 = Role.USER; + String expectedMessageContent1 = "value-1"; + String expectedMessageContent2 = "value-2"; + + List mockChatMessages = Arrays.asList( + new OllamaChatMessage(expectedRole1, expectedMessageContent1), + new OllamaChatMessage(expectedRole2, expectedMessageContent2)); + // Act + cut.setMessageHistory(mockChatMessages); + // Assert + ArgumentCaptor captor = ArgumentCaptor.forClass(MessageHistory.class); + verify(mockMemoryMessageHistory).save(captor.capture()); + + List capturedMessages = captor.getValue().getMessages(); + assertEquals(2, capturedMessages.size()); + assertEquals(expectedRole1, capturedMessages.get(0).getRole()); + assertEquals(expectedRole2, capturedMessages.get(1).getRole()); + assertEquals(expectedMessageContent1, capturedMessages.get(0).getContent()); + assertEquals(expectedMessageContent2, capturedMessages.get(1).getContent()); + + } + + @AfterEach + public void tearDown() { + if (mockHttpRequest != null) { + mockHttpRequest.close(); + } + if (mockURI != null) { + mockURI.close(); + } + if (mockStaticHttpClient != null) { + mockStaticHttpClient.close(); + } + } +} diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/ollama/OllamaRequestBodyTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/ollama/OllamaRequestBodyTest.java new file mode 100644 index 00000000..5aa0b96e --- /dev/null +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/chat/ollama/OllamaRequestBodyTest.java @@ -0,0 +1,81 @@ +package com.developer.nefarious.zjoule.test.chat.ollama; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.developer.nefarious.zjoule.plugin.chat.ollama.OllamaChatMessage; +import com.developer.nefarious.zjoule.plugin.chat.ollama.OllamaRequestBody; +import com.developer.nefarious.zjoule.plugin.models.Role; + +public class OllamaRequestBodyTest { + + // CHECKSTYLE:OFF - Deactivate checkstyle to avoid magic number issues on this test + @Test + public void shouldContainNullValuesWhenDoubleQuoted() { + // Arrange + OllamaRequestBody cut = new OllamaRequestBody(); + + String expectedValue = "{\"model\":\"llama3.2:latest\"," + + "\"stream\":false," + + "\"messages\":[" + + "{\"role\":\"user\"," + + "\"content\":\"null\"}," + + "{\"role\":\"assistant\"," + + "\"content\":\"Hi there!\"}]}"; + + OllamaChatMessage message1 = new OllamaChatMessage(); + message1.setRole(Role.USER); + message1.setContent("null"); + + OllamaChatMessage message2 = new OllamaChatMessage(); + message2.setRole(Role.ASSISTANT); + message2.setContent("Hi there!"); + + cut.setMessages(List.of(message1, message2)); + cut.setStream(false); + cut.setModel("llama3.2:latest"); + + // Act + String returnValue = cut.toString(); + + // Assert + assertEquals(expectedValue, returnValue); + } + + @Test + public void shouldConvertObjectToString() { + // Arrange + OllamaRequestBody cut = new OllamaRequestBody(); + + String expectedValue = "{\"model\":\"llama3.2:latest\"," + + "\"stream\":false," + + "\"messages\":[" + + "{\"role\":\"user\"," + + "\"content\":\"Hello!\"}," + + "{\"role\":\"assistant\"," + + "\"content\":\"Hi there!\"}]}"; + + OllamaChatMessage message1 = new OllamaChatMessage(); + message1.setRole(Role.USER); + message1.setContent("Hello!"); + + OllamaChatMessage message2 = new OllamaChatMessage(); + message2.setRole(Role.ASSISTANT); + message2.setContent("Hi there!"); + + cut.setMessages(List.of(message1, message2)); + cut.setStream(false); + cut.setModel("llama3.2:latest"); + + // Act + String returnValue = cut.toString(); + + // Assert + assertEquals(expectedValue, returnValue); + } + // CHECKSTYLE:ON + +} diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/core/ui/ViewListenerTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/core/ui/ViewListenerTest.java index 0aac11bc..d8dd7d18 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/core/ui/ViewListenerTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/core/ui/ViewListenerTest.java @@ -37,6 +37,7 @@ import com.developer.nefarious.zjoule.plugin.core.functions.ClearHandler; import com.developer.nefarious.zjoule.plugin.core.functions.LoginHandler; import com.developer.nefarious.zjoule.plugin.core.functions.LogoutHandler; +import com.developer.nefarious.zjoule.plugin.core.functions.PreferencesHandler; import com.developer.nefarious.zjoule.plugin.core.functions.PromptHandler; import com.developer.nefarious.zjoule.plugin.core.ui.BrowserFactory; import com.developer.nefarious.zjoule.plugin.core.ui.IViewRender; @@ -66,7 +67,9 @@ public class ViewListenerTest { private MockedStatic mockedStaticLogoutHandler; - private MockedStatic mockedSystemProvider; + private MockedStatic mockedStaticSystemProvider; + + private MockedStatic mockedStaticPreferencesHandler; @Mock private Shell mockShell; @@ -100,6 +103,9 @@ public class ViewListenerTest { @Mock private LogoutHandler mockLogoutHandler; + + @Mock + private PreferencesHandler mockPreferencesHandler; @BeforeEach public void setUp() { @@ -114,7 +120,8 @@ public void setUp() { mockedStaticLoginHandler = mockStatic(LoginHandler.class); mockedStaticClearHandler = mockStatic(ClearHandler.class); mockedStaticLogoutHandler = mockStatic(LogoutHandler.class); - mockedSystemProvider = mockStatic(SystemProvider.class); + mockedStaticSystemProvider = mockStatic(SystemProvider.class); + mockedStaticPreferencesHandler = mockStatic(PreferencesHandler.class); mockedStaticSelectionListener.when(() -> SelectionListener.create(mockBrowser)).thenReturn(mockSelectionListener); mockedStaticViewRender.when(ViewRender::create).thenReturn(mockViewRender); @@ -124,6 +131,7 @@ public void setUp() { mockedStaticLoginHandler.when(() -> LoginHandler.create(mockShell, mockBrowser)).thenReturn(mockLoginHandler); mockedStaticClearHandler.when(() -> ClearHandler.create(mockBrowser)).thenReturn(mockClearHandler); mockedStaticLogoutHandler.when(() -> LogoutHandler.create(mockBrowser)).thenReturn(mockLogoutHandler); + mockedStaticPreferencesHandler.when(PreferencesHandler::create).thenReturn(mockPreferencesHandler); cut = spy(new ViewListener()); cut.setShell(mockShell); @@ -132,7 +140,7 @@ public void setUp() { @Test public void shouldPlumbPartControlForWindows() { // Arrange - mockedSystemProvider.when(SystemProvider::getCurrentSystem).thenReturn("Windows 95"); + mockedStaticSystemProvider.when(SystemProvider::getCurrentSystem).thenReturn("Windows 95"); mockedStaticBrowserFactory.when(() -> BrowserFactory.create(mockParent, SWT.EDGE)).thenReturn(mockBrowser); String mockBuildResult = "html-text"; @@ -171,6 +179,7 @@ public void shouldPlumbPartControlForWindows() { verify(mockToolBarManager).add(mockLoginHandler); verify(mockMenuManager).add(mockClearHandler); + verify(mockMenuManager).add(mockPreferencesHandler); verify(mockMenuManager).add(any(Separator.class)); verify(mockMenuManager).add(mockLogoutHandler); } @@ -178,7 +187,7 @@ public void shouldPlumbPartControlForWindows() { @Test public void shouldPlumbPartControlForOtherSystems() { // Arrange - mockedSystemProvider.when(SystemProvider::getCurrentSystem).thenReturn("Mac OS"); + mockedStaticSystemProvider.when(SystemProvider::getCurrentSystem).thenReturn("Mac OS"); mockedStaticBrowserFactory.when(() -> BrowserFactory.create(mockParent, SWT.WEBKIT)).thenReturn(mockBrowser); String mockBuildResult = "html-text"; @@ -217,6 +226,7 @@ public void shouldPlumbPartControlForOtherSystems() { verify(mockToolBarManager).add(mockLoginHandler); verify(mockMenuManager).add(mockClearHandler); + verify(mockMenuManager).add(mockPreferencesHandler); verify(mockMenuManager).add(any(Separator.class)); verify(mockMenuManager).add(mockLogoutHandler); } @@ -251,8 +261,11 @@ public void tearDown() { if (mockedStaticLogoutHandler != null) { mockedStaticLogoutHandler.close(); } - if (mockedSystemProvider != null) { - mockedSystemProvider.close(); + if (mockedStaticSystemProvider != null) { + mockedStaticSystemProvider.close(); + } + if (mockedStaticPreferencesHandler != null) { + mockedStaticPreferencesHandler.close(); } } //CHECKSTYLE:ON CyclomaticComplexity diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/OllamaLoginClientHelperTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/OllamaLoginClientHelperTest.java new file mode 100644 index 00000000..583cf217 --- /dev/null +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/OllamaLoginClientHelperTest.java @@ -0,0 +1,59 @@ +package com.developer.nefarious.zjoule.test.login.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; + +import java.net.URI; +import java.util.concurrent.ThreadLocalRandom; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import com.developer.nefarious.zjoule.plugin.login.api.GetOllamaModelsResponse; +import com.developer.nefarious.zjoule.plugin.login.api.OllamaLoginClientHelper; +import com.google.gson.Gson; + +public class OllamaLoginClientHelperTest { + + private OllamaLoginClientHelper cut; + + private String randomWord() { + final String[] WORDS = { "apple", "banana", "grape" }; + int randomIndex = ThreadLocalRandom.current().nextInt(WORDS.length); + return WORDS[randomIndex]; + } + + @BeforeEach + public void setUp() { + cut = new OllamaLoginClientHelper(); + } + + @Test + public void shouldConvertTheModelsResponseBodyToObject() { + // Arrange + Gson gson = new Gson(); + String mockResponseBody = "{\"models\": [{\"name\": \"llama3.2:latest\"}]}"; + GetOllamaModelsResponse expectedObject = gson.fromJson(mockResponseBody, GetOllamaModelsResponse.class); + // Act + GetOllamaModelsResponse returnObject = cut.parseOllamaModelsResponseToObject(mockResponseBody); + // Assert + assertEquals(returnObject.getModels().getFirst().getName(), expectedObject.getModels().getFirst().getName()); + } + + @Test + public void shouldCreateTheUri() { + // Arrange + URI expectedObject = mock(URI.class); + String mockEndpoint = randomWord(); + try (MockedStatic uriStatic = mockStatic(URI.class)) { + uriStatic.when(() -> URI.create(mockEndpoint)).thenReturn(expectedObject); + // Act + URI returnObject = cut.createUri(mockEndpoint); + // Assert + assertEquals(returnObject, expectedObject); + } + } + +} diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/OllamaLoginClientTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/OllamaLoginClientTest.java new file mode 100644 index 00000000..4dc84f1d --- /dev/null +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/OllamaLoginClientTest.java @@ -0,0 +1,104 @@ +package com.developer.nefarious.zjoule.test.login.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.Builder; +import java.net.http.HttpResponse; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; + +import com.developer.nefarious.zjoule.plugin.login.api.GetOllamaModelsResponse; +import com.developer.nefarious.zjoule.plugin.login.api.IOllamaLoginClient; +import com.developer.nefarious.zjoule.plugin.login.api.IOllamaLoginClientHelper; +import com.developer.nefarious.zjoule.plugin.login.api.OllamaLoginClient; + +public class OllamaLoginClientTest { + + private IOllamaLoginClient cut; + + MockedStatic mockedStaticHttpClient; + + MockedStatic mockedStaticHttpRequest; + + @Mock + private HttpClient mockHttpClient; + + @Mock + private IOllamaLoginClientHelper mockOllamaLoginClientHelper; + + @Mock + private Builder mockBuilder; + + @Mock + private HttpRequest mockHttpRequest; + + @Mock + private HttpResponse mockHttpResponse; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + + mockedStaticHttpClient = mockStatic(HttpClient.class); + mockedStaticHttpClient.when(HttpClient::newHttpClient).thenReturn(mockHttpClient); + + mockedStaticHttpRequest = mockStatic(HttpRequest.class); + mockedStaticHttpRequest.when(HttpRequest::newBuilder).thenReturn(mockBuilder); + + cut = spy(new OllamaLoginClient(mockOllamaLoginClientHelper)); + } + + @Test + public void shouldPlumbModels() throws IOException, InterruptedException { + // Arrange + GetOllamaModelsResponse expectedValue = mock(GetOllamaModelsResponse.class); + + String mockOllamaUrl = "https://some-url.com"; + + String mockEndpointInStringFormat = mockOllamaUrl + "/api/tags"; + URI mockEndpointInURIFormat = mock(URI.class); + when(mockOllamaLoginClientHelper.createUri(mockEndpointInStringFormat)).thenReturn(mockEndpointInURIFormat); + when(mockBuilder.uri(mockEndpointInURIFormat)).thenReturn(mockBuilder); + + when(mockBuilder.GET()).thenReturn(mockBuilder); + when(mockBuilder.build()).thenReturn(mockHttpRequest); + when(mockHttpClient.send(mockHttpRequest, HttpResponse.BodyHandlers.ofString())).thenReturn(mockHttpResponse); + + String mockResponseBody = "response-content"; + when(mockHttpResponse.body()).thenReturn(mockResponseBody); + when(mockOllamaLoginClientHelper.parseOllamaModelsResponseToObject(mockResponseBody)).thenReturn(expectedValue); + + // Act + GetOllamaModelsResponse returnValue = cut.getModels(mockOllamaUrl); + + // Assert + assertEquals(expectedValue, returnValue); + verify(mockBuilder).uri(mockEndpointInURIFormat); + verify(mockBuilder).GET(); + } + + @AfterEach + public void tearDown() { + if (mockedStaticHttpClient != null) { + mockedStaticHttpClient.close(); + } + if (mockedStaticHttpRequest != null) { + mockedStaticHttpRequest.close(); + } + } + +} diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/LoginClientHelperTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/SapLoginClientHelperTest.java similarity index 91% rename from com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/LoginClientHelperTest.java rename to com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/SapLoginClientHelperTest.java index 10240980..af37bdf0 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/LoginClientHelperTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/SapLoginClientHelperTest.java @@ -13,12 +13,12 @@ import com.developer.nefarious.zjoule.plugin.login.api.GetDeploymentsResponse; import com.developer.nefarious.zjoule.plugin.login.api.GetResourceGroupsResponse; -import com.developer.nefarious.zjoule.plugin.login.api.LoginClientHelper; +import com.developer.nefarious.zjoule.plugin.login.api.SapLoginClientHelper; import com.google.gson.Gson; -public class LoginClientHelperTest { +public class SapLoginClientHelperTest { - private LoginClientHelper cut; + private SapLoginClientHelper cut; private String randomWord() { final String[] WORDS = { "apple", "banana", "grape" }; @@ -28,7 +28,7 @@ private String randomWord() { @BeforeEach public void setUp() { - cut = new LoginClientHelper(); + cut = new SapLoginClientHelper(); } @Test diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/LoginClientTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/SapLoginClientTest.java similarity index 86% rename from com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/LoginClientTest.java rename to com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/SapLoginClientTest.java index d0c2b84f..104d2f68 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/LoginClientTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/api/SapLoginClientTest.java @@ -24,14 +24,14 @@ import com.developer.nefarious.zjoule.plugin.auth.IAuthClient; import com.developer.nefarious.zjoule.plugin.login.api.GetDeploymentsResponse; import com.developer.nefarious.zjoule.plugin.login.api.GetResourceGroupsResponse; -import com.developer.nefarious.zjoule.plugin.login.api.ILoginClient; -import com.developer.nefarious.zjoule.plugin.login.api.ILoginClientHelper; -import com.developer.nefarious.zjoule.plugin.login.api.LoginClient; +import com.developer.nefarious.zjoule.plugin.login.api.ISapLoginClient; +import com.developer.nefarious.zjoule.plugin.login.api.ISapLoginClientHelper; +import com.developer.nefarious.zjoule.plugin.login.api.SapLoginClient; import com.developer.nefarious.zjoule.plugin.models.ServiceKey; -public class LoginClientTest { +public class SapLoginClientTest { - private ILoginClient cut; + private ISapLoginClient cut; MockedStatic mockedStaticHttpClient; @@ -47,7 +47,7 @@ public class LoginClientTest { private IAuthClient mockAuthClient; @Mock - private ILoginClientHelper mockLoginClientHelper; + private ISapLoginClientHelper mockSapLoginClientHelper; @Mock private Builder mockBuilder; @@ -68,7 +68,7 @@ public void setUp() { mockedStaticHttpRequest = mockStatic(HttpRequest.class); mockedStaticHttpRequest.when(HttpRequest::newBuilder).thenReturn(mockBuilder); - cut = spy(new LoginClient(mockLoginClientHelper, mockAuthClient)); + cut = spy(new SapLoginClient(mockSapLoginClientHelper, mockAuthClient)); } @Test @@ -83,7 +83,7 @@ public void shouldPlumbDeployments() throws IOException, InterruptedException { String mockEndpointInStringFormat = mockServiceURL + "/lm/deployments"; URI mockEndpointInURIFormat = mock(URI.class); - when(mockLoginClientHelper.createAuthUri(mockEndpointInStringFormat)).thenReturn(mockEndpointInURIFormat); + when(mockSapLoginClientHelper.createAuthUri(mockEndpointInStringFormat)).thenReturn(mockEndpointInURIFormat); when(mockBuilder.uri(mockEndpointInURIFormat)).thenReturn(mockBuilder); String mockToken = "access-token"; @@ -99,7 +99,7 @@ public void shouldPlumbDeployments() throws IOException, InterruptedException { String mockResponseBody = "response-content"; when(mockHttpResponse.body()).thenReturn(mockResponseBody); - when(mockLoginClientHelper.parseDeploymentsResponseToObject(mockResponseBody)).thenReturn(expectedValue); + when(mockSapLoginClientHelper.parseDeploymentsResponseToObject(mockResponseBody)).thenReturn(expectedValue); // Act GetDeploymentsResponse returnValue = cut.getDeployments(mockServiceKey, mockResourceGroup); @@ -122,7 +122,7 @@ public void shouldPlumbResourceGroups() throws IOException, InterruptedException String mockEndpointInStringFormat = mockServiceURL + "/admin/resourceGroups"; URI mockEndpointInURIFormat = mock(URI.class); - when(mockLoginClientHelper.createAuthUri(mockEndpointInStringFormat)).thenReturn(mockEndpointInURIFormat); + when(mockSapLoginClientHelper.createAuthUri(mockEndpointInStringFormat)).thenReturn(mockEndpointInURIFormat); when(mockBuilder.uri(mockEndpointInURIFormat)).thenReturn(mockBuilder); String mockToken = "access-token"; @@ -137,7 +137,7 @@ public void shouldPlumbResourceGroups() throws IOException, InterruptedException String mockResponseBody = "response-content"; when(mockHttpResponse.body()).thenReturn(mockResponseBody); - when(mockLoginClientHelper.parseResourceGroupsResponseToObject(mockResponseBody)).thenReturn(expectedValue); + when(mockSapLoginClientHelper.parseResourceGroupsResponseToObject(mockResponseBody)).thenReturn(expectedValue); // Act GetResourceGroupsResponse returnValue = cut.getResourceGroups(mockServiceKey); diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/DeploymentSelectionAdapterTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/DeploymentSelectionAdapterTest.java index 817035ca..a35bf2a3 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/DeploymentSelectionAdapterTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/DeploymentSelectionAdapterTest.java @@ -16,7 +16,7 @@ import org.mockito.MockitoAnnotations; import com.developer.nefarious.zjoule.plugin.login.events.DeploymentSelectionAdapter; -import com.developer.nefarious.zjoule.plugin.login.pages.SecondLoginWizardPage; +import com.developer.nefarious.zjoule.plugin.login.pages.SecondSapLoginWizardPage; import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; import com.developer.nefarious.zjoule.plugin.models.Deployment; @@ -25,7 +25,7 @@ public class DeploymentSelectionAdapterTest { private DeploymentSelectionAdapter cut; @Mock - private SecondLoginWizardPage mockSecondLoginWizardPage; + private SecondSapLoginWizardPage mockSecondLoginWizardPage; @Mock private IMemoryObject mockMemoryDeployment; diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/OllamaModelSelectionAdapterTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/OllamaModelSelectionAdapterTest.java new file mode 100644 index 00000000..1cfcc1ae --- /dev/null +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/OllamaModelSelectionAdapterTest.java @@ -0,0 +1,84 @@ +package com.developer.nefarious.zjoule.test.login.events; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Combo; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import com.developer.nefarious.zjoule.plugin.login.events.OllamaModelSelectionAdapter; +import com.developer.nefarious.zjoule.plugin.login.pages.SecondOllamaLoginWizardPage; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; +import com.developer.nefarious.zjoule.plugin.models.OllamaModel; + +public class OllamaModelSelectionAdapterTest { + + private OllamaModelSelectionAdapter cut; + + @Mock + private SecondOllamaLoginWizardPage mockSecondOllamaLoginWizardPage; + + @Mock + private IMemoryObject mockMemoryOllamaModel; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + + cut = spy(new OllamaModelSelectionAdapter(mockSecondOllamaLoginWizardPage, mockMemoryOllamaModel)); + } + + @Test + public void shouldSetAsCompleteWhenThereIsText() { + // Arrange + SelectionEvent mockSelectionEvent = mock(SelectionEvent.class); + + Combo mockOllamaModelDropdown = mock(Combo.class); + when(mockSecondOllamaLoginWizardPage.getOllamaModelDropdown()).thenReturn(mockOllamaModelDropdown); + String mockSelectedOllamaModelName = "Selected Ollama Model"; + when(mockOllamaModelDropdown.getText()).thenReturn(mockSelectedOllamaModelName); + + OllamaModel ollamaModel1 = new OllamaModel(); + ollamaModel1.setName("Not this one"); + OllamaModel ollamaModel2 = new OllamaModel(); + ollamaModel2.setName(mockSelectedOllamaModelName); + OllamaModel ollamaModel3 = new OllamaModel(); + ollamaModel3.setName("Note this one"); + List mockOllamaModelsForselection = Arrays.asList(ollamaModel1, ollamaModel2, ollamaModel3); + when(mockSecondOllamaLoginWizardPage.getOllamaModelsForSelection()).thenReturn(mockOllamaModelsForselection); + + // Act + cut.widgetSelected(mockSelectionEvent); + + // Assert + verify(mockSecondOllamaLoginWizardPage).setPageComplete(true); + verify(mockMemoryOllamaModel).save(ollamaModel2); + } + + @Test + public void shouldSetAsIncompleteWhenThereIsNoText() { + // Arrange + SelectionEvent mockSelectionEvent = mock(SelectionEvent.class); + + Combo mockOllamaModelDropdown = mock(Combo.class); + when(mockSecondOllamaLoginWizardPage.getOllamaModelDropdown()).thenReturn(mockOllamaModelDropdown); + String mockText = ""; + when(mockOllamaModelDropdown.getText()).thenReturn(mockText); + + // Act + cut.widgetSelected(mockSelectionEvent); + + // Assert + verify(mockSecondOllamaLoginWizardPage).setPageComplete(false); + } + +} diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/ResourceGroupSelectionAdapterTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/ResourceGroupSelectionAdapterTest.java index f9a33952..fcd8466f 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/ResourceGroupSelectionAdapterTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/ResourceGroupSelectionAdapterTest.java @@ -15,9 +15,9 @@ import org.mockito.MockitoAnnotations; import com.developer.nefarious.zjoule.plugin.login.api.GetDeploymentsResponse; -import com.developer.nefarious.zjoule.plugin.login.api.ILoginClient; +import com.developer.nefarious.zjoule.plugin.login.api.ISapLoginClient; import com.developer.nefarious.zjoule.plugin.login.events.ResourceGroupSelectionAdapter; -import com.developer.nefarious.zjoule.plugin.login.pages.SecondLoginWizardPage; +import com.developer.nefarious.zjoule.plugin.login.pages.SecondSapLoginWizardPage; import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; import com.developer.nefarious.zjoule.plugin.models.Deployment; import com.developer.nefarious.zjoule.plugin.models.ServiceKey; @@ -27,10 +27,10 @@ public class ResourceGroupSelectionAdapterTest { private ResourceGroupSelectionAdapter cut; @Mock - private SecondLoginWizardPage mockSecondLoginWizardPage; + private SecondSapLoginWizardPage mockSecondLoginWizardPage; @Mock - private ILoginClient mockLoginClient; + private ISapLoginClient mockSapLoginClient; @Mock private IMemoryObject mockMemoryResourceGroup; @@ -38,7 +38,7 @@ public class ResourceGroupSelectionAdapterTest { @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - cut = new ResourceGroupSelectionAdapter(mockSecondLoginWizardPage, mockLoginClient, mockMemoryResourceGroup); + cut = new ResourceGroupSelectionAdapter(mockSecondLoginWizardPage, mockSapLoginClient, mockMemoryResourceGroup); } @Test @@ -59,7 +59,7 @@ public void shouldEnableTheDeploymentSelection() throws IOException, Interrupted when(mockSecondLoginWizardPage.getServiceKey()).thenReturn(mockServiceKey); GetDeploymentsResponse mockGetDeploymentsResponse = mock(GetDeploymentsResponse.class); - when(mockLoginClient.getDeployments(mockServiceKey, mockText)).thenReturn(mockGetDeploymentsResponse); + when(mockSapLoginClient.getDeployments(mockServiceKey, mockText)).thenReturn(mockGetDeploymentsResponse); List mockDeployments = mock(List.class); when(mockGetDeploymentsResponse.getDeployments()).thenReturn(mockDeployments); diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/ServiceKeyModifyListenerTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/ServiceKeyModifyListenerTest.java index 34ab8358..e2efe317 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/ServiceKeyModifyListenerTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/events/ServiceKeyModifyListenerTest.java @@ -16,9 +16,9 @@ import org.mockito.MockitoAnnotations; import com.developer.nefarious.zjoule.plugin.login.api.GetResourceGroupsResponse; -import com.developer.nefarious.zjoule.plugin.login.api.ILoginClient; +import com.developer.nefarious.zjoule.plugin.login.api.ISapLoginClient; import com.developer.nefarious.zjoule.plugin.login.events.ServiceKeyModifyListener; -import com.developer.nefarious.zjoule.plugin.login.pages.FirstLoginWizardPage; +import com.developer.nefarious.zjoule.plugin.login.pages.FirstSapLoginWizardPage; import com.developer.nefarious.zjoule.plugin.login.utils.JsonValidator; import com.developer.nefarious.zjoule.plugin.models.ServiceKey; import com.google.gson.Gson; @@ -30,10 +30,10 @@ public class ServiceKeyModifyListenerTest { private ServiceKeyModifyListener cut; @Mock - private FirstLoginWizardPage mockFirstLoginWizardPage; + private FirstSapLoginWizardPage mockFirstLoginWizardPage; @Mock - private ILoginClient mockLoginClient; + private ISapLoginClient mockSapLoginClient; @Mock private Gson mockGson; @@ -44,7 +44,7 @@ public class ServiceKeyModifyListenerTest { @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - cut = spy(new ServiceKeyModifyListener(mockFirstLoginWizardPage, mockLoginClient, mockGson)); + cut = spy(new ServiceKeyModifyListener(mockFirstLoginWizardPage, mockSapLoginClient, mockGson)); } @Test @@ -109,7 +109,7 @@ public void shouldEnableTheNextButtonIfInputIsValid() throws IOException, Interr when(mockGson.fromJson(mockInputTextTrimmed, ServiceKey.class)).thenReturn(mockServiceKey); when(mockServiceKey.isValid()).thenReturn(true); GetResourceGroupsResponse mockGetResourceGroupsResponse = mock(GetResourceGroupsResponse.class); - when(mockLoginClient.getResourceGroups(mockServiceKey)).thenReturn(mockGetResourceGroupsResponse); + when(mockSapLoginClient.getResourceGroups(mockServiceKey)).thenReturn(mockGetResourceGroupsResponse); // Act cut.modifyText(mockModifyEvent); // Assert @@ -131,7 +131,7 @@ public void shouldGracefullyHandleAnyError() throws IOException, InterruptedExce } when(mockGson.fromJson(mockInputTextTrimmed, ServiceKey.class)).thenReturn(mockServiceKey); when(mockServiceKey.isValid()).thenReturn(true); - when(mockLoginClient.getResourceGroups(mockServiceKey)).thenThrow(new IOException()); + when(mockSapLoginClient.getResourceGroups(mockServiceKey)).thenThrow(new IOException()); // Act cut.modifyText(mockModifyEvent); // Assert diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryAccessTokenTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryAccessTokenTest.java index 504c6282..3f9d17b9 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryAccessTokenTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryAccessTokenTest.java @@ -139,5 +139,14 @@ public void shouldSaveAccessToken() { // Assert verify(mockEclipseMemory).saveOnEclipsePreferences(TEMPORARY_KEY, mockSerializedObject); } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(TEMPORARY_KEY); + } } diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryDeploymentTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryDeploymentTest.java index a460d537..bd6ee398 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryDeploymentTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryDeploymentTest.java @@ -139,5 +139,14 @@ public void shouldSaveDeployment() { // Assert verify(mockEclipseMemory).saveOnEclipsePreferences(TEMPORARY_KEY, mockSerializedObject); } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(TEMPORARY_KEY); + } } diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryOllamaEndpointTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryOllamaEndpointTest.java new file mode 100644 index 00000000..074789a7 --- /dev/null +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryOllamaEndpointTest.java @@ -0,0 +1,139 @@ +package com.developer.nefarious.zjoule.test.login.memory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryOllamaEndpoint; +import com.developer.nefarious.zjoule.plugin.memory.IEclipseMemory; + +public class TemporaryMemoryOllamaEndpointTest { + + public static final String FINAL_KEY = "ollama-endpoint"; + + public static final String TEMPORARY_KEY = "tmp-ollama-endpoint"; + + private TemporaryMemoryOllamaEndpoint cut; + + @Mock + IEclipseMemory mockEclipseMemory; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + + TemporaryMemoryOllamaEndpoint.resetInstance(); + TemporaryMemoryOllamaEndpoint.initialize(mockEclipseMemory); + cut = spy(TemporaryMemoryOllamaEndpoint.getInstance()); + } + + @Test + public void shouldBeEmptyWhenNoMemory() { + // Arrange + doReturn(null).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWhenOllamaEndpointIsBlank() { + // Arrange + String mockMockOllamaEndpoint = " "; + doReturn(mockMockOllamaEndpoint).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWhenOllamaEndpointIsEmpty() { + // Arrange + String mockMockOllamaEndpoint = ""; + doReturn(mockMockOllamaEndpoint).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWheOllamaEndpointIsNull() { + // Arrange + String mockMockOllamaEndpoint = null; + doReturn(mockMockOllamaEndpoint).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldLoadOllamaEndpoint() { + // Arrange + String expectedValue = "stored ollama endpoint"; + when(mockEclipseMemory.loadFromEclipsePreferences(TEMPORARY_KEY)).thenReturn(expectedValue); + // Act + String returnValue = cut.load(); + // Assert + assertEquals(returnValue, expectedValue); + } + + @Test + public void shouldNotBeEmptyWhenOllamaEndpointIsSave() { + // Arrange + String mockMockOllamaEndpoint = "ollama-endpoint"; + doReturn(mockMockOllamaEndpoint).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertFalse(returnValue); + } + + @Test + public void shouldPersistOllamaEndpoint() { + // Arrange + String mockOllamaEndpoint = "temporary ollama endpoint"; + when(mockEclipseMemory.loadFromEclipsePreferences(TEMPORARY_KEY)).thenReturn(mockOllamaEndpoint); + // Act + cut.persist(); + // Assert + verify(mockEclipseMemory).saveOnEclipsePreferences(FINAL_KEY, mockOllamaEndpoint); + } + + @Test + public void shouldSaveOllamaEndpoint() { + // Arrange + String mockOllamaEndpoint = "selected ollama endpoint"; + // Act + cut.save(mockOllamaEndpoint); + // Assert + verify(mockEclipseMemory).saveOnEclipsePreferences(TEMPORARY_KEY, mockOllamaEndpoint); + } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(TEMPORARY_KEY); + } + +} diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryOllamaModelTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryOllamaModelTest.java new file mode 100644 index 00000000..9b70f131 --- /dev/null +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryOllamaModelTest.java @@ -0,0 +1,152 @@ +package com.developer.nefarious.zjoule.test.login.memory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import com.developer.nefarious.zjoule.plugin.login.memory.TemporaryMemoryOllamaModel; +import com.developer.nefarious.zjoule.plugin.memory.IEclipseMemory; +import com.developer.nefarious.zjoule.plugin.memory.utils.IObjectSerializer; +import com.developer.nefarious.zjoule.plugin.models.OllamaModel; + +public class TemporaryMemoryOllamaModelTest { + + public static final String FINAL_KEY = "ollama-model"; + + public static final String TEMPORARY_KEY = "tmp-ollama-model"; + + private TemporaryMemoryOllamaModel cut; + + @Mock + private IObjectSerializer mockObjectSerializer; + + @Mock + private IEclipseMemory mockEclipseMemory; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + + TemporaryMemoryOllamaModel.resetInstance(); + TemporaryMemoryOllamaModel.initialize(mockObjectSerializer, mockEclipseMemory); + cut = spy(TemporaryMemoryOllamaModel.getInstance()); + } + + @Test + public void shouldBeEmptyWhenOllamaModelIsBlank() { + // Arrange + OllamaModel mockMockOllamaModel = new OllamaModel(); + mockMockOllamaModel.setName(" "); + doReturn(mockMockOllamaModel).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWhenOllamaModelIsEmpty() { + // Arrange + OllamaModel mockMockOllamaModel = new OllamaModel(); + mockMockOllamaModel.setName(""); + doReturn(mockMockOllamaModel).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWhenOllamaModelIsNull() { + // Arrange + OllamaModel mockMockOllamaModel = new OllamaModel(); + mockMockOllamaModel.setName(null); + doReturn(mockMockOllamaModel).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWhenNoMemory() { + // Arrange + doReturn(null).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldLoadOllamaModel() { + // Arrange + OllamaModel expectedValue = new OllamaModel(); + String mockSerializedObject = "It doesn't matter"; + when(mockEclipseMemory.loadFromEclipsePreferences(TEMPORARY_KEY)).thenReturn(mockSerializedObject); + when(mockObjectSerializer.deserialize(mockSerializedObject, OllamaModel.class)).thenReturn(expectedValue); + // Act + OllamaModel returnValue = cut.load(); + // Assert + assertEquals(returnValue, expectedValue); + } + + @Test + public void shouldNotBeEmptyWhenOllamaModelIsSave() { + // Arrange + OllamaModel mockMockOllamaModel = new OllamaModel(); + mockMockOllamaModel.setName("some-ollama-model"); + doReturn(mockMockOllamaModel).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertFalse(returnValue); + } + + @Test + public void shouldPersistOllamaModel() { + // Arrange + String mockSerializedObject = "It doesn't matter"; + when(mockEclipseMemory.loadFromEclipsePreferences(TEMPORARY_KEY)).thenReturn(mockSerializedObject); + // Act + cut.persist(); + // Assert + verify(mockEclipseMemory).saveOnEclipsePreferences(FINAL_KEY, mockSerializedObject); + } + + @Test + public void shouldSaveOllamaModel() { + // Arrange + OllamaModel mockOllamaModel = new OllamaModel(); + String mockSerializedObject = "It doesn't matter"; + when(mockObjectSerializer.serialize(mockOllamaModel)).thenReturn(mockSerializedObject); + // Act + cut.save(mockOllamaModel); + // Assert + verify(mockEclipseMemory).saveOnEclipsePreferences(TEMPORARY_KEY, mockSerializedObject); + } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(TEMPORARY_KEY); + } + +} diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryResourceGroupTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryResourceGroupTest.java index b1f6b787..d22df2d9 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryResourceGroupTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryResourceGroupTest.java @@ -126,5 +126,14 @@ public void shouldSaveResourceGroup() { // Assert verify(mockEclipseMemory).saveOnEclipsePreferences(TEMPORARY_KEY, mockResourceGroup); } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(TEMPORARY_KEY); + } } diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryServiceKeyTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryServiceKeyTest.java index 39d3a04c..dba75b7c 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryServiceKeyTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/login/memory/TemporaryMemoryServiceKeyTest.java @@ -139,5 +139,14 @@ public void shouldSaveServiceKey() { // Assert verify(mockEclipseMemory).saveOnEclipsePreferences(TEMPORARY_KEY, mockSerializedObject); } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(TEMPORARY_KEY); + } } diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryAccessTokenTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryAccessTokenTest.java index 12c05f4e..1a061536 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryAccessTokenTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryAccessTokenTest.java @@ -127,5 +127,14 @@ public void shouldSaveAccessToken() { // Assert verify(mockEclipseMemory).saveOnEclipsePreferences(KEY, mockSerializedObject); } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(KEY); + } } diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryDeploymentTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryDeploymentTest.java index 6cea4ceb..28059ece 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryDeploymentTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryDeploymentTest.java @@ -127,5 +127,14 @@ public void shouldSaveDeployment() { // Assert verify(mockEclipseMemory).saveOnEclipsePreferences(KEY, mockSerializedObject); } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(KEY); + } } diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryOllamaEndpointTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryOllamaEndpointTest.java new file mode 100644 index 00000000..0133fb20 --- /dev/null +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryOllamaEndpointTest.java @@ -0,0 +1,127 @@ +package com.developer.nefarious.zjoule.test.memory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import com.developer.nefarious.zjoule.plugin.memory.IEclipseMemory; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaEndpoint; + +public class MemoryOllamaEndpointTest { + + public static final String KEY = "ollama-endpoint"; + + private IMemoryObject cut; + + @Mock + private IEclipseMemory mockEclipseMemory; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + + MemoryOllamaEndpoint.resetInstance(); + MemoryOllamaEndpoint.initialize(mockEclipseMemory); + cut = spy(MemoryOllamaEndpoint.getInstance()); + } + + @Test + public void shouldBeEmptyWhenNoMemory() { + // Arrange + doReturn(null).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWhenOllamaEndpointIsBlank() { + // Arrange + String mockMockOllamaEndpoint = " "; + doReturn(mockMockOllamaEndpoint).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWhenOllamaEndpointIsEmpty() { + // Arrange + String mockMockOllamaEndpoint = ""; + doReturn(mockMockOllamaEndpoint).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWheOllamaEndpointIsNull() { + // Arrange + String mockMockOllamaEndpoint = null; + doReturn(mockMockOllamaEndpoint).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldLoadOllamaEndpoint() { + // Arrange + String expectedValue = "stored ollama endpoint"; + when(mockEclipseMemory.loadFromEclipsePreferences(KEY)).thenReturn(expectedValue); + // Act + String returnValue = cut.load(); + // Assert + assertEquals(returnValue, expectedValue); + } + + @Test + public void shouldNotBeEmptyWhenOllamaEndpointIsSave() { + // Arrange + String mockMockOllamaEndpoint = "ollama-endpoint"; + doReturn(mockMockOllamaEndpoint).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertFalse(returnValue); + } + + @Test + public void shouldSaveOllamaEndpoint() { + // Arrange + String mockOllamaEndpoint = "selected ollama endpoint"; + // Act + cut.save(mockOllamaEndpoint); + // Assert + verify(mockEclipseMemory).saveOnEclipsePreferences(KEY, mockOllamaEndpoint); + } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(KEY); + } + +} diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryOllamaModelTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryOllamaModelTest.java new file mode 100644 index 00000000..0b645c40 --- /dev/null +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryOllamaModelTest.java @@ -0,0 +1,140 @@ +package com.developer.nefarious.zjoule.test.memory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import com.developer.nefarious.zjoule.plugin.memory.IEclipseMemory; +import com.developer.nefarious.zjoule.plugin.memory.IMemoryObject; +import com.developer.nefarious.zjoule.plugin.memory.MemoryOllamaModel; +import com.developer.nefarious.zjoule.plugin.memory.utils.IObjectSerializer; +import com.developer.nefarious.zjoule.plugin.models.OllamaModel; + +public class MemoryOllamaModelTest { + + public static final String KEY = "ollama-model"; + + private IMemoryObject cut; + + @Mock + private IObjectSerializer mockObjectSerializer; + + @Mock + private IEclipseMemory mockEclipseMemory; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + + MemoryOllamaModel.resetInstance(); + MemoryOllamaModel.initialize(mockObjectSerializer, mockEclipseMemory); + cut = spy(MemoryOllamaModel.getInstance()); + } + + @Test + public void shouldBeEmptyWhenOllamaModelIsBlank() { + // Arrange + OllamaModel mockMockOllamaModel = new OllamaModel(); + mockMockOllamaModel.setName(" "); + doReturn(mockMockOllamaModel).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWhenOllamaModelIsEmpty() { + // Arrange + OllamaModel mockMockOllamaModel = new OllamaModel(); + mockMockOllamaModel.setName(""); + doReturn(mockMockOllamaModel).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWhenOllamaModelIsNull() { + // Arrange + OllamaModel mockMockOllamaModel = new OllamaModel(); + mockMockOllamaModel.setName(null); + doReturn(mockMockOllamaModel).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldBeEmptyWhenNoMemory() { + // Arrange + doReturn(null).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertTrue(returnValue); + } + + @Test + public void shouldLoadOllamaModel() { + // Arrange + OllamaModel expectedValue = new OllamaModel(); + String mockSerializedObject = "It doesn't matter"; + when(mockEclipseMemory.loadFromEclipsePreferences(KEY)).thenReturn(mockSerializedObject); + when(mockObjectSerializer.deserialize(mockSerializedObject, OllamaModel.class)).thenReturn(expectedValue); + // Act + OllamaModel returnValue = cut.load(); + // Assert + assertEquals(returnValue, expectedValue); + } + + @Test + public void shouldNotBeEmptyWhenOllamaModelIsSave() { + // Arrange + OllamaModel mockMockOllamaModel = new OllamaModel(); + mockMockOllamaModel.setName("some-ollama-model"); + doReturn(mockMockOllamaModel).when(cut).load(); + // Act + Boolean returnValue = cut.isEmpty(); + // Assert + verify(cut).load(); + assertFalse(returnValue); + } + + @Test + public void shouldSaveOllamaModel() { + // Arrange + OllamaModel mockOllamaModel = new OllamaModel(); + String mockSerializedObject = "It doesn't matter"; + when(mockObjectSerializer.serialize(mockOllamaModel)).thenReturn(mockSerializedObject); + // Act + cut.save(mockOllamaModel); + // Assert + verify(mockEclipseMemory).saveOnEclipsePreferences(KEY, mockSerializedObject); + } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(KEY); + } + +} diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryResourceGroupTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryResourceGroupTest.java index fe0bb201..eda3c32b 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryResourceGroupTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryResourceGroupTest.java @@ -114,5 +114,14 @@ public void shouldSaveResourceGroup() { // Assert verify(mockEclipseMemory).saveOnEclipsePreferences(KEY, mockResourceGroup); } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(KEY); + } } diff --git a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryServiceKeyTest.java b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryServiceKeyTest.java index 5966da9c..20cd19a0 100644 --- a/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryServiceKeyTest.java +++ b/com.developer.nefarious.zjoule.test/src/com/developer/nefarious/zjoule/test/memory/MemoryServiceKeyTest.java @@ -128,5 +128,14 @@ public void shouldSaveServiceKey() { // Assert verify(mockEclipseMemory).saveOnEclipsePreferences(KEY, mockSerializedObject); } + + @Test + public void shouldEraseFromMemory() { + // Arrange + // Act + cut.clear(); + // Assert + verify(mockEclipseMemory).deleteFromEclipsePreferences(KEY); + } } diff --git a/com.developer.nefarious.zjoule.updatesite/.project b/com.developer.nefarious.zjoule.updatesite/.project index 99b209a4..e7a3ac2c 100644 --- a/com.developer.nefarious.zjoule.updatesite/.project +++ b/com.developer.nefarious.zjoule.updatesite/.project @@ -10,8 +10,25 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature org.eclipse.pde.UpdateSiteNature + + + 1737318973876 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/scripts/prepare-release.sh b/scripts/prepare-release.sh index 24a34b05..05c3eb84 100755 --- a/scripts/prepare-release.sh +++ b/scripts/prepare-release.sh @@ -49,4 +49,7 @@ PLUGIN_FILE_NAME="plugin" # Zip the JavaDoc and copy it to the release folder DOC_FILE_NAME="doc" -(cd com.developer.nefarious.zjoule.plugin/target/reports/apidocs && zip -r "../../../../release/${DOC_FILE_NAME}.zip" .) \ No newline at end of file +(cd com.developer.nefarious.zjoule.plugin/target/reports/apidocs && zip -r "../../../../release/${DOC_FILE_NAME}.zip" .) + +# Update the version of the javadoc link in the README.md file +sed -i "s|\(https://zjoule.com/\)v[0-9]\+\.[0-9]\+\.[0-9]\+\(/doc/\)|\1${NEW_VERSION}\2|" README.md \ No newline at end of file diff --git a/scripts/update-website.sh b/scripts/update-website.sh new file mode 100755 index 00000000..2bf76a94 --- /dev/null +++ b/scripts/update-website.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +LATEST_VERSION=$1 # Needs to be passed as an argument + +set -e # Ensure the script stops on error + +#---------------------------------------------------------- +# Step 1: Check if LATEST_VERSION is provided + +if [[ -z "$LATEST_VERSION" ]]; then + echo "Error: LATEST_VERSION must be provided as an argument." + echo "Usage: $0 " + exit 1 +fi + +#---------------------------------------------------------- +# Step 2: Setup page for the new version + +# Create a new version variable with the dots replaced by underscores +NEW_FILE_NAME=$(echo $LATEST_VERSION | tr . _) + +# Copy the docs/version-template.md and name it after the new version +cp docs/version-template.md docs/$NEW_VERSION.md + +# Replace the placeholder __NEW_VERSION__ with the latest version in the new file +sed -i "s/__NEW_VERSION__/$LATEST_VERSION/g" docs/$NEW_FILE_NAME.md + +# Add the new version to the mkdocs.yml file +sed -i "/- Versions:/a\ - $LATEST_VERSION: $NEW_FILE_NAME.md" mkdocs.yml + +echo "Setup of the page for version $LATEST_VERSION is complete." + +#---------------------------------------------------------- +# Step 3: Setup the doc and plugin for the new version + +# Create a directory for the version in the /docs folder +mkdir -p docs/$LATEST_VERSION + +# Download the latest version of the documentation +wget -O docs/$LATEST_VERSION/doc.zip https://github.com/The-Nefarious-Developer/zjoule/releases/download/$LATEST_VERSION/doc.zip +unzip -o docs/$LATEST_VERSION/doc.zip -d docs/$LATEST_VERSION/doc && rm docs/$LATEST_VERSION/doc.zip +echo "Setup of the Javadoc for version $LATEST_VERSION is complete." + +# Download the latest version of the plugin +wget -O docs/$LATEST_VERSION/plugin.zip https://github.com/The-Nefarious-Developer/zjoule/releases/download/$LATEST_VERSION/plugin.zip +unzip -o docs/$LATEST_VERSION/plugin.zip -d docs/$LATEST_VERSION/plugin && rm docs/$LATEST_VERSION/plugin.zip +echo "Setup of the plugin for version $LATEST_VERSION is complete." + +#---------------------------------------------------------- +# Step 4: Update installation instructions at the home page + +# Replace the plugin version in the URL with the new version +sed -i "s|https://zjoule.com/.*/plugin|https://zjoule.com/$NEW_VERSION/plugin|g" docs/index.md +echo "Setup of the index for version $LATEST_VERSION is complete." \ No newline at end of file