Nova postagem

Pesquisar

Pergunta
· Dez. 19, 2024

Versión Evaluación de Cache II

Siguiendo con el hilo de Versión Evolución Cache, por si fuera interesante para alguien.

Instalada última versión de IRIS, tenido en cuenta el renombrando de CACHE.DAT a IRIS.DAT, no funciona. Si que monta correctamente las diferentes bases de datos, supongo que es debido a que es una licencia de la “Comunidad”

Al migrar los datos se ha producido una corrupción en los acentos y las eñes, cambiado el idioma local (enuw -> espw), si entras a la base de datos por el "terminal" del antiguo cubo todo parece normal.

Si utilizas una aplicación externa tipo "wrq reflection" (mi caso), no son posibles los acentos ni las eñes, de hecho, al pedir la contraseña, esta eñe no es correcta.

Salud

Discussão (0)1
Entre ou crie uma conta para continuar
Artigo
· Dez. 19, 2024 14min de leitura

Learn to create VSCode extensions with IRIS Global Editor app

VSCode is the most used IDE in the world. It is strategic have more extensions for VSCode for InterSystems technologies to keep increasing the developer community.

My new app IRIS VSCode Global Editor is an excellent sample to learn how to create extensions to IRIS. You can check it on https://openexchange.intersystems.com/package/IRIS-Global-VSCode-Editor.

To be ready to create extensions for VSCode

From https://code.visualstudio.com/api/get-started/your-first-extension you have all steps to get ready, but I will detail here to you.

1. Install Yeoman and start the creation of the extension project:

npm install --global yo generator-code

yo code

2. Answer the questions:

# ? What type of extension do you want to create? New Extension (TypeScript)
# ? What's the name of your extension? IRISExtension
### Press <Enter> to choose default for all options below ###

# ? What's the identifier of your extension? irisextension
# ? What's the description of your extension? LEAVE BLANK
# ? Initialize a git repository? Yes
# ? Bundle the source code with webpack? No
# ? Which package manager to use? npm

# ? Do you want to open the new folder with Visual Studio Code? Open with `code`

3. On VSCode, go to package.json, section contributes, to define new commands, views, editor and other elements created with your plugin, see this sample:

