diff --git a/loc/translations-import/vscode-powerplatform.cs.xlf b/loc/translations-import/vscode-powerplatform.cs.xlf index 9d7a5b3d..9f93e9e6 100644 --- a/loc/translations-import/vscode-powerplatform.cs.xlf +++ b/loc/translations-import/vscode-powerplatform.cs.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. Vytváří se {0}... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + Výchozí prostředí: {0} + Display Name: {0} Unique Name: {1} @@ -147,9 +152,9 @@ Typ: {3} Failed to fetch some files. Nepodařilo se načíst některé soubory. - - Failed to get file ready for edit. - Soubor se nepodařilo připravit k úpravám. + + Failed to get file ready for edit: {0} + Soubor se nepodařilo připravit k úpravám: {0} Fetching your file ... @@ -304,10 +309,9 @@ Kód organizace: {3} The {0} represents the profile type (Admin vs Dataverse) Druh profilu: {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - Prostředek: {0} + + Response data is empty + Data odpovědi jsou prázdná. Saving your file ... @@ -375,9 +379,9 @@ Kód organizace: {3} The {0} represents auth profile's user name (email address)) Uživatel: {0} - - We encountered an error preparing the file for edit. - Při přípravě souboru k úpravám došlo k chybě. + + We encountered an error preparing the files for edit. + Při přípravě souborů k úpravám došlo k chybě. Web files diff --git a/loc/translations-import/vscode-powerplatform.de.xlf b/loc/translations-import/vscode-powerplatform.de.xlf index 36e89770..c932f947 100644 --- a/loc/translations-import/vscode-powerplatform.de.xlf +++ b/loc/translations-import/vscode-powerplatform.de.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. "{0}" wird erstellt... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + Standardumgebung: {0} + Display Name: {0} Unique Name: {1} @@ -108,6 +113,7 @@ Typ: {3} Do not show again + Nicht mehr anzeigen Edit the site @@ -146,9 +152,9 @@ Typ: {3} Failed to fetch some files. Fehler beim Abrufen einiger Dateien - - Failed to get file ready for edit. - Fehler beim Vorbereiten der Datei für die Bearbeitung. + + Failed to get file ready for edit: {0} + Fehler beim Vorbereiten der Datei für die Bearbeitung: {0} Fetching your file ... @@ -170,9 +176,11 @@ Typ: {3} Learn more about Copilot + Weitere Informationen zu Copilot Let Copilot help you code + Von Copilot beim Codieren helfen lassen Managed @@ -301,10 +309,9 @@ Organisations-ID: {3} The {0} represents the profile type (Admin vs Dataverse) Profil-Variante: {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - Ressource: {0} + + Response data is empty + Antwortdaten sind leer Saving your file ... @@ -321,6 +328,7 @@ Organisations-ID: {3} Selection is empty. + Auswahl ist leer. The Power Pages generator is ready for use in your VS Code extension! @@ -348,6 +356,7 @@ Organisations-ID: {3} Try Copilot for Power Pages + Copilot für Power Pages testen Try again @@ -370,9 +379,9 @@ Organisations-ID: {3} The {0} represents auth profile's user name (email address)) Benutzer: {0} - - We encountered an error preparing the file for edit. - Beim Vorbereiten der Datei zur Bearbeitung ist ein Fehler aufgetreten. + + We encountered an error preparing the files for edit. + Beim Vorbereiten der Dateien für die Bearbeitung ist ein Fehler aufgetreten. Web files @@ -384,6 +393,7 @@ Organisations-ID: {3} Whether it’s HTML, CSS, JS, or Liquid code, just describe what you need and let AI build it for you. + Ob es sich um HTML-, CSS-, JS- oder Liquid-Code handelt, beschreiben Sie einfach, was Sie benötigen, und lassen Sie es von KI für Sie erstellen. You are editing a live, public site diff --git a/loc/translations-import/vscode-powerplatform.es.xlf b/loc/translations-import/vscode-powerplatform.es.xlf index 494b7919..78dec27b 100644 --- a/loc/translations-import/vscode-powerplatform.es.xlf +++ b/loc/translations-import/vscode-powerplatform.es.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. Creando {0}... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + Entorno predeterminado: {0} + Display Name: {0} Unique Name: {1} @@ -147,9 +152,9 @@ Tipo: {3} Failed to fetch some files. No se pudieron capturar algunos archivos. - - Failed to get file ready for edit. - No se pudo preparar el archivo para su edición. + + Failed to get file ready for edit: {0} + No se pudo preparar el archivo para su edición: {0} Fetching your file ... @@ -304,10 +309,9 @@ Id. de organización: {3} The {0} represents the profile type (Admin vs Dataverse) Tipo de perfil: {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - Recurso: {0} + + Response data is empty + Los datos de respuesta están vacíos Saving your file ... @@ -375,9 +379,9 @@ Id. de organización: {3} The {0} represents auth profile's user name (email address)) Usuario: {0} - - We encountered an error preparing the file for edit. - Se detectó un error al preparar el archivo para su edición. + + We encountered an error preparing the files for edit. + Se detectó un error al preparar los archivos para su edición. Web files diff --git a/loc/translations-import/vscode-powerplatform.fr.xlf b/loc/translations-import/vscode-powerplatform.fr.xlf index fab25d91..c09dde59 100644 --- a/loc/translations-import/vscode-powerplatform.fr.xlf +++ b/loc/translations-import/vscode-powerplatform.fr.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. Création de {0}... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + Environnement par défaut : {0} + Display Name: {0} Unique Name: {1} @@ -147,9 +152,9 @@ Type : {3} Failed to fetch some files. Échec de récupération de certains fichiers. - - Failed to get file ready for edit. - Impossible de préparer le fichier pour la modification. + + Failed to get file ready for edit: {0} + Impossible de préparer le fichier pour la modification : {0} Fetching your file ... @@ -304,10 +309,9 @@ ID d’organisation : {3} The {0} represents the profile type (Admin vs Dataverse) Type de profil : {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - Ressource : {0} + + Response data is empty + Les données de la réponse sont vides Saving your file ... @@ -375,9 +379,9 @@ ID d’organisation : {3} The {0} represents auth profile's user name (email address)) Utilisateur : {0} - - We encountered an error preparing the file for edit. - Une erreur s’est produite lors de la préparation du fichier pour la modification. + + We encountered an error preparing the files for edit. + Nous avons rencontré une erreur lors de la préparation des fichiers à modifier. Web files diff --git a/loc/translations-import/vscode-powerplatform.it.xlf b/loc/translations-import/vscode-powerplatform.it.xlf index b48ff89b..c7fc7846 100644 --- a/loc/translations-import/vscode-powerplatform.it.xlf +++ b/loc/translations-import/vscode-powerplatform.it.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. Creazione di {0} in corso... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + Ambiente predefinito: {0} + Display Name: {0} Unique Name: {1} @@ -147,9 +152,9 @@ Tipo: {3} Failed to fetch some files. Impossibile recuperare alcuni file. - - Failed to get file ready for edit. - Impossibile preparare il file per la modifica. + + Failed to get file ready for edit: {0} + Impossibile preparare il file per la modifica: {0} Fetching your file ... @@ -304,10 +309,9 @@ ID organizzazione: {3} The {0} represents the profile type (Admin vs Dataverse) Tipo di profilo: {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - Risorsa: {0} + + Response data is empty + I dati della risposta sono vuoti Saving your file ... @@ -375,9 +379,9 @@ ID organizzazione: {3} The {0} represents auth profile's user name (email address)) Utente: {0} - - We encountered an error preparing the file for edit. - Si è verificato un errore durante la preparazione del file per la modifica. + + We encountered an error preparing the files for edit. + Si è verificato un errore durante la preparazione dei file per la modifica. Web files diff --git a/loc/translations-import/vscode-powerplatform.ja.xlf b/loc/translations-import/vscode-powerplatform.ja.xlf index 3e642e0c..40c3c565 100644 --- a/loc/translations-import/vscode-powerplatform.ja.xlf +++ b/loc/translations-import/vscode-powerplatform.ja.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. {0} を作成しています... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + 既定の環境: {0} + Display Name: {0} Unique Name: {1} @@ -147,9 +152,9 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca Failed to fetch some files. 一部のファイルを取得できませんでした。 - - Failed to get file ready for edit. - ファイルの編集準備に失敗しました。 + + Failed to get file ready for edit: {0} + ファイルの編集準備に失敗しました: {0} Fetching your file ... @@ -304,10 +309,9 @@ URL: {1} The {0} represents the profile type (Admin vs Dataverse) プロファイルの種類: {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - リソース: {0} + + Response data is empty + 応答データが空です Saving your file ... @@ -375,9 +379,9 @@ URL: {1} The {0} represents auth profile's user name (email address)) ユーザー: {0} - - We encountered an error preparing the file for edit. - ファイルの編集準備中にエラーが発生しました。 + + We encountered an error preparing the files for edit. + ファイルの編集準備でエラーが発生しました。 Web files diff --git a/loc/translations-import/vscode-powerplatform.ko.xlf b/loc/translations-import/vscode-powerplatform.ko.xlf index bd0e4ff0..2444a39a 100644 --- a/loc/translations-import/vscode-powerplatform.ko.xlf +++ b/loc/translations-import/vscode-powerplatform.ko.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. {0} 생성 중... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + 기본 환경: {0} + Display Name: {0} Unique Name: {1} @@ -147,9 +152,9 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca Failed to fetch some files. 일부 파일을 가져오지 못했습니다. - - Failed to get file ready for edit. - 편집을 위해 파일 준비를 하지 못했습니다. + + Failed to get file ready for edit: {0} + 다음을 편집할 파일을 준비하지 못했습니다. {0} Fetching your file ... @@ -304,10 +309,9 @@ URL: {1} The {0} represents the profile type (Admin vs Dataverse) 프로필 종류: {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - 리소스: {0} + + Response data is empty + 응답 데이터가 비어 있음 Saving your file ... @@ -375,9 +379,9 @@ URL: {1} The {0} represents auth profile's user name (email address)) 사용자: {0} - - We encountered an error preparing the file for edit. - 파일 편집을 준비하는 동안 오류가 발생했습니다. + + We encountered an error preparing the files for edit. + 편집할 파일을 준비하는 동안 오류가 발생했습니다. Web files diff --git a/loc/translations-import/vscode-powerplatform.pt-BR.xlf b/loc/translations-import/vscode-powerplatform.pt-BR.xlf index 870c76ed..9c035059 100644 --- a/loc/translations-import/vscode-powerplatform.pt-BR.xlf +++ b/loc/translations-import/vscode-powerplatform.pt-BR.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. Criando {0}... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + Ambiente Padrão: {0} + Display Name: {0} Unique Name: {1} @@ -147,9 +152,9 @@ Tipo: {3} Failed to fetch some files. Falha ao buscar alguns arquivos. - - Failed to get file ready for edit. - Falha ao preparar o arquivo para edição. + + Failed to get file ready for edit: {0} + Falha ao preparar o arquivo para edição: {0} Fetching your file ... @@ -304,10 +309,9 @@ ID da Organização: {3} The {0} represents the profile type (Admin vs Dataverse) Tipo de Perfil: {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - Recurso: {0} + + Response data is empty + Os dados da resposta estão vazios Saving your file ... @@ -375,9 +379,9 @@ ID da Organização: {3} The {0} represents auth profile's user name (email address)) Usuário: {0} - - We encountered an error preparing the file for edit. - Encontramos um erro ao preparar o arquivo para edição. + + We encountered an error preparing the files for edit. + Encontramos um erro ao preparar os arquivos para edição. Web files diff --git a/loc/translations-import/vscode-powerplatform.ru.xlf b/loc/translations-import/vscode-powerplatform.ru.xlf index d226621e..a9a40833 100644 --- a/loc/translations-import/vscode-powerplatform.ru.xlf +++ b/loc/translations-import/vscode-powerplatform.ru.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. Создание {0}... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + Среда по умолчанию: {0} + Display Name: {0} Unique Name: {1} @@ -147,9 +152,9 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca Failed to fetch some files. Не удалось получить некоторые файлы. - - Failed to get file ready for edit. - Не удалось получить файл для редактирования. + + Failed to get file ready for edit: {0} + Не удалось получить файл для редактирования: {0} Fetching your file ... @@ -304,10 +309,9 @@ URL-адрес: {1} The {0} represents the profile type (Admin vs Dataverse) Вид профиля: {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - Ресурс: {0} + + Response data is empty + Данные ответа пусты Saving your file ... @@ -375,9 +379,9 @@ URL-адрес: {1} The {0} represents auth profile's user name (email address)) Пользователь: {0} - - We encountered an error preparing the file for edit. - Произошла ошибка при подготовке файла к редактированию. + + We encountered an error preparing the files for edit. + Произошла ошибка при подготовке файлов к редактированию. Web files diff --git a/loc/translations-import/vscode-powerplatform.tr.xlf b/loc/translations-import/vscode-powerplatform.tr.xlf index 5040c1bc..2334fecf 100644 --- a/loc/translations-import/vscode-powerplatform.tr.xlf +++ b/loc/translations-import/vscode-powerplatform.tr.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. {0} oluşturuluyor... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + Varsayılan Ortam: {0} + Display Name: {0} Unique Name: {1} @@ -147,9 +152,9 @@ Tür: {3} Failed to fetch some files. Bazı dosyalar alınamadı. - - Failed to get file ready for edit. - Dosya düzenlenmek üzere alınamadı. + + Failed to get file ready for edit: {0} + Dosya düzenlenmek üzere alınamadı: {0} Fetching your file ... @@ -304,10 +309,9 @@ Kuruluş Kimliği: {3} The {0} represents the profile type (Admin vs Dataverse) Profil Türü: {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - Kaynak: {0} + + Response data is empty + Yanıt verileri boş Saving your file ... @@ -375,9 +379,9 @@ Kuruluş Kimliği: {3} The {0} represents auth profile's user name (email address)) Kullanıcı: {0} - - We encountered an error preparing the file for edit. - Dosya düzenlemeye hazırlanırken bir hatayla karşılaştık. + + We encountered an error preparing the files for edit. + Dosyalar düzenleme için hazırlanırken bir hatayla karşılaştık. Web files diff --git a/loc/translations-import/vscode-powerplatform.zh-CN.xlf b/loc/translations-import/vscode-powerplatform.zh-CN.xlf index b12e879d..c22d9d80 100644 --- a/loc/translations-import/vscode-powerplatform.zh-CN.xlf +++ b/loc/translations-import/vscode-powerplatform.zh-CN.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. 正在创建 {0}... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + 默认环境: {0} + Display Name: {0} Unique Name: {1} @@ -147,9 +152,9 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca Failed to fetch some files. 无法获取部分文件。 - - Failed to get file ready for edit. - 无法获取可供编辑的文件。 + + Failed to get file ready for edit: {0} + 无法获取可供编辑的文件: {0} Fetching your file ... @@ -304,10 +309,9 @@ URL: {1} The {0} represents the profile type (Admin vs Dataverse) 配置文件种类: {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - 资源: {0} + + Response data is empty + 响应数据为空 Saving your file ... @@ -375,9 +379,9 @@ URL: {1} The {0} represents auth profile's user name (email address)) 用户: {0} - - We encountered an error preparing the file for edit. - 准备可供编辑的文件时出错。 + + We encountered an error preparing the files for edit. + 我们在准备编辑文件时遇到错误。 Web files diff --git a/loc/translations-import/vscode-powerplatform.zh-TW.xlf b/loc/translations-import/vscode-powerplatform.zh-TW.xlf index c6c81d40..b4bef113 100644 --- a/loc/translations-import/vscode-powerplatform.zh-TW.xlf +++ b/loc/translations-import/vscode-powerplatform.zh-TW.xlf @@ -91,6 +91,11 @@ {0} will be replaced by the entity type. 正在建立 {0}... + + Default Environment: {0} + The {0} represents profile's resource/environment URL + 預設環境: {0} + Display Name: {0} Unique Name: {1} @@ -147,9 +152,9 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca Failed to fetch some files. 無法擷取部分檔案。 - - Failed to get file ready for edit. - 無法準備好文件進行編輯。 + + Failed to get file ready for edit: {0} + 無法準備好文件進行編輯: {0} Fetching your file ... @@ -304,10 +309,9 @@ URL: {1} The {0} represents the profile type (Admin vs Dataverse) 設定檔種類: {0} - - Resource: {0} - The {0} represents profile's resource/environment URL - 資源: {0} + + Response data is empty + 回覆資料是空的 Saving your file ... @@ -375,9 +379,9 @@ URL: {1} The {0} represents auth profile's user name (email address)) 使用者: {0} - - We encountered an error preparing the file for edit. - 準備要編輯的檔案時發生錯誤。 + + We encountered an error preparing the files for edit. + 我們在準備要編輯的檔案時發生錯誤。 Web files diff --git a/package-lock.json b/package-lock.json index 3e865fcd..c6a86fdb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@microsoft/generator-powerpages": "1.21.19", "@types/jwt-decode": "2.2.0", "@types/node-fetch": "^2.6.2", + "@types/xmldom": "^0.1.34", "@vscode/extension-telemetry": "^0.6.2", "cockatiel": "^3.1.1", "command-exists": "^1.2.9", @@ -35,6 +36,7 @@ "vscode-languageserver": "^7.0.0", "vscode-languageserver-textdocument": "^1.0.1", "worker-loader": "^3.0.8", + "xmldom": "^0.6.0", "yaml": "^2.2.2" }, "devDependencies": { @@ -102,6 +104,7 @@ "yargs": "^16.2.0" }, "engines": { + "npm": ">=8.3.0", "vscode": "^1.73.0" }, "optionalDependencies": { @@ -732,6 +735,76 @@ "fluid-framework": "^1.3.3" } }, + "node_modules/@fluidframework/azure-client/node_modules/@fluidframework/server-services-client": { + "version": "0.1036.5001", + "resolved": "https://registry.npmjs.org/@fluidframework/server-services-client/-/server-services-client-0.1036.5001.tgz", + "integrity": "sha512-e+Zk2uPcds9yqlalAZ0PpfEAKPPUIpIoQb3YSm42ZVpAgQmLYIRqTpXrmTC7qdeQnSGFJUAliW4DfHBEwpSXlQ==", + "dependencies": { + "@fluidframework/common-utils": "^0.32.2", + "@fluidframework/gitresources": "^0.1036.5001", + "@fluidframework/protocol-base": "^0.1036.5001", + "@fluidframework/protocol-definitions": "^0.1028.2000", + "axios": "^0.26.0", + "crc-32": "1.2.0", + "debug": "^4.1.1", + "json-stringify-safe": "^5.0.1", + "jsrsasign": "^10.2.0", + "jwt-decode": "^3.0.0", + "querystring": "^0.2.0", + "sillyname": "^0.1.0", + "uuid": "^8.3.1" + } + }, + "node_modules/@fluidframework/azure-client/node_modules/@fluidframework/server-services-client/node_modules/@fluidframework/common-utils": { + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@fluidframework/common-utils/-/common-utils-0.32.2.tgz", + "integrity": "sha512-PoGX7/l0vWKt5JaAxcgFOdGje30Q6qSE06YzFIKh9Ba3oq7B60+TFqu7c2ErQt6sNddmvcAcAiLVNaTGAip3vw==", + "dependencies": { + "@fluidframework/common-definitions": "^0.20.1", + "@types/events": "^3.0.0", + "base64-js": "^1.5.1", + "buffer": "^6.0.3", + "events": "^3.1.0", + "lodash": "^4.17.21", + "sha.js": "^2.4.11" + } + }, + "node_modules/@fluidframework/azure-client/node_modules/@fluidframework/server-services-client/node_modules/@fluidframework/protocol-definitions": { + "version": "0.1028.2000", + "resolved": "https://registry.npmjs.org/@fluidframework/protocol-definitions/-/protocol-definitions-0.1028.2000.tgz", + "integrity": "sha512-ZUPCmPFcK7UAK4RkfVWfzQPAWFvYNm6ywP51V42YC38gCGye+Epvyr3beA+FSaHPIZGxm5+Uw52+ykTvmDb2UA==", + "dependencies": { + "@fluidframework/common-definitions": "^0.20.1" + } + }, + "node_modules/@fluidframework/azure-client/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/@fluidframework/azure-client/node_modules/jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" + }, "node_modules/@fluidframework/common-definitions": { "version": "0.20.1", "resolved": "https://registry.npmjs.org/@fluidframework/common-definitions/-/common-definitions-0.20.1.tgz", @@ -1256,14 +1329,6 @@ "@fluidframework/common-definitions": "^0.20.1" } }, - "node_modules/@fluidframework/driver-utils/node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "dependencies": { - "follow-redirects": "^1.14.8" - } - }, "node_modules/@fluidframework/driver-utils/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -1687,6 +1752,26 @@ "@fluidframework/common-definitions": "^0.20.1" } }, + "node_modules/@fluidframework/routerlicious-driver/node_modules/@fluidframework/server-services-client": { + "version": "0.1036.5001", + "resolved": "https://registry.npmjs.org/@fluidframework/server-services-client/-/server-services-client-0.1036.5001.tgz", + "integrity": "sha512-e+Zk2uPcds9yqlalAZ0PpfEAKPPUIpIoQb3YSm42ZVpAgQmLYIRqTpXrmTC7qdeQnSGFJUAliW4DfHBEwpSXlQ==", + "dependencies": { + "@fluidframework/common-utils": "^0.32.2", + "@fluidframework/gitresources": "^0.1036.5001", + "@fluidframework/protocol-base": "^0.1036.5001", + "@fluidframework/protocol-definitions": "^0.1028.2000", + "axios": "^0.26.0", + "crc-32": "1.2.0", + "debug": "^4.1.1", + "json-stringify-safe": "^5.0.1", + "jsrsasign": "^10.2.0", + "jwt-decode": "^3.0.0", + "querystring": "^0.2.0", + "sillyname": "^0.1.0", + "uuid": "^8.3.1" + } + }, "node_modules/@fluidframework/routerlicious-driver/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -1710,6 +1795,11 @@ "ieee754": "^1.2.1" } }, + "node_modules/@fluidframework/routerlicious-driver/node_modules/jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" + }, "node_modules/@fluidframework/runtime-definitions": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/@fluidframework/runtime-definitions/-/runtime-definitions-1.3.7.tgz", @@ -1895,84 +1985,6 @@ "ieee754": "^1.2.1" } }, - "node_modules/@fluidframework/server-services-client": { - "version": "0.1036.5001", - "resolved": "https://registry.npmjs.org/@fluidframework/server-services-client/-/server-services-client-0.1036.5001.tgz", - "integrity": "sha512-e+Zk2uPcds9yqlalAZ0PpfEAKPPUIpIoQb3YSm42ZVpAgQmLYIRqTpXrmTC7qdeQnSGFJUAliW4DfHBEwpSXlQ==", - "dependencies": { - "@fluidframework/common-utils": "^0.32.2", - "@fluidframework/gitresources": "^0.1036.5001", - "@fluidframework/protocol-base": "^0.1036.5001", - "@fluidframework/protocol-definitions": "^0.1028.2000", - "axios": "^0.26.0", - "crc-32": "1.2.0", - "debug": "^4.1.1", - "json-stringify-safe": "^5.0.1", - "jsrsasign": "^10.2.0", - "jwt-decode": "^3.0.0", - "querystring": "^0.2.0", - "sillyname": "^0.1.0", - "uuid": "^8.3.1" - } - }, - "node_modules/@fluidframework/server-services-client/node_modules/@fluidframework/common-utils": { - "version": "0.32.2", - "resolved": "https://registry.npmjs.org/@fluidframework/common-utils/-/common-utils-0.32.2.tgz", - "integrity": "sha512-PoGX7/l0vWKt5JaAxcgFOdGje30Q6qSE06YzFIKh9Ba3oq7B60+TFqu7c2ErQt6sNddmvcAcAiLVNaTGAip3vw==", - "dependencies": { - "@fluidframework/common-definitions": "^0.20.1", - "@types/events": "^3.0.0", - "base64-js": "^1.5.1", - "buffer": "^6.0.3", - "events": "^3.1.0", - "lodash": "^4.17.21", - "sha.js": "^2.4.11" - } - }, - "node_modules/@fluidframework/server-services-client/node_modules/@fluidframework/protocol-definitions": { - "version": "0.1028.2000", - "resolved": "https://registry.npmjs.org/@fluidframework/protocol-definitions/-/protocol-definitions-0.1028.2000.tgz", - "integrity": "sha512-ZUPCmPFcK7UAK4RkfVWfzQPAWFvYNm6ywP51V42YC38gCGye+Epvyr3beA+FSaHPIZGxm5+Uw52+ykTvmDb2UA==", - "dependencies": { - "@fluidframework/common-definitions": "^0.20.1" - } - }, - "node_modules/@fluidframework/server-services-client/node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "dependencies": { - "follow-redirects": "^1.14.8" - } - }, - "node_modules/@fluidframework/server-services-client/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/@fluidframework/server-services-client/node_modules/jwt-decode": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", - "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" - }, "node_modules/@fluidframework/shared-object-base": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/@fluidframework/shared-object-base/-/shared-object-base-1.3.7.tgz", @@ -3430,6 +3442,11 @@ "integrity": "sha512-56/MAlX5WMsPVbOg7tAxnYvNYMMWr/QJiIp6BxVSW3JJXUVzzOn64qW8TzQyMSqSUFM2+PVI4aUHcHOzIz/1tg==", "dev": true }, + "node_modules/@types/xmldom": { + "version": "0.1.34", + "resolved": "https://registry.npmjs.org/@types/xmldom/-/xmldom-0.1.34.tgz", + "integrity": "sha512-7eZFfxI9XHYjJJuugddV6N5YNeXgQE1lArWOcd1eCOKWb/FGs5SIjacSYuEJuwhsGS3gy4RuZ5EUIcqYscuPDA==" + }, "node_modules/@types/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", @@ -5012,8 +5029,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/at-least-node": { "version": "1.0.0", @@ -5049,11 +5065,26 @@ } }, "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.28.0.tgz", + "integrity": "sha512-Tu7NYoGY4Yoc7I+Npf9HhUMtEEpV7ZiLH9yndTCoNhcpBH0kwcvFbzYN9/u5QKI5A6uefjsNNWaz5olJVYS62Q==", "dependencies": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, "node_modules/azure-devops-node-api": { @@ -6399,7 +6430,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -7117,7 +7147,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -11277,9 +11306,9 @@ } }, "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", + "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==" }, "node_modules/is-absolute": { "version": "1.0.0", @@ -12348,9 +12377,9 @@ ] }, "node_modules/jsrsasign": { - "version": "10.8.6", - "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.8.6.tgz", - "integrity": "sha512-bQmbVtsfbgaKBTWCKiDCPlUPbdlRIK/FzSwT3BzIgZl/cU6TqXu6pZJsCI/dJVrZ9Gir5GC4woqw9shH/v7MBw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-11.0.0.tgz", + "integrity": "sha512-BtRwVKS+5dsgPpAtzJcpo5OoWjSs1/zllSBG0+8o8/aV0Ki76m6iZwHnwnsqoTdhfFZDN1XIdcaZr5ZkP+H2gg==", "funding": { "url": "https://github.com/kjur/jsrsasign#donations" } @@ -15467,9 +15496,9 @@ } }, "node_modules/pac-resolver/node_modules/ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", + "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==", "dev": true }, "node_modules/package-hash": { @@ -21392,6 +21421,14 @@ "node": ">=4.0" } }, + "node_modules/xmldom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.6.0.tgz", + "integrity": "sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/xmlhttprequest-ssl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", diff --git a/package.json b/package.json index 6b09ad17..2d648033 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,8 @@ "email": "PPTools@microsoft.com" }, "engines": { - "vscode": "^1.73.0" + "vscode": "^1.73.0", + "npm": ">=8.3.0" }, "extensionKind": [ "workspace" @@ -659,7 +660,7 @@ "command": "powerPlatform.previewCurrentActiveUsers", "alt": "current active users", "group": "navigation", - "when": "never" + "when": "isWeb && virtualWorkspace" } ], "commandPalette": [ @@ -954,7 +955,7 @@ { "id": "powerpages.collaborationView", "name": "People On The Site", - "when": "never", + "when": "isWeb && virtualWorkspace", "contextualTitle": "People On The Site", "visibility": "visible" } @@ -1021,6 +1022,8 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "1.0.2", + "@microsoft/1ds-core-js": "4.0.5", + "@microsoft/1ds-post-js": "4.0.5", "@types/chai": "^4.2.14", "@types/command-exists": "^1.2.0", "@types/debug": "^4.1.9", @@ -1079,17 +1082,16 @@ "webpack": "^5.76.0", "webpack-cli": "^4.7.0", "webpack-stream": "^7.0.0", - "yargs": "^16.2.0", - "@microsoft/1ds-core-js": "4.0.5", - "@microsoft/1ds-post-js": "4.0.5" + "yargs": "^16.2.0" }, "dependencies": { "@fluidframework/azure-client": "^1.1.1", - "@microsoft/generator-powerpages": "1.21.19", - "@types/jwt-decode": "2.2.0", "@microsoft/1ds-core-js": "4.0.5", "@microsoft/1ds-post-js": "4.0.5", + "@microsoft/generator-powerpages": "1.21.19", + "@types/jwt-decode": "2.2.0", "@types/node-fetch": "^2.6.2", + "@types/xmldom": "^0.1.34", "@vscode/extension-telemetry": "^0.6.2", "cockatiel": "^3.1.1", "command-exists": "^1.2.9", @@ -1110,6 +1112,7 @@ "vscode-languageserver": "^7.0.0", "vscode-languageserver-textdocument": "^1.0.1", "worker-loader": "^3.0.8", + "xmldom": "^0.6.0", "yaml": "^2.2.2" }, "__metadata": { @@ -1121,5 +1124,9 @@ "optionalDependencies": { "bufferutil": "^4.0.6", "utf-8-validate": "^5.0.9" + }, + "overrides": { + "jsrsasign": "^11.0.0", + "axios": "^0.28.0" } -} \ No newline at end of file +} diff --git a/src/common/OneDSLoggerTelemetry/oneDSLogger.ts b/src/common/OneDSLoggerTelemetry/oneDSLogger.ts index e6a42737..15a6826a 100644 --- a/src/common/OneDSLoggerTelemetry/oneDSLogger.ts +++ b/src/common/OneDSLoggerTelemetry/oneDSLogger.ts @@ -16,7 +16,6 @@ import { EXTENSION_ID } from "../../client/constants"; import {OneDSCollectorEventName} from "./EventContants"; import { telemetryEventNames } from "../../web/client/telemetry/constants"; import { region } from "../telemetry-generated/buildRegionConfiguration"; -import { geoMappingsToAzureRegion } from "./shortNameMappingToAzureRegion"; interface IInstrumentationSettings { endpointURL: string; @@ -139,10 +138,6 @@ export class OneDSLogger implements ITelemetryLogger{ endpointURL: 'https://self.pipe.aria.int.microsoft.com/OneCollector/1.0/', instrumentationKey: 'ffdb4c99ca3a4ad5b8e9ffb08bf7da0d-65357ff3-efcd-47fc-b2fd-ad95a52373f4-7402' }; - let geoName = 'us'; - if(geoMappingsToAzureRegion[geo!]) { - geoName = geoMappingsToAzureRegion[geo!].geoName; - } switch (buildRegion) { case 'tie': case 'test': @@ -150,7 +145,7 @@ export class OneDSLogger implements ITelemetryLogger{ break; case 'prod': case 'preview': - switch (geoName) { + switch (geo) { case 'us': case 'br': case 'jp': diff --git a/src/common/copilot/IntelligenceApiService.ts b/src/common/copilot/IntelligenceApiService.ts index dc133a71..07add9e6 100644 --- a/src/common/copilot/IntelligenceApiService.ts +++ b/src/common/copilot/IntelligenceApiService.ts @@ -15,12 +15,13 @@ import { EXTENSION_NAME } from "../../client/constants"; const clientType = EXTENSION_NAME + '-' + getExtensionType(); const clientVersion = getExtensionVersion(); -export async function sendApiRequest(userPrompt: UserPrompt[], activeFileParams: IActiveFileParams, orgID: string, apiToken: string, sessionID: string, entityName: string, entityColumns: string[], telemetry: ITelemetry, aibEndpoint: string | null, geoName: string | null) { +export async function sendApiRequest(userPrompt: UserPrompt[], activeFileParams: IActiveFileParams, orgID: string, apiToken: string, sessionID: string, entityName: string, entityColumns: string [], telemetry: ITelemetry, aibEndpoint: string | null, geoName: string | null) { if (!aibEndpoint) { return NetworkError; } + // eslint-disable-next-line prefer-const let requestBody = { "question": userPrompt[0].displayText, "top": 1, diff --git a/src/common/copilot/PowerPagesCopilot.ts b/src/common/copilot/PowerPagesCopilot.ts index 5767c3b5..535377c7 100644 --- a/src/common/copilot/PowerPagesCopilot.ts +++ b/src/common/copilot/PowerPagesCopilot.ts @@ -21,7 +21,7 @@ import { sendTelemetryEvent } from "./telemetry/copilotTelemetry"; import { INTELLIGENCE_SCOPE_DEFAULT, PROVIDER_ID } from "../../web/client/common/constants"; import { getIntelligenceEndpoint } from "../ArtemisService"; import TelemetryReporter from "@vscode/extension-telemetry"; -import { getEntityColumns, getEntityName } from "./dataverseMetadata"; +import { getEntityColumns, getEntityName, getFormXml } from "./dataverseMetadata"; import { COPILOT_STRINGS } from "./assets/copilotStrings"; import { isWithinTokenLimit, encode } from "gpt-tokenizer"; @@ -344,17 +344,24 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider { this.sendMessageToWebview({ type: 'userName', value: userName }); - let entityName = ""; - let entityColumns: string[] = []; + let metadataInfo = { entityName: '', formName: '' }; + let entityInfo : string [] = []; if (activeFileParams.dataverseEntity == "adx_entityform" || activeFileParams.dataverseEntity == 'adx_entitylist') { - entityName = await getEntityName(telemetry, sessionID, activeFileParams.dataverseEntity); + metadataInfo = await getEntityName(telemetry, sessionID, activeFileParams.dataverseEntity); const dataverseToken = await dataverseAuthentication(activeOrgUrl, true); - entityColumns = await getEntityColumns(entityName, activeOrgUrl, dataverseToken, telemetry, sessionID); + if (activeFileParams.dataverseEntity == "adx_entityform") { + const formColumns = await getFormXml(metadataInfo.entityName, metadataInfo.formName, activeOrgUrl, dataverseToken, telemetry, sessionID); + entityInfo = formColumns; + } else { + const entityColumns = await getEntityColumns(metadataInfo.entityName, activeOrgUrl, dataverseToken, telemetry, sessionID); + entityInfo = entityColumns; + } + } - return sendApiRequest(data, activeFileParams, orgID, intelligenceApiToken, sessionID, entityName, entityColumns, telemetry, this.aibEndpoint, this.geoName); + return sendApiRequest(data, activeFileParams, orgID, intelligenceApiToken, sessionID, metadataInfo.entityName, entityInfo, telemetry, this.aibEndpoint, this.geoName); }) .then(apiResponse => { this.sendMessageToWebview({ type: 'apiResponse', value: apiResponse }); diff --git a/src/common/copilot/dataverseMetadata.ts b/src/common/copilot/dataverseMetadata.ts index fb23b0e1..14bdf8a3 100644 --- a/src/common/copilot/dataverseMetadata.ts +++ b/src/common/copilot/dataverseMetadata.ts @@ -10,7 +10,8 @@ import yaml from 'yaml'; import { ITelemetry } from "../../client/telemetry/ITelemetry"; import { sendTelemetryEvent } from "./telemetry/copilotTelemetry"; import { CopilotDataverseMetadataFailureEvent, CopilotDataverseMetadataSuccessEvent, CopilotGetEntityFailureEvent, CopilotYamlParsingFailureEvent } from "./telemetry/telemetryConstants"; -import { getFileLogicalEntityName } from "../../web/client/utilities/fileAndEntityUtil"; +import { getFileLogicalEntityName, getFormLogicalName } from "../../web/client/utilities/fileAndEntityUtil"; +import { DOMParser } from "xmldom"; interface Attribute { LogicalName: string; @@ -44,6 +45,32 @@ export async function getEntityColumns(entityName: string, orgUrl: string, apiTo } } +export async function getFormXml(entityName: string, formName: string, orgUrl: string, apiToken: string, telemetry: ITelemetry, sessionID: string): Promise<(string [])>{ + try { + const dataverseURL = `${orgUrl.endsWith('/') ? orgUrl : orgUrl.concat('/')}api/data/v9.2/systemforms?$filter=objecttypecode eq '${entityName}' and name eq '${formName}'`; + const requestInit: RequestInit = { + method: "GET", + headers: { + 'Content-Type': "application/json", + Authorization: `Bearer ${apiToken}`, + }, + }; + + const startTime = performance.now(); + const jsonResponse = await fetchJsonResponse(dataverseURL, requestInit); + const endTime = performance.now(); + const responseTime = endTime - startTime || 0; + const formxml =getFormXMLFromResponse(jsonResponse); + + sendTelemetryEvent(telemetry, { eventName: CopilotDataverseMetadataSuccessEvent, copilotSessionId: sessionID, durationInMills: responseTime, orgUrl: orgUrl }) + return parseXML(formxml); + + } catch (error) { + sendTelemetryEvent(telemetry, { eventName: CopilotDataverseMetadataFailureEvent, copilotSessionId: sessionID, error: error as Error, orgUrl: orgUrl }) + return []; + } +} + // eslint-disable-next-line @typescript-eslint/no-explicit-any async function fetchJsonResponse(url: string, requestInit: RequestInit): Promise { const response = await fetch(url, requestInit); @@ -64,9 +91,51 @@ function getAttributesFromResponse(jsonResponse: any): Attribute[] { return []; } +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function getFormXMLFromResponse(jsonResponse: any): string { + if (jsonResponse.value[0].formxml) { + return jsonResponse.value[0].formxml; + } + + return ''; +} + +function parseXML(formXml: string) { + const parser = new DOMParser(); + const xmlDoc = parser.parseFromString(formXml, "text/xml"); + + // Get all 'row' elements + const rows = xmlDoc.getElementsByTagName('row'); + + const result = []; + + // Iterate over all 'row' elements + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + + // Get the 'label' and 'control' elements within the current 'row' + const label = row.getElementsByTagName('label')[0]; + const control = row.getElementsByTagName('control')[0]; + + // If both 'label' and 'control' elements exist, create an object and add it to the result array + if (label && control) { + const description = label.getAttribute('description'); + const datafieldname = control.getAttribute('datafieldname'); + //const classid = control.getAttribute('classid'); + + if (description && datafieldname) { + result.push(description, datafieldname); + } + } + } + + return result +} + -export async function getEntityName(telemetry: ITelemetry, sessionID: string, dataverseEntity: string): Promise { +export async function getEntityName(telemetry: ITelemetry, sessionID: string, dataverseEntity: string): Promise<{entityName: string, formName: string}> { let entityName = ''; + let formName = ''; try { const activeEditor = vscode.window.activeTextEditor; @@ -86,15 +155,17 @@ export async function getEntityName(telemetry: ITelemetry, sessionID: string, da const yamlContent = diskRead.readFileSync(yamlFilePath, 'utf8'); const parsedData = parseYamlContent(yamlContent, telemetry, sessionID, dataverseEntity); entityName = parsedData['adx_entityname'] || parsedData['adx_targetentitylogicalname']; + formName = parsedData['adx_formname']; } else if (!IS_DESKTOP) { entityName = getFileLogicalEntityName(document.uri.fsPath); + formName = getFormLogicalName(document.uri.fsPath); } } } catch (error) { sendTelemetryEvent(telemetry, { eventName: CopilotGetEntityFailureEvent, copilotSessionId: sessionID, dataverseEntity: dataverseEntity, error: error as Error }); entityName = ''; } - return entityName; + return {entityName, formName}; } async function getMatchingFiles(folderPath: string, fileNameFirstPart: string): Promise { diff --git a/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts b/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts index d3c86f8f..72dd3f37 100644 --- a/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts +++ b/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts @@ -19,8 +19,6 @@ export async function copilotNotificationPanel(context: vscode.ExtensionContext, NotificationPanel = createNotificationPanel(); - console.log(telemetry, telemetryData, countOfActivePortals) - const { notificationCssUri, notificationJsUri, copilotImageUri, arrowImageUri } = getWebviewURIs(context, NotificationPanel); const nonce = getNonce(); diff --git a/src/web/client/WebExtensionContext.ts b/src/web/client/WebExtensionContext.ts index a24d96ea..14a80ea7 100644 --- a/src/web/client/WebExtensionContext.ts +++ b/src/web/client/WebExtensionContext.ts @@ -402,7 +402,8 @@ class WebExtensionContext implements IWebExtensionContext { encodeAsBase64: boolean, mimeType?: string, isContentLoaded?: boolean, - logicalEntityName?: string + logicalEntityName?: string, + logicalFormName?: string ) { this.fileDataMap.setEntity( fileUri, @@ -415,7 +416,8 @@ class WebExtensionContext implements IWebExtensionContext { encodeAsBase64, mimeType, isContentLoaded, - logicalEntityName); + logicalEntityName, + logicalFormName); } public async updateEntityDetailsInContext( diff --git a/src/web/client/common/constants.ts b/src/web/client/common/constants.ts index 060a9ae1..6ef7ecfe 100644 --- a/src/web/client/common/constants.ts +++ b/src/web/client/common/constants.ts @@ -98,7 +98,7 @@ export enum queryParameters { ORG_URL = "orgurl", REGION = "region", ENV_ID = "envid", - GEO = "geo", + GEO = "geo", // User geo location ENABLE_MULTIFILE = "enablemultifile", WEBSITE_PREVIEW_URL = "websitepreviewurl", ENTITY = "entity", diff --git a/src/web/client/context/fileData.ts b/src/web/client/context/fileData.ts index 2097ae12..e5389794 100644 --- a/src/web/client/context/fileData.ts +++ b/src/web/client/context/fileData.ts @@ -30,6 +30,7 @@ export class FileData implements IFileData { private _mimeType: string | undefined; private _isContentLoaded: boolean | undefined; private _logicalEntityName: string | undefined; + private _logicalFormName: string | undefined; // Getters public get entityName(): string { @@ -70,6 +71,11 @@ export class FileData implements IFileData { return this._logicalEntityName; } + public get logicalFormName(): string | undefined { + return this._logicalFormName; + } + + // Setters public set setHasDirtyChanges(value: boolean) { this._hasDirtyChanges = value; @@ -85,6 +91,10 @@ export class FileData implements IFileData { this._logicalEntityName = value; } + public set setLogicalFormName(value: string | undefined) { + this._logicalFormName = value; + } + constructor( entityId: string, entityName: string, @@ -95,7 +105,8 @@ export class FileData implements IFileData { encodeAsBase64?: boolean, mimeType?: string, isContentLoaded?: boolean, - logicalEntityName?: string + logicalEntityName?: string, + logicalFormName?: string ) { this._entityId = entityId; this._entityName = entityName; @@ -109,5 +120,6 @@ export class FileData implements IFileData { this._hasDiffViewTriggered = false; this._isContentLoaded = isContentLoaded; this._logicalEntityName = logicalEntityName; + this._logicalFormName = logicalFormName; } } diff --git a/src/web/client/context/fileDataMap.ts b/src/web/client/context/fileDataMap.ts index 47979077..b3b65693 100644 --- a/src/web/client/context/fileDataMap.ts +++ b/src/web/client/context/fileDataMap.ts @@ -25,7 +25,8 @@ export class FileDataMap { isBase64Encoded: boolean, mimeType?: string, isContentLoaded?: boolean, - logicalEntityName?: string + logicalEntityName?: string, + logicalFormName?: string ) { const fileData = new FileData( entityId, @@ -37,7 +38,8 @@ export class FileDataMap { isBase64Encoded, mimeType, isContentLoaded, - logicalEntityName + logicalEntityName, + logicalFormName ); this.fileMap.set(vscode.Uri.parse(fileUri).fsPath, fileData); } diff --git a/src/web/client/dal/remoteFetchProvider.ts b/src/web/client/dal/remoteFetchProvider.ts index 1ae78b2d..0e9802e5 100644 --- a/src/web/client/dal/remoteFetchProvider.ts +++ b/src/web/client/dal/remoteFetchProvider.ts @@ -14,7 +14,7 @@ import { isWebfileContentLoadNeeded, setFileContent, } from "../utilities/commonUtil"; -import { getCustomRequestURL, getLogicalEntityName, getMappingEntityContent, getMappingEntityId, getMimeType, getRequestURL } from "../utilities/urlBuilderUtil"; +import { getCustomRequestURL, getMappingEntityContent, getMappingEntityId, getMetadataInfo, getMimeType, getRequestURL } from "../utilities/urlBuilderUtil"; import { getCommonHeaders } from "../common/authenticationProvider"; import * as Constants from "../common/constants"; import { ERRORS, showErrorDialog } from "../common/errorHandler"; @@ -24,6 +24,7 @@ import { getAttributePath, getEntity, getLogicalEntityParameter, + getLogicalFormNameParameter, isBase64Encoded, } from "../utilities/schemaHelperUtil"; import WebExtensionContext from "../WebExtensionContext"; @@ -431,6 +432,7 @@ async function createFile( // By default content is preloaded for all the files except for non-text webfiles for V2 const isPreloadedContent = mappingEntityFetchQuery ? isWebfileContentLoadNeeded(fileNameWithExtension, fileUri) : true; const logicalEntityName = getLogicalEntityParameter(entityName); + const logicalFormNameParameter = getLogicalFormNameParameter(entityName); // update func for webfiles for V2 const attributePath: IAttributePath = getAttributePath( @@ -453,6 +455,9 @@ async function createFile( fileContent = getAttributeContent(result, attributePath, entityName, entityId); } + const metadataKeys = [logicalEntityName, logicalFormNameParameter]; + const metadataValues = getMetadataInfo(result, metadataKeys.filter(key => key !== undefined) as string[]); + await createVirtualFile( portalsFS, fileUri, @@ -468,7 +473,8 @@ async function createFile( mimeType ?? result[Constants.MIMETYPE], isPreloadedContent, mappingEntityId, - getLogicalEntityName(result, logicalEntityName), + metadataValues[logicalEntityName ?? ''] ?? '', + metadataValues[logicalFormNameParameter ?? ''] ?? '', rootWebPageId, ); } @@ -620,6 +626,7 @@ async function createVirtualFile( isPreloadedContent?: boolean, mappingEntityId?: string, logicalEntityName?: string, + logicalFormName?: string, rootWebPageId?: string, ) { // Maintain file information in context @@ -634,7 +641,8 @@ async function createVirtualFile( encodeAsBase64, mimeType, isPreloadedContent, - logicalEntityName + logicalEntityName, + logicalFormName ); // Call file system provider write call for buffering file data in VFS diff --git a/src/web/client/extension.ts b/src/web/client/extension.ts index df30945f..581b231d 100644 --- a/src/web/client/extension.ts +++ b/src/web/client/extension.ts @@ -92,12 +92,9 @@ export function activate(context: vscode.ExtensionContext): void { ); } } - const geo = queryParamsMap.get('geo')?.toLowerCase(); - // Authenticated scenario. Pass the geo to OneDSLogger for data boundary - if(geo){ - oneDSLoggerWrapper.instantiate(geo); - } - + const orgId = queryParamsMap.get(queryParameters.ORG_ID) as string; + const orgGeo = await fetchArtemisData(orgId); + oneDSLoggerWrapper.instantiate(orgGeo); if ( !checkMandatoryParameters( appName, @@ -601,6 +598,16 @@ function isActiveDocument(fileFsPath: string): boolean { ); } +async function fetchArtemisData(orgId: string) : Promise { + const artemisResponse = await fetchArtemisResponse(orgId, WebExtensionContext.telemetry.getTelemetryReporter()); + if (!artemisResponse) { + // Todo: Log in error telemetry. Runtime maintains another table for this kind of failure. We should do the same. + return ''; + } + + return artemisResponse[0].geoName as string; +} + async function logArtemisTelemetry() { try { @@ -608,13 +615,7 @@ async function logArtemisTelemetry() { queryParameters.ORG_ID ) as string - const artemisResponse = await fetchArtemisResponse(orgId, WebExtensionContext.telemetry.getTelemetryReporter()); - - if (!artemisResponse) { - return; - } - - const { geoName } = artemisResponse[0]; + const geoName= fetchArtemisData(orgId); WebExtensionContext.telemetry.sendInfoTelemetry(telemetryEventNames.WEB_EXTENSION_ARTEMIS_RESPONSE, { orgId: orgId, geoName: String(geoName) }); } catch (error) { diff --git a/src/web/client/schema/constants.ts b/src/web/client/schema/constants.ts index 60cfb451..af80629b 100644 --- a/src/web/client/schema/constants.ts +++ b/src/web/client/schema/constants.ts @@ -35,6 +35,7 @@ export enum schemaEntityKey { export enum schemaMetaDataKey { DATAVERSE_LOGICAL_ENTITY_NAME = "_dataverselogicalentityname", + DATAVERSE_FORM_NAME = "_dataverseformname", } export enum schemaEntityName { diff --git a/src/web/client/schema/portalSchema.ts b/src/web/client/schema/portalSchema.ts index 5ac54ef7..950e7956 100644 --- a/src/web/client/schema/portalSchema.ts +++ b/src/web/client/schema/portalSchema.ts @@ -379,7 +379,7 @@ export const portal_schema_V2 = { relationships: "", _vscodeentityname: "basicforms", _dataverseenityname: "powerpagecomponents", - _dataverseentitymetadata: new Map([["_dataverselogicalentityname", "content.entityname"]]), + _dataverseentitymetadata: new Map([["_dataverselogicalentityname", "content.entityname"], ["_dataverseformname", "content.formname"]]), _displayname: "Basic Forms", _etc: "10271", _primaryidfield: "powerpagecomponentid", diff --git a/src/web/client/telemetry/webExtensionTelemetry.ts b/src/web/client/telemetry/webExtensionTelemetry.ts index db39fffc..b561c883 100644 --- a/src/web/client/telemetry/webExtensionTelemetry.ts +++ b/src/web/client/telemetry/webExtensionTelemetry.ts @@ -29,10 +29,14 @@ export class WebExtensionTelemetry { eventName: telemetryEventNames.WEB_EXTENSION_INIT_PATH_PARAMETERS, properties: { appName: this.getPathParameterValue(appName), - entity: this.getPathParameterValue(entity), - entityId: this.getPathParameterValue(entityId) } } + + if (entity && entityId) { + telemetryData.properties.entity = this.getPathParameterValue(entity), + telemetryData.properties.entityId = this.getPathParameterValue(entityId) + } + this._telemetry?.sendTelemetryEvent(telemetryData.eventName, telemetryData.properties); oneDSLoggerWrapper.getLogger().traceInfo(telemetryData.eventName, telemetryData.properties); } @@ -56,6 +60,12 @@ export class WebExtensionTelemetry { referrerSource: queryParamsMap.get(queryParameters.REFERRER_SOURCE) } } + + if (queryParamsMap.has(queryParameters.ENTITY) && queryParamsMap.has(queryParameters.ENTITY_ID)) { + telemetryData.properties.entity = queryParamsMap.get(queryParameters.ENTITY); + telemetryData.properties.entityId = queryParamsMap.get(queryParameters.ENTITY_ID); + } + this._telemetry?.sendTelemetryEvent(telemetryData.eventName, telemetryData.properties); oneDSLoggerWrapper.getLogger().traceInfo(telemetryData.eventName, telemetryData.properties); } diff --git a/src/web/client/test/integration/webExtensionTelemetry.test.ts b/src/web/client/test/integration/webExtensionTelemetry.test.ts index 02dcce9a..94e0a87a 100644 --- a/src/web/client/test/integration/webExtensionTelemetry.test.ts +++ b/src/web/client/test/integration/webExtensionTelemetry.test.ts @@ -69,8 +69,6 @@ describe("webExtensionTelemetry", () => { //Assert const properties = { appName: "", - entity: "", - entityId: "", }; assert.calledOnceWithExactly( sendTelemetryEvent, @@ -100,6 +98,11 @@ describe("webExtensionTelemetry", () => { [queryParameters.REGION, "NAM"], [queryParameters.GEO, "US"], [queryParameters.ENV_ID, "c4dc3686-1e6b-e428-b886-16cd0b9f4918"], + [queryParameters.ENTITY, "webpage"], + [ + queryParameters.ENTITY_ID, + "e5dce21c-f85f-4849-b699-920c0fad5fbf", + ], [queryParameters.REFERRER_SOURCE, "test"], ]); @@ -119,6 +122,8 @@ describe("webExtensionTelemetry", () => { region: queryParamsMap.get(queryParameters.REGION), geo: queryParamsMap.get(queryParameters.GEO), envId: queryParamsMap.get(queryParameters.ENV_ID), + entity: queryParamsMap.get(queryParameters.ENTITY), + entityId: queryParamsMap.get(queryParameters.ENTITY_ID), referrerSource: queryParamsMap.get(queryParameters.REFERRER_SOURCE), // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; diff --git a/src/web/client/utilities/commonUtil.ts b/src/web/client/utilities/commonUtil.ts index f1828075..56562cd7 100644 --- a/src/web/client/utilities/commonUtil.ts +++ b/src/web/client/utilities/commonUtil.ts @@ -151,9 +151,7 @@ export function isCoPresenceEnabled() { ); } - // return isCoPresenceEnabled as boolean; - // this feature is under development and will be enabled in future - return false; + return isCoPresenceEnabled as boolean; } /** diff --git a/src/web/client/utilities/fileAndEntityUtil.ts b/src/web/client/utilities/fileAndEntityUtil.ts index 27fd8105..b72ddc60 100644 --- a/src/web/client/utilities/fileAndEntityUtil.ts +++ b/src/web/client/utilities/fileAndEntityUtil.ts @@ -43,6 +43,10 @@ export function getFileLogicalEntityName(fileFsPath: string) { ?.logicalEntityName as string; } +export function getFormLogicalName(fileFsPath: string) { + return WebExtensionContext.fileDataMap.getFileMap.get(fileFsPath)?.logicalFormName as string; +} + export function updateFileEntityEtag(fileFsPath: string, entityEtag: string) { WebExtensionContext.fileDataMap.updateEtagValue(fileFsPath, entityEtag); } diff --git a/src/web/client/utilities/schemaHelperUtil.ts b/src/web/client/utilities/schemaHelperUtil.ts index 5867d2e5..192a63ba 100644 --- a/src/web/client/utilities/schemaHelperUtil.ts +++ b/src/web/client/utilities/schemaHelperUtil.ts @@ -27,6 +27,11 @@ export function getLogicalEntityParameter(entity: string) { return entityMetadata ? (entityMetadata as unknown as Map).get(schemaMetaDataKey.DATAVERSE_LOGICAL_ENTITY_NAME) : undefined; } +export function getLogicalFormNameParameter(entity: string) { + const entityMetadata = getEntity(entity)?.get(schemaEntityKey.DATAVERSE_ENTITY_METADATA); + return entityMetadata ? (entityMetadata as unknown as Map).get(schemaMetaDataKey.DATAVERSE_FORM_NAME) : undefined; +} + export function getPortalSchema(schema: string) { if ( schema.toLowerCase() === diff --git a/src/web/client/utilities/urlBuilderUtil.ts b/src/web/client/utilities/urlBuilderUtil.ts index 94c779c9..07a20aee 100644 --- a/src/web/client/utilities/urlBuilderUtil.ts +++ b/src/web/client/utilities/urlBuilderUtil.ts @@ -216,6 +216,25 @@ export function getLogicalEntityName(result: any, logicalEntityName?: string) { return logicalEntity; } +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function getMetadataInfo(result: any, metadataKeys?: string[]) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const metadataValues: { [key: string]: any } = {}; + + if (metadataKeys) { + // eslint-disable-next-line prefer-const + for (let key of metadataKeys) { + const attributePath = getAttributePath(key); + const value = attributePath.relativePath.length > 0 ? + JSON.parse(result[attributePath.source])[attributePath.relativePath] : + result[attributePath.source]; + metadataValues[key] = value; + } + } + + return metadataValues; +} + export function pathHasEntityFolderName(uri: string): boolean { for (const entry of WebExtensionContext.entitiesFolderNameMap.entries()) { if (uri.includes(entry[1])) {