{
  "name": "iris-global-editor",
  "displayName": "iris-global-editor",
  "description": "IRIS Global Editor",
  "repository": "https://github.com/yurimarx/iris-global-editor",
  "version": "0.0.2",
  "engines": {
    "vscode": "^1.96.0"
  },
  "categories": [
    "Other"
  ],
  "activationEvents": [],
  "main": "./dist/extension.js",
  "contributes": {
    "views": {
      "explorer": [
        {
          "id": "irisGlobals",
          "name": "InterSystems IRIS Globals"
        }
      ]
    },
    "commands": [
      {
        "command": "irisGlobals.addEntry",
        "title": "Add",
        "icon": {
          "light": "resources/light/add.svg",
          "dark": "resources/dark/add.svg"
        }
      },
      {
        "command": "irisGlobals.deleteEntry",
        "title": "Delete",
        "icon": {
          "light": "resources/light/trash.svg",
          "dark": "resources/dark/trash.svg"
        }
      },
      {
        "command": "irisGlobals.editEntry",
        "title": "Edit",
        "icon": {
          "light": "resources/light/edit.svg",
          "dark": "resources/dark/edit.svg"
        }
      },
      {
        "command": "irisGlobals.editTextEntry",
        "title": "Editor",
        "icon": {
          "light": "resources/light/file.svg",
          "dark": "resources/dark/file.svg"
        }
      },
      {
        "command": "irisGlobals.refreshEntry",
        "title": "IRIS Globals: Refresh",
        "icon": {
          "light": "resources/light/refresh.svg",
          "dark": "resources/dark/refresh.svg"
        }
      },
      {
        "command": "irisGlobals.filterGlobals",
        "title": "IRIS Globals: Filter",
        "icon": {
          "light": "resources/light/filter.svg",
          "dark": "resources/dark/filter.svg"
        }
      }
    ],
    "menus": {
      "view/title": [
        {
          "command": "irisGlobals.filterGlobals",
          "when": "view == irisGlobals",
          "group": "navigation"
        },
        {
          "command": "irisGlobals.refreshEntry",
          "when": "view == irisGlobals",
          "group": "navigation"
        },
        {
          "command": "irisGlobals.addEntry",
          "when": "view == irisGlobals",
          "group": "navigation"
        }
      ],
      "view/item/context": [
        {
          "command": "irisGlobals.editEntry",
          "when": "view == irisGlobals",
          "group": "inline"
        },
        {
          "command": "irisGlobals.deleteEntry",
          "when": "view == irisGlobals",
          "group": "inline"
        },
        {
          "command": "irisGlobals.editTextEntry",
          "when": "view == irisGlobals",
          "group": "inline"
        }
      ]
    },
    "configuration": [
      {
        "id": "irisGlobals",
        "title": "Settings InterSystems IRIS Global Editor",
        "order": 1,
        "properties": {
          "conf.irisGlobalEditor.filter": {
            "type": "string",
            "default": "",
            "description": "Filter criteria to filter globals list"
          },
          "conf.irisGlobalEditor.serverconfig": {
            "type": "object",
            "order": 1,
            "description": "Connection settings",
            "properties": {
              "host": {
                "type": "string",
                "description": "InterSystems IRIS Host"
              },
              "namespace": {
                "type": "string",
                "description": "InterSystems IRIS Namespace"
              },
              "username": {
                "type": "string",
                "description": "User name credential"
              },
              "password": {
                "type": "string",
                "description": "Password credential"
              }
            },
            "additionalProperties": false
          }
        }
      }
    ]
  },
  "scripts": {
    "vscode:prepublish": "npm run package",
    "compile": "npm run check-types && npm run lint && node esbuild.js",
    "watch": "npm-run-all -p watch:*",
    "watch:esbuild": "node esbuild.js --watch",
    "watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
    "package": "npm run check-types && npm run lint && node esbuild.js --production",
    "compile-tests": "tsc -p . --outDir out",
    "watch-tests": "tsc -p . -w --outDir out",
    "pretest": "npm run compile-tests && npm run compile && npm run lint",
    "check-types": "tsc --noEmit",
    "lint": "eslint src",
    "test": "vscode-test"
  },
  "devDependencies": {
    "@types/mocha": "^10.0.9",
    "@types/node": "20.x",
    "@types/vscode": "^1.96.0",
    "@typescript-eslint/eslint-plugin": "^8.10.0",
    "@typescript-eslint/parser": "^8.7.0",
    "@vscode/test-cli": "^0.0.10",
    "@vscode/test-electron": "^2.4.1",
    "axios": "^1.7.9",
    "esbuild": "^0.24.0",
    "eslint": "^9.13.0",
    "npm-run-all": "^4.1.5",
    "typescript": "^5.6.3"
  },
  "dependencies": {
    "axios": "^1.7.9",
    "formdata-node": "^6.0.3",
    "iris-global-editor": "file:"
  }
}

4. With the functionalities declared, it is necessary the implementation. My app implement a TreeView on src folder on file IrisGlobalsProvider. For treeviews it is required implement a vscode.TreeDataProvider, for other type of VSCode functions see the article https://code.visualstudio.com/api/extension-guides/overview. It is my Treeview implementation:

import * as vscode from 'vscode';
import * as path from 'path';
import {fileFromPath} from 'formdata-node/file-from-path';
import { log } from 'console';
import axios, { AxiosRequestConfig, AxiosResponse, RawAxiosRequestHeaders } from 'axios';

export class IrisGlobalsTreeProvider implements vscode.TreeDataProvider<IrisGlobal> {

  constructor(private context: vscode.ExtensionContext) {}

  baseURL: string = "/iris-global-yaml";
  
  private _onDidChangeTreeData: vscode.EventEmitter<IrisGlobal | undefined | null | void> = new vscode.EventEmitter<IrisGlobal | undefined | null | void>();
  readonly onDidChangeTreeData: vscode.Event<IrisGlobal | undefined | null | void> = this._onDidChangeTreeData.event;
  
  public refresh() {
		this._onDidChangeTreeData.fire(undefined);
	}

  public async delete(globalname: string) {
    
    const cfgValues:any = await this.getIrisGlobalsConfig();

    const headers: AxiosRequestConfig = {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Basic ' + btoa(cfgValues.username + ":" + cfgValues.password)
      } as RawAxiosRequestHeaders,
    };
    
    const client = axios.create({
      baseURL: cfgValues.host + this.baseURL,
    });

    const globalParts = globalname.split(":", 2);

    const requestString = "/globals/" + cfgValues.namespace + "/" + globalParts[0].trim();
    
    const globalsYaml: AxiosResponse = await client.delete(requestString, headers);
    
    if (globalsYaml.status === 200) {
      vscode.window.showInformationMessage("Global successfully deleted");
    } else {
      vscode.window.showErrorMessage("Error on try to delete Global. Error: " + globalsYaml.statusText);
    }
  }

  private async getIrisGlobalsConfig() {
    
    try {
      const configuration = await vscode.workspace.getConfiguration('');
      return configuration.get('conf.irisGlobalEditor.serverconfig');
    } catch (error) {
      log(error);
      return null;
    }

  }

  public async add() {

    let selectedText = "";

    const addInput = await vscode.window.showInputBox({
      placeHolder: "GlobalName: Value",
      prompt: "Enter de name of the new Global and its value", 
      value: selectedText
    });
    
    if(addInput === '' || addInput === undefined){
      console.log(addInput);
      vscode.window.showErrorMessage('A Global Name and Value is mandatory to execute this action');
    } else {
      vscode.window.showInformationMessage(addInput!);
    }

    const cfgValues:any = await this.getIrisGlobalsConfig();

    const headers: AxiosRequestConfig = {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Basic ' + btoa(cfgValues.username + ":" + cfgValues.password)
      } as RawAxiosRequestHeaders,
    };
    
    const client = axios.create({
      baseURL: cfgValues.host + this.baseURL,
    });

    const inputParts = addInput === undefined ? [] : addInput.split(':', 2);
    
    const requestString = "/globals/" + cfgValues.namespace + "/" + inputParts[0].trim() + "?globalvalue=" + inputParts[1].trim();
    
    const globalsYaml: AxiosResponse = await client.put(requestString, "{}", headers);
    
    if (globalsYaml.status === 200) {
      vscode.window.showInformationMessage("Global successfully assigned");
    } else {
      vscode.window.showErrorMessage("Error while Global assigned. Error: " + globalsYaml.statusText);
    }

  }

  public async edit(globalvalue: string) {

    let selectedText = globalvalue;

    const addInput = await vscode.window.showInputBox({
      placeHolder: "GlobalName: Value",
      prompt: "Enter de name of the selected Global and its value", 
      value: selectedText
    });
    
    if(addInput === '' || addInput === undefined){
      console.log(addInput);
      vscode.window.showErrorMessage('A Global Name and Value is mandatory to execute this action');
    } else {
      vscode.window.showInformationMessage(addInput!);
    }

    const cfgValues:any = await this.getIrisGlobalsConfig();

    const headers: AxiosRequestConfig = {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Basic ' + btoa(cfgValues.username + ":" + cfgValues.password)
      } as RawAxiosRequestHeaders,
    };
    
    const client = axios.create({
      baseURL: cfgValues.host + this.baseURL,
    });

    const inputParts = addInput === undefined ? [] : addInput.split(':', 2);
    
    const requestString = "/globals/" + cfgValues.namespace + "/" + inputParts[0].trim() + "?globalvalue=" + inputParts[1].trim();
    
    const globalsYaml: AxiosResponse = await client.put(requestString, "{}", headers);
    
    if (globalsYaml.status === 200) {
      vscode.window.showInformationMessage("Global successfully assigned");
    } else {
      vscode.window.showErrorMessage("Error while Global assigned. Error: " + globalsYaml.statusText);
    }

  }

  public async saveGlobalWithYaml(filename: string) {
    
    if(filename.endsWith('.yml')) {
      const cfgValues:any = await this.getIrisGlobalsConfig();
  
      const headers: AxiosRequestConfig = {
        headers: {
          'Content-Type': 'multipart/form-data',
          'Authorization': 'Basic ' + btoa(cfgValues.username + ":" + cfgValues.password)
        } as RawAxiosRequestHeaders,
      };
      
      const client = axios.create({
        baseURL: cfgValues.host + this.baseURL
      });
  
      const requestString = "/globals/" + cfgValues.namespace;
      
      const formData = new FormData();
      formData.append('file', await fileFromPath(filename));
      
      try {
        const globalsYaml: AxiosResponse = await client.post(requestString, formData, headers);
    
        if (globalsYaml.statusText === "OK") {
          vscode.window.showInformationMessage("File " + filename + "sent with success and global saved");
        } else {
          vscode.window.showErrorMessage("Error while send yml file. Error: " + globalsYaml.statusText);
        }
      } catch (error) {
        vscode.window.showErrorMessage("Error while send yml file. Error: " + error);
      }

    }

  }

  public async editText(globalvalue: string) {

    const cfgValues:any = await this.getIrisGlobalsConfig();

    const headers: AxiosRequestConfig = {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Basic ' + btoa(cfgValues.username + ":" + cfgValues.password)
      } as RawAxiosRequestHeaders,
    };
    
    const client = axios.create({
      baseURL: cfgValues.host + this.baseURL
    });

    const inputParts = globalvalue === undefined ? [] : globalvalue.split(':', 2);
    
    const requestString = "/globals/yaml/" + cfgValues.namespace + "/" + inputParts[0].trim();
    
    const globalsYaml: AxiosResponse = await client.get(requestString, headers);
    
    if (globalsYaml.status === 200) {
      vscode.workspace.openTextDocument({
        language: 'yaml',
        content: globalsYaml.data
      });
    } else {
      vscode.window.showErrorMessage("Error while get Global text content. Error: " + globalsYaml.statusText);
    }

  }

  getTreeItem(element: IrisGlobal): vscode.TreeItem {
    return element;
  }

  getChildren(element?: IrisGlobal): Thenable<IrisGlobal[]> {
      
    return Promise.resolve(this.getGlobals());
      
  }

  async filterGlobals() {
    
    const configuration = await vscode.workspace.getConfiguration('');
    var filter: string = configuration.get('conf.irisGlobalEditor.filter')!;
    
    var selectedText = filter;

    const addInput = await vscode.window.showInputBox({
      prompt: "Enter the filter to global name (partial name of global)",
      value: selectedText
    });
    
    if(addInput === undefined){
      console.log(addInput);
      vscode.window.showErrorMessage('A filter value is mandatory to execute this action');
    } else {
      await vscode.workspace.getConfiguration().update('conf.irisGlobalEditor.filter', addInput);
      this.getGlobals();
      this.refresh();
      vscode.window.showInformationMessage('Filtered with success');
    }

  }

  async getGlobals(): Promise<IrisGlobal[]> {

    let response: IrisGlobal[] = [];

    const cfgValues:any = await this.getIrisGlobalsConfig();

    const headers: AxiosRequestConfig = {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Basic ' + btoa(cfgValues.username + ":" + cfgValues.password)
      } as RawAxiosRequestHeaders,
    };
    
    const client = axios.create({
      baseURL: cfgValues.host + this.baseURL,
    });

    const globalsYaml: AxiosResponse = await client.get("/globals/" + cfgValues.namespace, headers);

    var arr = globalsYaml.data.split("\r\n");
    if(arr.length > 0 && arr[0] === "# IRIS-Global-YAML") {
      arr.splice(0, 1);
    }
    const configuration = await vscode.workspace.getConfiguration('');
    var filter: string = configuration.get('conf.irisGlobalEditor.filter')!;
    
    if(filter !== undefined && filter !== null && filter !== "") {
      arr = arr.filter((el: string) => el.toLowerCase().includes(filter!.toLowerCase()));
    } 

    for (var i = 0; i < arr.length; i++) {
      const value = (arr[i] as string).trim();

      if(value !== "") {
        const item: IrisGlobal = {
          label: value,
          version: '1.0',
          collapsibleState: vscode.TreeItemCollapsibleState.None,
          iconPath: {
            light: this.context.asAbsolutePath(path.join('resources', 'light', 'dependency.svg')),
            dark: this.context.asAbsolutePath(path.join('resources', 'dark', 'dependency.svg'))
          }
        };
        response.push(item);
      }
      
    }
    
    return response;
    
  }

}


export class IrisGlobal extends vscode.TreeItem {
  
  constructor(
		public readonly label: string,
		public readonly version: string,
		public readonly collapsibleState: vscode.TreeItemCollapsibleState,
    public readonly iconPath: any
	) {
		super(label, collapsibleState);
    this.tooltip = `${this.label}-${this.version}`;
		this.description = this.description;
    this.iconPath = iconPath;
    this.contextValue = 'irisGlobals';
	}

  

}
  • I used axios to communicate with a REST API created for me to manage globals.
  • All methods for each function are created here.
  • It is necessary implement the interface vscode.TreeItem also, to define the data of treeview items.

5. On folder src, it is necessary register your implementation to be executed on the VSCode processes on the file extension.ts, see the sample:

// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import { IrisGlobal, IrisGlobalsTreeProvider } from './IrisGlobalsProvider';

export function activate(context: vscode.ExtensionContext) {

	const irisGlobalsProvider = new IrisGlobalsTreeProvider(context);
	vscode.window.registerTreeDataProvider('irisGlobals', irisGlobalsProvider);
	const disposableRefresh = vscode.commands.registerCommand('irisGlobals.refreshEntry', () => irisGlobalsProvider.refresh());
	const disposableAdd = vscode.commands.registerCommand('irisGlobals.addEntry', () => irisGlobalsProvider.add());
	const filterGlobals = vscode.commands.registerCommand('irisGlobals.filterGlobals', () => irisGlobalsProvider.filterGlobals());
	const disposableDelete = vscode.commands.registerCommand('irisGlobals.deleteEntry', (node: IrisGlobal) => irisGlobalsProvider.delete(node.label));
	const disposableEdit = vscode.commands.registerCommand('irisGlobals.editEntry', (node: IrisGlobal) => irisGlobalsProvider.edit(node.label));
	const disposableTextEdit = vscode.commands.registerCommand('irisGlobals.editTextEntry', (node: IrisGlobal) => irisGlobalsProvider.editText(node.label));


	context.subscriptions.push(vscode.workspace.onDidSaveTextDocument(editorsaveevent => {      

        if (editorsaveevent) {
			irisGlobalsProvider.saveGlobalWithYaml(editorsaveevent.fileName);
        }

    }));
	
	context.subscriptions.push(disposableRefresh);
	context.subscriptions.push(disposableAdd);
	context.subscriptions.push(disposableDelete);
	context.subscriptions.push(disposableEdit);
	context.subscriptions.push(disposableTextEdit);
	context.subscriptions.push(filterGlobals);
}

// This method is called when your extension is deactivated
export function deactivate() {}
  • I registered my TreeView Provider class and all commands implementations.
  • I subscribed to the event saveTextDocument, to call the saveGlobalWithYaml always a yaml file are saved on VSCode.

It is simple, no? Enjoy!

Discussão (0)1
Entre ou crie uma conta para continuar
Anúncio
· Dez. 19, 2024

Le Père Fouettard a volé notre récapitulatif !

Chers membres de la Communauté,

Il semble que le Père Fouettard ait joué un mauvais tour et ait emporté notre récapitulatif avant qu’il ne puisse être publié ! 🎄😅 Pas d’inquiétude, nos lutins s’activent pour le récupérer, et il sera en ligne très bientôt.

Restez à l’écoute, nous vous promettons de le ramener à temps pour partager toute la magie (et les détails) que vous attendez. Merci pour votre patience et préparez-vous pour le retour de notre récap festif !

Discussão (0)1
Entre ou crie uma conta para continuar
Discussão (0)1
Entre ou crie uma conta para continuar
Discussão (0)1
Entre ou crie uma conta para continuar