Comparar commits

..

5 Commits

Autor SHA1 Mensagem Data
Wallace Breza dd0727d022 Fixed editor sidebar bad css class 2019-08-27 08:32:37 -07:00
Wallace Breza d562d56ae9 Reverted window.require changes and enabled node integration 2019-08-27 08:15:31 -07:00
Wallace Breza b38ffe4a99 Fixes electron mocks in tests 2019-08-27 08:15:31 -07:00
Wallace Breza b1bf5cce8f Fixing issue with dependency updates 2019-08-27 08:15:31 -07:00
Wallace Breza 111077275b Updated VoTT dependencies 2019-08-27 08:15:30 -07:00
102 arquivos alterados com 11162 adições e 19053 exclusões
-13
Ver Arquivo
@@ -1,13 +0,0 @@
version: 1
update_configs:
target_branch: "develop"
# Keep package.json (& lockfiles) up to date weekly
- package_manager: "javascript"
directory: "/"
update_schedule: "weekly"
default_labels:
- "dependencies"
- "dependabot"
-6
Ver Arquivo
@@ -19,7 +19,6 @@
# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
@@ -38,8 +37,3 @@ secrets.sh
# complexity reports
es6-src/
report/
# VoTT Server
server/lib
server/node_modules
server/coverage
+2 -13
Ver Arquivo
@@ -59,7 +59,7 @@ VoTT helps facilitate an end-to-end machine learning pipeline:
## Getting Started
VoTT can be installed as a native application or run from source. VoTT is also available as a [stand-alone Web application](https://vott.z22.web.core.windows.net) and can be used in any modern Web browser.
VoTT can be installed as a native application or run from source. VoTT is also available as a [stand-alone Web application](https://vott.z5.web.core.windows.net) and can be used in any modern Web browser.
### Download and install a release package for your platform (recommended)
@@ -81,7 +81,7 @@ VoTT requires [NodeJS (>= 10.x, Dubnium) and NPM](https://github.com/nodejs/Rele
### Run as Web Application
Using a modern Web browser, VoTT can be loaded from: [https://vott.z22.web.core.windows.net](https://vott.z22.web.core.windows.net)
Using a modern Web browser, VoTT can be loaded from: [https://vott.z5.web.core.windows.net](https://vott.z5.web.core.windows.net)
As noted above, the Web version of VoTT *cannot* access the local file system; all assets must be imported/exported through a Cloud project.
@@ -268,17 +268,6 @@ When the video playback bar is present, it allows the following shortcuts to sel
* Multi-select - Hold down Shift while selecting regions
* Exclusive Tracking mode - Ctrl + N to block frame UI allowing a user to create a region on top of existing regions
## Release Process
![alt text](./docs/images/release-process.png "Create Release Process")
For more details on github/web releases and versions -- please review our [release process document](./docs/RELEASE_GUIDE.md)
To build VoTT executable using command:
```
npm run release
```
For details on packaging executable for the release -- please review our [PACKAGING.md](./docs/PACKAGING.md)
## Collaborators
+2 -5
Ver Arquivo
@@ -5,9 +5,6 @@ pr:
- dev* # kick off for pr targeting dev or prefix dev
- master # trigger build for pr targeting master
variables:
- group: CODE_COV
jobs:
- job: Linux
pool:
@@ -20,7 +17,7 @@ jobs:
- job: MacOS
pool:
vmImage: macOS-10.15
vmImage: macOS-10.13
timeoutInMinutes: 60 # how long to run the job before automatically cancelling
steps:
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
@@ -29,7 +26,7 @@ jobs:
- job: Windows
pool:
vmImage: "windows-2019"
vmImage: win1803
timeoutInMinutes: 60 # how long to run the job before automatically cancelling
steps:
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
-33
Ver Arquivo
@@ -1,33 +0,0 @@
name: "GitHub & Web Release [$(SourceBranchName)] - $(Date:yyyyMMdd)$(Rev:.r)"
trigger: none # manual queue only when we're ready to release
pr: none # disable CI build for PR
variables:
azureSubscription: "pj-little-sub"
DEV_STORAGE_ACCOUNT: 'vottdev'
PROD_STORAGE_ACCOUNT: 'vott'
DEV_URL: "https://vottdev.z5.web.core.windows.net/"
PROD_URL: "https://vott.z22.web.core.windows.net/"
stages:
- stage: version_bump_commit
jobs:
- template: templates/npm-version-bump.yml
parameters:
versionType: $(NPM_VERSION_TYPE)
- stage: github_release
dependsOn: version_bump_commit
jobs:
- template: templates/create-github-release.yml
parameters:
GitHubConnection: 'GitHub connection' # defaults for any parameters that aren't specified
repositoryName: '$(Build.Repository.Name)' # microsoft/VoTT
isPreRelease: $(IS_PRERELEASE) # set when queuing build
isDraft: $(IS_DRAFT)
- stage: web_release
dependsOn: version_bump_commit
jobs:
- template: templates/create-web-release.yml
@@ -0,0 +1,26 @@
# Node.js with React
# Build a Node.js project that uses React.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
steps:
- task: NodeTool@0
displayName: 'Use Node 10.x'
inputs:
versionSpec: 10.x
- bash: |
set -ex
# clean install
npm ci
npm run release-ci
mkdir -p linux
cp releases/vott*.snap linux/
displayName: Build
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: linux'
inputs:
PathtoPublish: linux
ArtifactName: linux
+22
Ver Arquivo
@@ -0,0 +1,22 @@
steps:
- task: NodeTool@0
displayName: 'Use Node 10.x'
inputs:
versionSpec: 10.x
- bash: |
set -ex
# clean install
npm ci
npm run release-ci
mkdir -p mac
cp releases/vott*.dmg mac/
displayName: Build
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: mac'
inputs:
PathtoPublish: mac
ArtifactName: mac
@@ -0,0 +1,69 @@
name: $(Date:yyyyMMdd).$(Hours)$(Minutes)$(Seconds)
variables:
AZURE_STORAGE_ACCOUNT: vottv2
trigger:
- dev*
- master
pr: none # disable CI build for PR
pool:
vmImage: 'Ubuntu-16.04'
steps:
- bash: |
set -e
COMMIT_SHA=`echo ${BUILD_SOURCEVERSION} | cut -c1-8`
echo "sha: " $COMMIT_SHA
# rewrite build number
echo "##vso[build.updatebuildnumber]Report-${COMMIT_SHA}-${BUILD_BUILDNUMBER}"
displayName: "Rewrite build number"
- bash: |
set -e
ACCOUNT=$(AZURE_STORAGE_ACCOUNT)
if [[ -z "${ACCOUNT}" ]]; then
echo "Need to set AZURE_STORAGE_ACCOUNT"
exit 1
fi
KEY=$(AZURE_STORAGE_KEY)
if [[ -z "${KEY}" ]]; then
echo "Need to set AZURE_STORAGE_KEY"
exit 1
fi
displayName: "Verify storage account cred exists"
- task: NodeTool@0
displayName: "Use Node 10.x"
inputs:
versionSpec: 10.x
- task: Npm@1
displayName: 'Run `npm ci`'
inputs:
command: custom
verbose: false
customCommand: ci
- task: AzureCLI@1
displayName: "Pull down old report and add updates"
inputs:
azureSubscription: 'PELITTLE TEAM - CSE DWR (d36d0808-a967-4f73-9fdc-32ea232fc81d)'
scriptLocation: inlineScript
inlineScript: './scripts/update-report.sh'
- bash: |
set -e
cat /tmp/download.log
displayName: "print download log"
condition: succeededOrFailed()
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: report'
inputs:
PathtoPublish: ./report
ArtifactName: report
+5
Ver Arquivo
@@ -0,0 +1,5 @@
# Overview
Collection of release definitions used in this project.
Since release does not yet support yaml, we're exporting them for
record keeping.
@@ -0,0 +1,535 @@
{
"source": 2,
"revision": 6,
"description": null,
"createdBy": {
"displayName": "My Ho 🥓🥐🍚",
"url": "https://app.vssps.visualstudio.com/Af945f528-1155-48f8-b08b-b6529166b9f0/_apis/Identities/1077cc21-332d-415a-8e93-e6277da44f6d",
"_links": {
"avatar": {
"href": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
}
},
"id": "1077cc21-332d-415a-8e93-e6277da44f6d",
"uniqueName": "myho@microsoft.com",
"imageUrl": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1",
"descriptor": "aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
},
"createdOn": "2019-03-22T18:43:08.407Z",
"modifiedBy": {
"displayName": "My Ho 🥓🥐🍚",
"url": "https://app.vssps.visualstudio.com/Af945f528-1155-48f8-b08b-b6529166b9f0/_apis/Identities/1077cc21-332d-415a-8e93-e6277da44f6d",
"_links": {
"avatar": {
"href": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
}
},
"id": "1077cc21-332d-415a-8e93-e6277da44f6d",
"uniqueName": "myho@microsoft.com",
"imageUrl": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1",
"descriptor": "aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
},
"modifiedOn": "2019-04-12T21:39:14.277Z",
"isDeleted": false,
"variables": {},
"variableGroups": [],
"environments": [
{
"id": 4,
"name": "Dev",
"rank": 1,
"owner": {
"displayName": "My Ho 🥓🥐🍚",
"url": "https://app.vssps.visualstudio.com/Af945f528-1155-48f8-b08b-b6529166b9f0/_apis/Identities/1077cc21-332d-415a-8e93-e6277da44f6d",
"_links": {
"avatar": {
"href": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
}
},
"id": "1077cc21-332d-415a-8e93-e6277da44f6d",
"uniqueName": "myho@microsoft.com",
"imageUrl": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1",
"descriptor": "aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
},
"variables": {
"DEV_STORAGE_ACCOUNT": {
"value": "vottdev",
"allowOverride": true
}
},
"variableGroups": [],
"preDeployApprovals": {
"approvals": [
{
"rank": 1,
"isAutomated": true,
"isNotificationOn": false,
"id": 10
}
],
"approvalOptions": {
"requiredApproverCount": null,
"releaseCreatorCanBeApprover": false,
"autoTriggeredAndPreviousEnvironmentApprovedCanBeSkipped": false,
"enforceIdentityRevalidation": false,
"timeoutInMinutes": 0,
"executionOrder": 1
}
},
"deployStep": {
"id": 11
},
"postDeployApprovals": {
"approvals": [
{
"rank": 1,
"isAutomated": true,
"isNotificationOn": false,
"id": 12
}
],
"approvalOptions": {
"requiredApproverCount": null,
"releaseCreatorCanBeApprover": false,
"autoTriggeredAndPreviousEnvironmentApprovedCanBeSkipped": false,
"enforceIdentityRevalidation": false,
"timeoutInMinutes": 0,
"executionOrder": 2
}
},
"deployPhases": [
{
"deploymentInput": {
"parallelExecution": {
"parallelExecutionType": 0
},
"skipArtifactsDownload": false,
"artifactsDownloadInput": {
"downloadInputs": [
{
"alias": "_Build Web Artifact",
"artifactType": "Build",
"artifactDownloadMode": "All",
"artifactItems": []
}
]
},
"queueId": 3,
"demands": [],
"enableAccessToken": false,
"timeoutInMinutes": 0,
"jobCancelTimeoutInMinutes": 1,
"condition": "succeeded()",
"overrideInputs": {}
},
"rank": 1,
"phaseType": 1,
"name": "Deploy to dev*",
"refName": null,
"workflowTasks": [
{
"environment": {},
"taskId": "eb72cb01-a7e5-427b-a8a1-1b31ccac8a43",
"version": "2.*",
"name": "AzureBlob File Copy to $(DEV_STORAGE_ACCOUNT)",
"refName": "",
"enabled": true,
"alwaysRun": false,
"continueOnError": false,
"timeoutInMinutes": 0,
"definitionType": "task",
"overrideInputs": {},
"condition": "succeeded()",
"inputs": {
"SourcePath": "$(System.DefaultWorkingDirectory)/_Build Web Artifact/build",
"ConnectedServiceNameSelector": "ConnectedServiceNameARM",
"ConnectedServiceName": "",
"ConnectedServiceNameARM": "28e5058e-d34a-48f3-978b-4bb47ea9e227",
"Destination": "AzureBlob",
"StorageAccount": "",
"StorageAccountRM": "$(DEV_STORAGE_ACCOUNT)",
"ContainerName": "$web",
"BlobPrefix": "",
"EnvironmentName": "",
"EnvironmentNameRM": "",
"ResourceFilteringMethod": "machineNames",
"MachineNames": "",
"vmsAdminUserName": "",
"vmsAdminPassword": "",
"TargetPath": "",
"AdditionalArgumentsForBlobCopy": "",
"AdditionalArgumentsForVMCopy": "",
"enableCopyPrerequisites": "false",
"CopyFilesInParallel": "true",
"CleanTargetBeforeCopy": "false",
"skipCACheck": "true",
"outputStorageUri": "",
"outputStorageContainerSasToken": ""
}
}
]
}
],
"environmentOptions": {
"emailNotificationType": "OnlyOnFailure",
"emailRecipients": "release.environment.owner;release.creator",
"skipArtifactsDownload": false,
"timeoutInMinutes": 0,
"enableAccessToken": false,
"publishDeploymentStatus": true,
"badgeEnabled": false,
"autoLinkWorkItems": false,
"pullRequestDeploymentEnabled": false
},
"demands": [],
"conditions": [
{
"name": "ReleaseStarted",
"conditionType": 1,
"value": ""
}
],
"executionPolicy": {
"concurrencyCount": 1,
"queueDepthCount": 0
},
"schedules": [],
"currentRelease": {
"id": 215,
"url": "https://vsrm.dev.azure.com/msft-vott/4e739af6-f85e-44b9-a3f3-75eb893a6223/_apis/Release/releases/215",
"_links": {}
},
"retentionPolicy": {
"daysToKeep": 30,
"releasesToKeep": 3,
"retainBuild": true
},
"processParameters": {
"dataSourceBindings": [
{
"dataSourceName": "AzureRMWebAppNamesByAppType",
"parameters": {
"WebAppKind": "$(WebAppKind)"
},
"endpointId": "$(ConnectedServiceName)",
"target": "WebAppName"
}
]
},
"properties": {},
"preDeploymentGates": {
"id": 0,
"gatesOptions": null,
"gates": []
},
"postDeploymentGates": {
"id": 0,
"gatesOptions": null,
"gates": []
},
"environmentTriggers": [],
"badgeUrl": "https://vsrm.dev.azure.com/msft-vott/_apis/public/Release/badge/4e739af6-f85e-44b9-a3f3-75eb893a6223/4/4"
},
{
"id": 7,
"name": "v2/master",
"rank": 2,
"owner": {
"displayName": "My Ho 🥓🥐🍚",
"url": "https://app.vssps.visualstudio.com/Af945f528-1155-48f8-b08b-b6529166b9f0/_apis/Identities/1077cc21-332d-415a-8e93-e6277da44f6d",
"_links": {
"avatar": {
"href": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
}
},
"id": "1077cc21-332d-415a-8e93-e6277da44f6d",
"uniqueName": "myho@microsoft.com",
"imageUrl": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1",
"descriptor": "aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
},
"variables": {
"PROD_STORAGE_ACCOUNT": {
"value": "vott"
}
},
"variableGroups": [],
"preDeployApprovals": {
"approvals": [
{
"rank": 1,
"isAutomated": true,
"isNotificationOn": false,
"id": 19
}
],
"approvalOptions": {
"requiredApproverCount": null,
"releaseCreatorCanBeApprover": false,
"autoTriggeredAndPreviousEnvironmentApprovedCanBeSkipped": false,
"enforceIdentityRevalidation": false,
"timeoutInMinutes": 0,
"executionOrder": 1
}
},
"deployStep": {
"id": 20
},
"postDeployApprovals": {
"approvals": [
{
"rank": 1,
"isAutomated": true,
"isNotificationOn": false,
"id": 21
}
],
"approvalOptions": {
"requiredApproverCount": null,
"releaseCreatorCanBeApprover": false,
"autoTriggeredAndPreviousEnvironmentApprovedCanBeSkipped": false,
"enforceIdentityRevalidation": false,
"timeoutInMinutes": 0,
"executionOrder": 2
}
},
"deployPhases": [
{
"deploymentInput": {
"parallelExecution": {
"parallelExecutionType": 0
},
"skipArtifactsDownload": false,
"artifactsDownloadInput": {
"downloadInputs": [
{
"alias": "_Build Web Artifact",
"artifactType": "Build",
"artifactDownloadMode": "All",
"artifactItems": []
}
]
},
"queueId": 3,
"demands": [],
"enableAccessToken": false,
"timeoutInMinutes": 0,
"jobCancelTimeoutInMinutes": 1,
"condition": "and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/v2'), eq(variables['Build.SourceBranch'], 'refs/heads/master')))",
"overrideInputs": {}
},
"rank": 1,
"phaseType": 1,
"name": "Deploy to V2/Master",
"refName": null,
"workflowTasks": [
{
"environment": {},
"taskId": "eb72cb01-a7e5-427b-a8a1-1b31ccac8a43",
"version": "2.*",
"name": "AzureBlob File Copy to $(PROD_STORAGE_ACCOUNT)",
"refName": "",
"enabled": true,
"alwaysRun": false,
"continueOnError": false,
"timeoutInMinutes": 0,
"definitionType": "task",
"overrideInputs": {},
"condition": "succeeded()",
"inputs": {
"SourcePath": "$(System.DefaultWorkingDirectory)/_Build Web Artifact/build",
"ConnectedServiceNameSelector": "ConnectedServiceNameARM",
"ConnectedServiceName": "",
"ConnectedServiceNameARM": "28e5058e-d34a-48f3-978b-4bb47ea9e227",
"Destination": "AzureBlob",
"StorageAccount": "",
"StorageAccountRM": "$(PROD_STORAGE_ACCOUNT)",
"ContainerName": "$web",
"BlobPrefix": "",
"EnvironmentName": "",
"EnvironmentNameRM": "",
"ResourceFilteringMethod": "machineNames",
"MachineNames": "",
"vmsAdminUserName": "",
"vmsAdminPassword": "",
"TargetPath": "",
"AdditionalArgumentsForBlobCopy": "",
"AdditionalArgumentsForVMCopy": "",
"enableCopyPrerequisites": "false",
"CopyFilesInParallel": "true",
"CleanTargetBeforeCopy": "false",
"skipCACheck": "true",
"outputStorageUri": "",
"outputStorageContainerSasToken": ""
}
}
]
}
],
"environmentOptions": {
"emailNotificationType": "OnlyOnFailure",
"emailRecipients": "release.environment.owner;release.creator",
"skipArtifactsDownload": false,
"timeoutInMinutes": 0,
"enableAccessToken": false,
"publishDeploymentStatus": true,
"badgeEnabled": false,
"autoLinkWorkItems": false,
"pullRequestDeploymentEnabled": false
},
"demands": [],
"conditions": [
{
"name": "Dev",
"conditionType": 2,
"value": "4"
},
{
"name": "_Build Web Artifact",
"conditionType": 4,
"value": "{\"sourceBranch\":\"master\",\"tags\":[],\"useBuildDefinitionBranch\":false,\"createReleaseOnBuildTagging\":false,\"tagFilter\":null}"
}
],
"executionPolicy": {
"concurrencyCount": 1,
"queueDepthCount": 0
},
"schedules": [],
"currentRelease": {
"id": 0,
"url": "https://vsrm.dev.azure.com/msft-vott/4e739af6-f85e-44b9-a3f3-75eb893a6223/_apis/Release/releases/0",
"_links": {}
},
"retentionPolicy": {
"daysToKeep": 30,
"releasesToKeep": 3,
"retainBuild": true
},
"processParameters": {
"dataSourceBindings": [
{
"dataSourceName": "AzureRMWebAppNamesByAppType",
"parameters": {
"WebAppKind": "$(WebAppKind)"
},
"endpointId": "$(ConnectedServiceName)",
"target": "WebAppName"
}
]
},
"properties": {},
"preDeploymentGates": {
"id": 0,
"gatesOptions": null,
"gates": []
},
"postDeploymentGates": {
"id": 0,
"gatesOptions": null,
"gates": []
},
"environmentTriggers": [],
"badgeUrl": "https://vsrm.dev.azure.com/msft-vott/_apis/public/Release/badge/4e739af6-f85e-44b9-a3f3-75eb893a6223/4/7"
}
],
"artifacts": [
{
"sourceId": "4e739af6-f85e-44b9-a3f3-75eb893a6223:36",
"type": "Build",
"alias": "_Build Web Artifact",
"definitionReference": {
"artifactSourceDefinitionUrl": {
"id": "https://dev.azure.com/msft-vott/_permalink/_build/index?collectionId=2f887b33-5e65-447a-a189-049f04641d6d&projectId=4e739af6-f85e-44b9-a3f3-75eb893a6223&definitionId=36",
"name": ""
},
"defaultVersionBranch": {
"id": "",
"name": ""
},
"defaultVersionSpecific": {
"id": "",
"name": ""
},
"defaultVersionTags": {
"id": "",
"name": ""
},
"defaultVersionType": {
"id": "latestType",
"name": "Latest"
},
"definition": {
"id": "36",
"name": "Build Web Artifact"
},
"definitions": {
"id": "",
"name": ""
},
"IsMultiDefinitionType": {
"id": "False",
"name": "False"
},
"project": {
"id": "4e739af6-f85e-44b9-a3f3-75eb893a6223",
"name": "VoTT"
},
"repository": {
"id": "",
"name": ""
}
},
"isPrimary": true,
"isRetained": false
}
],
"triggers": [
{
"artifactAlias": "_Build Web Artifact",
"triggerConditions": [
{
"sourceBranch": "dev*",
"tags": [],
"useBuildDefinitionBranch": false,
"createReleaseOnBuildTagging": false
},
{
"sourceBranch": "v2",
"tags": [],
"useBuildDefinitionBranch": false,
"createReleaseOnBuildTagging": false
},
{
"sourceBranch": "master",
"tags": [],
"useBuildDefinitionBranch": false,
"createReleaseOnBuildTagging": false
}
],
"triggerType": 1
}
],
"releaseNameFormat": "Release-$(rev:r)",
"tags": [],
"pipelineProcess": {
"type": 1
},
"properties": {
"DefinitionCreationSource": {
"$type": "System.String",
"$value": "ReleaseClone"
}
},
"id": 4,
"name": "VoTT - Deploy to Web",
"path": "\\",
"projectReference": null,
"url": "https://vsrm.dev.azure.com/msft-vott/4e739af6-f85e-44b9-a3f3-75eb893a6223/_apis/Release/definitions/4",
"_links": {
"self": {
"href": "https://vsrm.dev.azure.com/msft-vott/4e739af6-f85e-44b9-a3f3-75eb893a6223/_apis/Release/definitions/4"
},
"web": {
"href": "https://dev.azure.com/msft-vott/4e739af6-f85e-44b9-a3f3-75eb893a6223/_release?definitionId=4"
}
}
}
@@ -0,0 +1,329 @@
{
"source": 2,
"revision": 8,
"description": "copy artifact to azure blob hosting plato report for VoTT",
"createdBy": {
"displayName": "My Ho 🥓🥐🍚",
"url": "https://app.vssps.visualstudio.com/Af945f528-1155-48f8-b08b-b6529166b9f0/_apis/Identities/1077cc21-332d-415a-8e93-e6277da44f6d",
"_links": {
"avatar": {
"href": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
}
},
"id": "1077cc21-332d-415a-8e93-e6277da44f6d",
"uniqueName": "myho@microsoft.com",
"imageUrl": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1",
"descriptor": "aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
},
"createdOn": "2019-03-25T22:52:29.780Z",
"modifiedBy": {
"displayName": "My Ho 🥓🥐🍚",
"url": "https://app.vssps.visualstudio.com/Af945f528-1155-48f8-b08b-b6529166b9f0/_apis/Identities/1077cc21-332d-415a-8e93-e6277da44f6d",
"_links": {
"avatar": {
"href": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
}
},
"id": "1077cc21-332d-415a-8e93-e6277da44f6d",
"uniqueName": "myho@microsoft.com",
"imageUrl": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1",
"descriptor": "aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
},
"modifiedOn": "2019-04-12T22:29:15.590Z",
"isDeleted": false,
"variables": {
"STORAGE_ACCOUNT": {
"value": "vottv2",
"allowOverride": true
}
},
"variableGroups": [],
"environments": [
{
"id": 6,
"name": "Upload Report",
"rank": 1,
"owner": {
"displayName": "My Ho 🥓🥐🍚",
"url": "https://app.vssps.visualstudio.com/Af945f528-1155-48f8-b08b-b6529166b9f0/_apis/Identities/1077cc21-332d-415a-8e93-e6277da44f6d",
"_links": {
"avatar": {
"href": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
}
},
"id": "1077cc21-332d-415a-8e93-e6277da44f6d",
"uniqueName": "myho@microsoft.com",
"imageUrl": "https://dev.azure.com/msft-vott/_apis/GraphProfile/MemberAvatars/aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1",
"descriptor": "aad.MTRlNGE0YjQtNDU5NS03OGI5LTk2MmItNTM0ZjNjYjUxMWI1"
},
"variables": {},
"variableGroups": [],
"preDeployApprovals": {
"approvals": [
{
"rank": 1,
"isAutomated": true,
"isNotificationOn": false,
"id": 16
}
],
"approvalOptions": {
"requiredApproverCount": null,
"releaseCreatorCanBeApprover": false,
"autoTriggeredAndPreviousEnvironmentApprovedCanBeSkipped": false,
"enforceIdentityRevalidation": false,
"timeoutInMinutes": 0,
"executionOrder": 1
}
},
"deployStep": {
"id": 17
},
"postDeployApprovals": {
"approvals": [
{
"rank": 1,
"isAutomated": true,
"isNotificationOn": false,
"id": 18
}
],
"approvalOptions": {
"requiredApproverCount": null,
"releaseCreatorCanBeApprover": false,
"autoTriggeredAndPreviousEnvironmentApprovedCanBeSkipped": false,
"enforceIdentityRevalidation": false,
"timeoutInMinutes": 0,
"executionOrder": 2
}
},
"deployPhases": [
{
"deploymentInput": {
"parallelExecution": {
"parallelExecutionType": 0
},
"skipArtifactsDownload": false,
"artifactsDownloadInput": {
"downloadInputs": [
{
"alias": "_VoTT - Generate latest plato report",
"artifactType": "Build",
"artifactDownloadMode": "All",
"artifactItems": []
}
]
},
"queueId": 3,
"demands": [],
"enableAccessToken": false,
"timeoutInMinutes": 0,
"jobCancelTimeoutInMinutes": 1,
"condition": "succeeded()",
"overrideInputs": {}
},
"rank": 1,
"phaseType": 1,
"name": "Upload Report",
"refName": null,
"workflowTasks": [
{
"environment": {},
"taskId": "eb72cb01-a7e5-427b-a8a1-1b31ccac8a43",
"version": "2.*",
"name": "AzureBlob File Copy To $(STORAGE_ACCOUNT)",
"refName": "",
"enabled": true,
"alwaysRun": false,
"continueOnError": false,
"timeoutInMinutes": 0,
"definitionType": "task",
"overrideInputs": {},
"condition": "succeeded()",
"inputs": {
"SourcePath": "$(System.DefaultWorkingDirectory)/_VoTT - Generate latest plato report/report",
"ConnectedServiceNameSelector": "ConnectedServiceNameARM",
"ConnectedServiceName": "",
"ConnectedServiceNameARM": "28e5058e-d34a-48f3-978b-4bb47ea9e227",
"Destination": "AzureBlob",
"StorageAccount": "",
"StorageAccountRM": "$(STORAGE_ACCOUNT)",
"ContainerName": "$web",
"BlobPrefix": "",
"EnvironmentName": "",
"EnvironmentNameRM": "",
"ResourceFilteringMethod": "machineNames",
"MachineNames": "",
"vmsAdminUserName": "",
"vmsAdminPassword": "",
"TargetPath": "",
"AdditionalArgumentsForBlobCopy": "",
"AdditionalArgumentsForVMCopy": "",
"enableCopyPrerequisites": "false",
"CopyFilesInParallel": "true",
"CleanTargetBeforeCopy": "false",
"skipCACheck": "true",
"outputStorageUri": "",
"outputStorageContainerSasToken": ""
}
}
]
}
],
"environmentOptions": {
"emailNotificationType": "OnlyOnFailure",
"emailRecipients": "release.environment.owner;release.creator",
"skipArtifactsDownload": false,
"timeoutInMinutes": 0,
"enableAccessToken": false,
"publishDeploymentStatus": true,
"badgeEnabled": false,
"autoLinkWorkItems": false,
"pullRequestDeploymentEnabled": false
},
"demands": [],
"conditions": [
{
"name": "ReleaseStarted",
"conditionType": 1,
"value": ""
}
],
"executionPolicy": {
"concurrencyCount": 1,
"queueDepthCount": 0
},
"schedules": [],
"currentRelease": {
"id": 216,
"url": "https://vsrm.dev.azure.com/msft-vott/4e739af6-f85e-44b9-a3f3-75eb893a6223/_apis/Release/releases/216",
"_links": {}
},
"retentionPolicy": {
"daysToKeep": 30,
"releasesToKeep": 3,
"retainBuild": true
},
"processParameters": {
"dataSourceBindings": [
{
"dataSourceName": "AzureRMWebAppNamesByAppType",
"parameters": {
"WebAppKind": "$(WebAppKind)"
},
"endpointId": "$(ConnectedServiceName)",
"target": "WebAppName"
}
]
},
"properties": {},
"preDeploymentGates": {
"id": 0,
"gatesOptions": null,
"gates": []
},
"postDeploymentGates": {
"id": 0,
"gatesOptions": null,
"gates": []
},
"environmentTriggers": [],
"badgeUrl": "https://vsrm.dev.azure.com/msft-vott/_apis/public/Release/badge/4e739af6-f85e-44b9-a3f3-75eb893a6223/6/6"
}
],
"artifacts": [
{
"sourceId": "4e739af6-f85e-44b9-a3f3-75eb893a6223:41",
"type": "Build",
"alias": "_VoTT - Generate latest plato report",
"definitionReference": {
"artifactSourceDefinitionUrl": {
"id": "https://dev.azure.com/msft-vott/_permalink/_build/index?collectionId=2f887b33-5e65-447a-a189-049f04641d6d&projectId=4e739af6-f85e-44b9-a3f3-75eb893a6223&definitionId=41",
"name": ""
},
"defaultVersionBranch": {
"id": "",
"name": ""
},
"defaultVersionSpecific": {
"id": "",
"name": ""
},
"defaultVersionTags": {
"id": "",
"name": ""
},
"defaultVersionType": {
"id": "latestType",
"name": "Latest"
},
"definition": {
"id": "41",
"name": "VoTT - Generate latest plato report"
},
"definitions": {
"id": "",
"name": ""
},
"IsMultiDefinitionType": {
"id": "False",
"name": "False"
},
"project": {
"id": "4e739af6-f85e-44b9-a3f3-75eb893a6223",
"name": "VoTT"
},
"repository": {
"id": "",
"name": ""
}
},
"isPrimary": true,
"isRetained": false
}
],
"triggers": [
{
"artifactAlias": "_VoTT - Generate latest plato report",
"triggerConditions": [
{
"sourceBranch": "dev*",
"tags": [],
"useBuildDefinitionBranch": false,
"createReleaseOnBuildTagging": false
},
{
"sourceBranch": "master",
"tags": [],
"useBuildDefinitionBranch": false,
"createReleaseOnBuildTagging": false
}
],
"triggerType": 1
}
],
"releaseNameFormat": "Release-$(Date:MMddyy).$(Date:hhmm)-$(rev:r)",
"tags": [],
"pipelineProcess": {
"type": 1
},
"properties": {
"DefinitionCreationSource": {
"$type": "System.String",
"$value": "ReleaseClone"
}
},
"id": 6,
"name": "VoTT - Upload Plato Report",
"path": "\\",
"projectReference": null,
"url": "https://vsrm.dev.azure.com/msft-vott/4e739af6-f85e-44b9-a3f3-75eb893a6223/_apis/Release/definitions/6",
"_links": {
"self": {
"href": "https://vsrm.dev.azure.com/msft-vott/4e739af6-f85e-44b9-a3f3-75eb893a6223/_apis/Release/definitions/6"
},
"web": {
"href": "https://dev.azure.com/msft-vott/4e739af6-f85e-44b9-a3f3-75eb893a6223/_release?definitionId=6"
}
}
}
@@ -4,8 +4,6 @@ jobs:
pool: ${{ parameters.pool }}
timeoutInMinutes: 15 # how long to run the job before automatically cancelling
steps:
- template: git-pull-current-branch.yml
- task: NodeTool@0
displayName: 'Use Node 10.x'
inputs:
@@ -1,96 +1,32 @@
parameters:
GitHubConnection: '' # defaults for any parameters that aren't specified
repositoryName: ''
isPreRelease: false
isDraft: false
jobs:
- template: build-artifact.yml
parameters:
name: LinuxArtifact
pool:
vmImage: ubuntu-16.04
os: linux
artifact: vott*.snap
- template: build-artifact.yml
parameters:
name: WindowsArtifact
pool:
vmImage: "windows-2019"
os: windows
artifact: vott*.exe
- template: build-artifact.yml
parameters:
name: MacOSArtifact
pool:
vmImage: macOS-10.15
os: mac
artifact: vott*.dmg
parameters:
GitHubConnection: '' # defaults for any parameters that aren't specified
repositoryName: ''
releaseNotesSource: input
addChangeLog: false
isPreRelease: true
isDraft: true
jobs:
- job: Create_Github_Release
timeoutInMinutes: 30 # timeout on job if deploy is not completed in 30 minutes
dependsOn:
- LinuxArtifact
- WindowsArtifact
- MacOSArtifact
pool:
vmImage: ubuntu-16.04
steps:
- checkout: none # we already have the artifacts built, don't need the code
- download: current
- template: git-pull-current-branch.yml
- bash: |
set -e
echo
echo "======> Set commit sha"
COMMIT_SHA=$(git rev-parse --short HEAD)
echo "COMMIT SHA: $COMMIT_SHA"
echo "##vso[task.setvariable variable=COMMIT_SHA]$COMMIT_SHA"
###
# These variables were set in the "Version Bump" stage. There are
# currently no way to pass variables between stages, hence this workaround.
###
echo
echo "======> Set version variables"
CURRENT_VERSION=$(cat $(Pipeline.Workspace)/variables/CURRENT_VERSION)
echo "##vso[task.setvariable variable=CURRENT_VERSION]$CURRENT_VERSION"
NEXT_VERSION=$(cat $(Pipeline.Workspace)/variables/NEXT_VERSION)
echo "##vso[task.setvariable variable=NEXT_VERSION]$NEXT_VERSION"
displayName: "Set variables for release task"
- task: GitHubRelease@1
- task: GitHubRelease@0
displayName: 'GitHub release (create)'
inputs:
gitHubConnection: ${{ parameters.GitHubConnection }}
repositoryName: '$(Build.Repository.Name)'
action: 'create'
target: '$(Build.SourceBranch)'
tagSource: 'userSpecifiedTag'
tag: $(NEXT_VERSION)
releaseNotesSource: 'inline'
releaseNotesInline: |
## Web Release
### Dev
$(DEV_URL)
### Prod
$(PROD_URL)
## Docs
https://github.com/Microsoft/VoTT/blob/$(Build.SourceBranch)/README.md
repositoryName: ${{ parameters.repositoryName }}
releaseNotesSource: ${{ parameters.releaseNotesSource }}
target: $(Build.SourceBranch)
assets: |
../linux/*
../windows/*
../mac/*
isDraft: ${{ parameters.isDraft }}
addChangeLog: ${{ parameters.addChangeLog }}
isDraft: true # for testing, change to true when ready to merge
isPreRelease: ${{ parameters.isPrelease }}
changeLogCompareToRelease: 'lastNonDraftReleaseByTag'
changeLogCompareToReleaseTag: $(CURRENT_VERSION)
changeLogType: 'commitBased'
@@ -1,56 +0,0 @@
jobs:
- job: "Web_Release"
pool:
vmImage: 'windows-latest'
steps:
- template: git-pull-current-branch.yml
- task: NodeTool@0
displayName: 'Use Node 10.x'
inputs:
versionSpec: 10.x
- task: Npm@1
displayName: 'npm ci'
inputs:
command: custom
workingDir: .
verbose: false
customCommand: ci
- task: Bash@3
displayName: 'Create artifact'
inputs:
targetType: filePath
filePath: './scripts/generate-web-artifact.sh'
- task: AzureFileCopy@3
displayName: 'AzureBlob File Copy to $DEV_STORAGE_ACCOUNT'
inputs:
SourcePath: './build'
azureSubscription: $(azureSubscription)
destination: azureBlob
storage: $(DEV_STORAGE_ACCOUNT)
containerName: '$web'
- task: AzureFileCopy@3
displayName: 'AzureBlob File Copy to $PROD_STORAGE_ACCOUNT'
inputs:
SourcePath: './build'
azureSubscription: $(azureSubscription)
destination: azureBlob
storage: $(PROD_STORAGE_ACCOUNT)
containerName: '$web'
condition: eq(variables['Build.SourceBranch'], 'refs/heads/master')
- bash: |
echo
echo "Dev url: $DEV_URL"
displayName: "Dev URL"
- bash: |
echo
echo "Prod url: $PROD_URL"
displayName: "Prod URL"
condition: eq(variables['Build.SourceBranch'], 'refs/heads/master')
@@ -1,8 +0,0 @@
steps:
- task: Bash@3
displayName: 'Pull latest from branch'
inputs:
targetType: filePath
filePath: './scripts/git-pull-current-branch.sh'
env:
SOURCE_BRANCH: $(Build.SourceBranch)
@@ -1,60 +0,0 @@
parameters:
versionType: ''
jobs:
- job: "version_bump"
variables:
- group: GitHub-Deploy-Creds
timeoutInMinutes: 30 # timeout on job if deploy is not completed in 30 minutes
cancelTimeoutInMinutes: 1 # time limit to wait for job to cancel
pool:
vmImage: macOS-latest # ssh key was generated on a Mac so using the same type of OS here
steps:
- task: NodeTool@0
inputs:
versionSpec: 10.x
displayName: 'Install Node.js'
# Download secure file
# Download a secure file to the agent machine
- task: DownloadSecureFile@1
# name: sshKey # The name with which to reference the secure file's path on the agent, like $(mySecureFile.secureFilePath)
inputs:
secureFile: vott_id_rsa
# Install an SSH key prior to a build or deployment
- task: InstallSSHKey@0 # https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/install-ssh-key?view=azure-devops
inputs:
knownHostsEntry: $(KNOWN_HOSTS_ENTRY)
sshPublicKey: $(SSH_PUBLIC_KEY)
#sshPassphrase: # Optional
sshKeySecureFile: vott_id_rsa
env:
KNOWN_HOSTS_ENTRY: $(KNOWN_HOSTS_ENTRY)
SSH_PUBLIC_KEY: $(SSH_PUBLIC_KEY) # map to the right format (camelCase) that Azure credentials understand
- task: Bash@3
name: BumpNpmVersion
displayName: Bump NPM Version
inputs:
targetType: filePath
filePath: ./scripts/version-bump-commit.sh
arguments:
${{ parameters.versionType }}
env:
SOURCE_BRANCH: $(Build.SourceBranch)
- bash: |
printenv | sort
mkdir -p $(System.DefaultWorkingDirectory)/variables
echo "$NEXT_VERSION" > $(System.DefaultWorkingDirectory)/variables/NEXT_VERSION
echo "$CURRENT_VERSION" > $(System.DefaultWorkingDirectory)/variables/CURRENT_VERSION
displayName: "Prep variables for publishing"
# Publish the variables folder as pipeline artifact
- publish: $(System.DefaultWorkingDirectory)/variables
artifact: variables
-74
Ver Arquivo
@@ -1,74 +0,0 @@
name: $(Date:yyyyMMdd).$(Hours)$(Minutes)$(Seconds)
variables:
AZURE_STORAGE_ACCOUNT: vottv2
azureSubscription: 'pj-little-sub'
REPORT_URL: 'https://vottv2.z22.web.core.windows.net/'
trigger:
- dev*
- master
pr: none # disable CI build for PR
pool:
vmImage: 'Ubuntu-16.04'
steps:
- bash: |
set -e
COMMIT_SHA=`echo ${BUILD_SOURCEVERSION} | cut -c1-8`
echo "sha: " $COMMIT_SHA
# rewrite build number
echo "##vso[build.updatebuildnumber]Report-${COMMIT_SHA}-${BUILD_BUILDNUMBER}"
displayName: "Rewrite build number"
- bash: |
set -e
ACCOUNT=$(AZURE_STORAGE_ACCOUNT)
if [[ -z "${ACCOUNT}" ]]; then
echo "Need to set AZURE_STORAGE_ACCOUNT"
exit 1
fi
displayName: "Verify storage account cred exists"
- task: NodeTool@0
displayName: "Use Node 10.x"
inputs:
versionSpec: 10.x
- task: Npm@1
displayName: 'Run `npm ci`'
inputs:
command: custom
verbose: false
customCommand: ci
- task: AzureCLI@1
displayName: "Pull down old report and add updates"
inputs:
azureSubscription: $(azureSubscription)
scriptLocation: inlineScript
inlineScript: './scripts/update-report.sh'
- bash: |
set -e
cat /tmp/download.log
displayName: "print download log"
condition: succeededOrFailed()
- task: AzureFileCopy@3
displayName: 'AzureBlob File Copy to $(AZURE_STORAGE_ACCOUNT)'
inputs:
SourcePath: './report'
azureSubscription: $(azureSubscription)
destination: azureBlob
storage: $(AZURE_STORAGE_ACCOUNT)
containerName: '$web'
- bash: |
echo "See report: $(REPORT_URL) "
displayName: "Report URl"
@@ -0,0 +1,35 @@
trigger:
branches:
include:
- dev*
- master
pr: none # disable CI build for PR
pool:
vmImage: 'Ubuntu-16.04'
steps:
- task: NodeTool@0
displayName: 'Use Node 10.x'
inputs:
versionSpec: 10.x
- task: Npm@1
displayName: 'npm ci'
inputs:
command: custom
workingDir: .
verbose: false
customCommand: ci
- task: Bash@3
displayName: 'Create artifact'
inputs:
targetType: filePath
filePath: './scripts/generate-web-artifact.sh'
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: build'
inputs:
PathtoPublish: ./build
ArtifactName: build
@@ -0,0 +1,24 @@
steps:
- task: NodeTool@0
displayName: 'Use Node 10.x'
inputs:
versionSpec: 10.x
- bash: |
set -ex
# clean install
npm ci
npm run release-ci
mkdir -p windows
cp releases/vott*.exe windows/
displayName: Build
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: windows'
inputs:
PathtoPublish: windows
ArtifactName: windows
+1 -1
Ver Arquivo
@@ -4,4 +4,4 @@ const common = require('./webpack.common.js')
module.exports = merge(common, {
mode: 'development',
devtool: "inline-source-map",
})
})
-66
Ver Arquivo
@@ -1,66 +0,0 @@
# Overview
[![Build Status](https://dev.azure.com/msft-vott/VoTT/_apis/build/status/VoTT/Create%20Release?branchName=master)](https://dev.azure.com/msft-vott/VoTT/_build/latest?definitionId=55&branchName=master)
Instruction on how to create new GitHub & Web Releases.
## Release Process
![alt text](./images/release-process.png "Create Release Process")
### AzDO Tasks
[GitHub Release Task](https://github.com/microsoft/azure-pipelines-tasks/tree/master/Tasks/GitHubReleaseV1)
[Azure File Copy](https://github.com/microsoft/azure-pipelines-tasks/tree/master/Tasks/AzureFileCopyV3)
## Versioning
Follow [NPM Semantic Versioning](https://docs.npmjs.com/about-semantic-versioning#incrementing-semantic-versions-in-published-packages)
| Code status | Stage | Rule | Example version |
| ----------------------------------------- | ------------- | ------------------------------------------------------------------ | --------------- |
| First release | New product | Start with 1.0.0 | 1.0.0 |
| Backward compatible bug fixes | Patch release | Increment the third digit | 1.0.1 |
| Backward compatible new features | Minor release | Increment the middle digit and reset last digit to zero | 1.1.0 |
| Changes that break backward compatibility | Major release | Increment the first digit and reset middle and last digits to zero | 2.0.0 |
### Commands
The pipeline use [npm-version](https://docs.npmjs.com/cli/version) to update version
#### Pre
All version with `pre`, ie. `preminor` will bump the appropriate didgit & append `-0` to the new version
Examples:
`npm version prepatch`
1. v2.3.0 --> v2.3.1-0
1. v2.3.1-0 --> v2.3.2-0
`npm version preminor`
1. v2.3.0 --> v2.4.0-0
1. v2.4.0-0 --> v2.5.0-0
##### Exception
`prerelease` behave similar to prepatch, but would increment the last digit.
Examples:
`npm version prerelease`
v2.3.0 --> v2.3.1-0
v2.3.0-0 --> v2.3.0-1
#### Major
v2.x.x --> v3.0.0
#### Minor
v2.2.0 --> v2.3.0
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 87 KiB

+1 -1
Ver Arquivo
@@ -19,6 +19,6 @@ linux:
target:
- snap
publish: null
electronVersion: 3.0.13
electronVersion: 6.0.3
extraFiles:
- "cocoSSDModel"
+9655 -7257
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+84 -84
Ver Arquivo
@@ -1,6 +1,6 @@
{
"name": "vott",
"version": "2.2.0",
"version": "2.1.0",
"author": {
"name": "Microsoft",
"url": "https://github.com/Microsoft/VoTT"
@@ -14,51 +14,6 @@
"license": "MIT",
"private": true,
"main": "build/main.js",
"dependencies": {
"@azure/storage-blob": "^10.3.0",
"@tensorflow/tfjs": "^1.0.3",
"@types/snapsvg": "^0.4.35",
"axios": "^0.18.0",
"bootstrap": "^4.1.3",
"buffer-reverse": "^1.0.1",
"crypto-js": "^3.1.9-1",
"dotenv": "^7.0.0",
"express-request-id": "^1.4.1",
"google-protobuf": "^3.6.1",
"jpeg-js": "^0.3.4",
"json2csv": "^4.5.0",
"lodash": "^4.17.11",
"md5.js": "^1.3.5",
"node-fetch": "^2.3.0",
"node-int64": "^0.4.0",
"rc-align": "^2.4.5",
"rc-checkbox": "^2.1.6",
"rc-menu": "^7.4.21",
"rc-slider": "^8.6.7",
"react": "^16.7.0",
"react-appinsights": "^3.0.0-rc.5",
"react-color": "^2.17.0",
"react-dom": "^16.7.0",
"react-jsonschema-form": "^1.3.0",
"react-localization": "^1.0.13",
"react-modal": "^3.8.1",
"react-redux": "^5.1.1",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.1",
"react-split-pane": "^0.1.87",
"react-tag-input": "^6.1.0",
"react-toastify": "^4.5.2",
"react-virtualized": "^9.21.0",
"react-vis": "^1.11.6",
"reactstrap": "^6.5.0",
"redux": "^4.0.1",
"redux-thunk": "^2.3.0",
"rimraf": "^2.6.2",
"shortid": "^2.2.14",
"video-react": "^0.13.2",
"vott-ct": "2.1.24",
"vott-react": "^0.2.10"
},
"scripts": {
"start": "nf start -p 3000",
"compile": "tsc",
@@ -85,6 +40,89 @@
"postinstall": "electron-builder install-app-deps",
"predebug": "npm run build && npm run webpack:dev"
},
"dependencies": {
"@azure/storage-blob": "^10.4.0",
"@tensorflow/tfjs": "^1.2.8",
"@types/snapsvg": "^0.4.35",
"axios": "^0.18.0",
"bootstrap": "^4.1.3",
"buffer-reverse": "^1.0.1",
"crypto-js": "^3.1.9-1",
"dotenv": "^7.0.0",
"google-protobuf": "^3.6.1",
"jpeg-js": "^0.3.4",
"json2csv": "^4.5.0",
"lodash": "^4.17.11",
"md5.js": "^1.3.5",
"node-fetch": "^2.3.0",
"node-int64": "^0.4.0",
"rc-align": "^2.4.5",
"rc-checkbox": "^2.1.6",
"rc-menu": "^7.4.21",
"rc-slider": "^8.6.7",
"react": "^16.9.0",
"react-appinsights": "^3.0.0",
"react-color": "^2.17.0",
"react-dom": "^16.9.0",
"react-jsonschema-form": "^1.3.0",
"react-localization": "^1.0.13",
"react-modal": "^3.8.1",
"react-redux": "^5.1.1",
"react-router-dom": "^4.3.1",
"react-scripts": "^3.1.1",
"react-split-pane": "^0.1.87",
"react-tag-input": "^6.1.0",
"react-toastify": "^4.5.2",
"react-virtualized": "^9.21.1",
"react-vis": "^1.11.6",
"reactstrap": "^6.5.0",
"redux": "^4.0.4",
"redux-thunk": "^2.3.0",
"rimraf": "^2.6.2",
"shortid": "^2.2.14",
"video-react": "^0.13.9",
"vott-ct": "2.1.24",
"vott-react": "^0.2.10"
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.5.0",
"@types/axios": "^0.14.0",
"@types/dotenv": "^6.1.0",
"@types/enzyme": "^3.10.3",
"@types/jest": "^23.3.13",
"@types/json2csv": "^4.4.0",
"@types/node": "10.12.7",
"@types/react": "^16.9.2",
"@types/react-dom": "^16.9.0",
"@types/react-jsonschema-form": "^1.0.12",
"@types/react-router-dom": "^4.3.5",
"@types/react-split-pane": "^0.1.67",
"@types/react-toastify": "^4.0.1",
"@types/reactstrap": "^6.4.3",
"@types/redux-logger": "^3.0.6",
"@types/redux-mock-store": "^1.0.0",
"cross-env": "^5.2.0",
"electron": "^6.0.4",
"electron-builder": "^21.2.0",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
"eslint": "^6.2.2",
"foreman": "^3.0.1",
"jest-enzyme": "^7.1.0",
"jquery": "^3.3.1",
"node-sass": "^4.10.0",
"popper.js": "^1.14.6",
"react-test-renderer": "^16.9.0",
"redux-immutable-state-invariant": "^2.1.0",
"redux-logger": "^3.0.6",
"redux-mock-store": "^1.5.3",
"ts-loader": "^5.3.0",
"tslint": "^5.11.0",
"typescript": "^3.5.3",
"webpack": "^4.39.1",
"webpack-cli": "^3.3.7",
"webpack-merge": "^4.1.5"
},
"eslintConfig": {
"extends": "react-app"
},
@@ -94,44 +132,6 @@
"not ie <= 11",
"not op_mini all"
],
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.5.0",
"@types/axios": "^0.14.0",
"@types/dotenv": "^6.1.0",
"@types/enzyme": "^3.1.15",
"@types/jest": "23.3.9",
"@types/json2csv": "^4.4.0",
"@types/node": "10.12.7",
"@types/react": "16.7.6",
"@types/react-dom": "16.0.9",
"@types/react-jsonschema-form": "^1.0.12",
"@types/react-router-dom": "^4.3.1",
"@types/react-split-pane": "^0.1.67",
"@types/react-toastify": "^4.0.1",
"@types/reactstrap": "^6.4.3",
"@types/redux-logger": "^3.0.6",
"@types/redux-mock-store": "^1.0.0",
"cross-env": "^5.2.0",
"electron": "^3.0.13",
"electron-builder": "^22.6.0",
"enzyme": "^3.7.0",
"enzyme-adapter-react-16": "^1.7.0",
"foreman": "^3.0.1",
"jest-enzyme": "^7.0.1",
"jquery": "^3.3.1",
"mock-fs": "^4.13.0",
"node-sass": "^4.14.1",
"popper.js": "^1.14.6",
"redux-immutable-state-invariant": "^2.1.0",
"redux-logger": "^3.0.6",
"redux-mock-store": "^1.5.3",
"ts-loader": "^5.3.0",
"tslint": "^5.11.0",
"typescript": "^3.1.6",
"webpack": "^4.19.1",
"webpack-cli": "^3.1.2",
"webpack-merge": "^4.1.5"
},
"engines": {
"node": ">=10.14.2",
"npm": ">=6.4.1"
+88
Ver Arquivo
@@ -0,0 +1,88 @@
trigger: none # manual queue only when we're ready to release
pr: none # disable CI build for PR
stages:
- stage: version_bump_commit
jobs:
- job: "version_bump"
variables:
- group: GitHub-Deploy-Creds
timeoutInMinutes: 30 # timeout on job if deploy is not completed in 30 minutes
cancelTimeoutInMinutes: 1 # time limit to wait for job to cancel
pool:
vmImage: macOS-10.13 # ssh key was generated on a Mac so using the same type of OS here
steps:
- task: NodeTool@0
inputs:
versionSpec: 10.x
displayName: 'Install Node.js'
# Download secure file
# Download a secure file to the agent machine
- task: DownloadSecureFile@1
# name: sshKey # The name with which to reference the secure file's path on the agent, like $(mySecureFile.secureFilePath)
inputs:
secureFile: vott_id_rsa
# Install an SSH key prior to a build or deployment
- task: InstallSSHKey@0 # https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/install-ssh-key?view=azure-devops
inputs:
knownHostsEntry: $(KNOWN_HOSTS_ENTRY)
sshPublicKey: $(SSH_PUBLIC_KEY)
#sshPassphrase: # Optional
sshKeySecureFile: vott_id_rsa
env:
KNOWN_HOSTS_ENTRY: $(KNOWN_HOSTS_ENTRY)
SSH_PUBLIC_KEY: $(SSH_PUBLIC_KEY) # map to the right format (camelCase) that Azure credentials understand
- task: Bash@3
name: BumpNpmVersion
displayName: Bump NPM Prerelease Version
inputs:
targetType: filePath
filePath: ./scripts/version-bump-commit.sh
env:
SOURCE_BRANCH: $(Build.SourceBranch)
- stage: package_build
dependsOn: version_bump_commit
jobs:
- template: azure-pipelines/templates/build-artifact.yml
parameters:
name: Linux
pool:
vmImage: ubuntu-16.04
os: linux
artifact: vott*.snap
- template: azure-pipelines/templates/build-artifact.yml
parameters:
name: Windows
pool:
vmImage: vs2017-win2016
os: windows
artifact: vott*.exe
- template: azure-pipelines/templates/build-artifact.yml
parameters:
name: MacOS
pool:
vmImage: macOS-10.13
os: mac
artifact: vott*.dmg
- stage: github_release
dependsOn: package_build
jobs:
- template: azure-pipelines/templates/create-github-release.yml
parameters:
GitHubConnection: 'GitHub connection' # defaults for any parameters that aren't specified
repositoryName: 'Microsoft/VoTT'
releaseNotesSource: input
addChangeLog: false
isPreRelease: true
isDraft: false
+34
Ver Arquivo
@@ -0,0 +1,34 @@
trigger:
- refs/tags/* # trigger on any tag
pr: none # disable CI build for PR
jobs:
- job: Linux
condition: succeeded()
pool:
vmImage: ubuntu-16.04
timeoutInMinutes: 60 # how long to run the job before automatically cancelling
steps:
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
fetchDepth: 1 # the depth of commits to ask Git to fetch
- template: azure-pipelines/linux/artifact-build-linux.yml
- job: MacOS
condition: succeeded()
pool:
vmImage: macOS-10.13
timeoutInMinutes: 60 # how long to run the job before automatically cancelling
steps:
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
fetchDepth: 1 # the depth of commits to ask Git to fetch
- template: azure-pipelines/mac/artifact-build-mac.yml
- job: Windows
condition: succeeded()
pool:
vmImage: vs2017-win2016
timeoutInMinutes: 60 # how long to run the job before automatically cancelling
steps:
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
fetchDepth: 1 # the depth of commits to ask Git to fetch
- template: azure-pipelines/windows/artifact-build-windows.yml
-7
Ver Arquivo
@@ -49,15 +49,8 @@ echo "commit=${COMMIT_SHA}"
# these are just needed for the reports; just install ad-hoc and don't save to package.json
npm install es6-plato eslint-plugin-react command-line-args --no-save
echo
echo "------> Finish installing dependencies"
echo
echo "------> Transpile TS to ES6"
# we can't do complexity analysis on TypeScript directly; transpile to ES6
rm -rf ${ES6_SRC}
tsc --noEmit false --outDir ${ES6_SRC}
echo
echo "------> Running complexity analasis ..."
node ${BASEDIR}/complexity-analysis.js --src ${ES6_SRC} --output ${REPORT_DIR} --version ${VERSION} --commit ${COMMIT_SHA}
-10
Ver Arquivo
@@ -1,10 +0,0 @@
#!/bin/bash
set -euo pipefail
# Get full branch name excluding refs/head from the env var SOURCE_BRANCH
SOURCE_BRANCH_NAME=${SOURCE_BRANCH/refs\/heads\/}
echo "SOURCE_BRANCH: ${SOURCE_BRANCH_NAME}"
git pull origin ${SOURCE_BRANCH_NAME}
git checkout ${SOURCE_BRANCH_NAME}
echo "Checked out branch: ${SOURCE_BRANCH_NAME}"
Arquivo executável → Arquivo normal
+6 -18
Ver Arquivo
@@ -1,16 +1,12 @@
#!/bin/bash
set -euo pipefail
NPM_VERSION_TYPE=${1:-"prepatch --preid=preview"}
echo "Next version type: ----->$NPM_VERSION_TYPE<-----"
PACKAGE_VERSION=$(node -pe "require('./package.json').version")
CURRENT_VERSION="v$PACKAGE_VERSION"
NPM_RELEASE_TYPE=${1-"prepatch --preid=preview"}
# Get full branch name excluding refs/head from the env var SOURCE_BRANCH
SOURCE_BRANCH_NAME=${SOURCE_BRANCH/refs\/heads\/}
# Configure git to commit as VoTT Service Account
# Configure git to commit as SLS Azure Functions Service Account
echo "Configuring git to use deploy key..."
git config --local user.email "vott@microsoft.com"
git config --local user.name "Vott"
@@ -20,21 +16,13 @@ git pull origin ${SOURCE_BRANCH_NAME}
git checkout ${SOURCE_BRANCH_NAME}
echo "Checked out branch: ${SOURCE_BRANCH_NAME}"
## format: v2.2.0
NEXT_VERSION=`npm version ${NPM_VERSION_TYPE} -m "release: Update ${NPM_VERSION_TYPE} version to %s ***NO_CI***"`
echo "Set next version to: ${NEXT_VERSION}"
NPM_VERSION=`npm version ${NPM_RELEASE_TYPE} -m "release: Update ${NPM_RELEASE_TYPE} version to %s ***NO_CI***"`
echo "Set NPM version to: ${NPM_VERSION}"
# There is currently no way to pass variables between stages, hence this workaround
echo
echo "##vso[task.setvariable variable=NEXT_VERSION]$NEXT_VERSION"
echo "##vso[task.setvariable variable=CURRENT_VERSION]$CURRENT_VERSION"
#### Push new tag
SHA=`git rev-parse HEAD`
export GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
export GIT_SSH_COMMAND="ssh -vvv -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
git remote add authOrigin git@github.com:microsoft/VoTT.git
git push authOrigin ${SOURCE_BRANCH_NAME} --tags
echo
echo "Pushed new tag: ${NEXT_VERSION} @ SHA: ${SHA:0:8}"
echo "Pushed new tag: ${NPM_VERSION} @ SHA: ${SHA:0:8}"
-5
Ver Arquivo
@@ -1,5 +0,0 @@
APP_ID=xyz
APP_SECRET=asdf
COOKIE_SECRETS="[ { key: '12345678901234567890123456789012', iv: '123456789012' }, { key: 'abcdefghijklmnopqrstuvwxyzabcdef', iv: 'abcdefghijkl' }, ])"
ALLOW_HTTP=true
BASE_URL=http://localhost:3000/
-35
Ver Arquivo
@@ -1,35 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch via NPM",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run-script",
"debug"
],
"port": 9229
},
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/lib/app.js", //"${workspaceFolder}\\lib\\app.js",
"args": [
"|",
"bunyan"
],
"outFiles": [
"${workspaceFolder}/**/*.js"
],
"console": "internalConsole",
"outputCapture": "std",
}
]
}
-24
Ver Arquivo
@@ -1,24 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "build",
"problemMatcher": [
"$tsc"
],
"group": "build"
},
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"option": "watch",
"problemMatcher": [
"$tsc-watch"
],
"group": "build"
}
]
}
-75
Ver Arquivo
@@ -1,75 +0,0 @@
# Node.js
# Build a general Node.js project with npm.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
trigger: none
pr: none
variables:
# Azure Resource Manager connection created during pipeline creation
azureSubscription: fe7b93fe-e836-4a55-804c-883dbea6af24'
# Web app name
webAppName: 'vott'
# Agent VM image name
vmImageName: 'ubuntu-latest'
stages:
- stage: Build
displayName: Build stage
jobs:
- job: Build
displayName: Build
pool:
vmImage: $(vmImageName)
steps:
- task: NodeTool@0
inputs:
versionSpec: '10.x'
displayName: 'Install Node.js'
- script: |
npm install
npm run build --if-present
# npm run test --if-present
workingDirectory: $(System.DefaultWorkingDirectory)/server
displayName: 'npm install, build and test'
- task: ArchiveFiles@2
displayName: 'Archive files'
inputs:
rootFolderOrFile: '$(System.DefaultWorkingDirectory)/server'
includeRootFolder: false
archiveType: zip
archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
replaceExistingArchive: true
- upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
artifact: drop
- stage: Deploy
displayName: Deploy stage
dependsOn: Build
condition: succeeded()
jobs:
- deployment: Deploy
displayName: Deploy
environment: 'development'
pool:
vmImage: $(vmImageName)
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
displayName: 'Azure Web App Deploy: vott'
inputs:
azureSubscription: $(azureSubscription)
appType: webAppLinux
appName: $(webAppName)
runtimeStack: 'NODE|10.10'
package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip
startUpCommand: 'npm run start'
-6
Ver Arquivo
@@ -1,6 +0,0 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
rootDir: "src",
coverageDirectory: "../coverage",
};
-7320
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
-62
Ver Arquivo
@@ -1,62 +0,0 @@
{
"name": "vott-server",
"version": "1.0.0",
"description": "Server to support VoTT with login",
"main": "./lib/app.js",
"scripts": {
"build": "tsc",
"start": "node ./lib/app.js",
"test:unit": "jest --runInBand",
"test": "npm run lint && npm run test:unit",
"watch": "concurrently --kill-others \"tsc -w\" \"nodemon --inspect ./lib/app.js\"",
"lint": "tslint -q -p . -c tslint.json",
"lint:fix": "tslint --fix -p . -c tslint.json",
"debug": "nodemon --inspect ./lib/app.js | bunyan"
},
"author": "Microsoft",
"license": "MIT",
"dependencies": {
"@microsoft/microsoft-graph-client": "^1.7.0",
"body-parser": "^1.15.2",
"bunyan": "*",
"cookie-parser": "^1.4.3",
"cookie-session": "^1.3.3",
"cookies": "^0.7.3",
"ejs": ">= 0.0.0",
"ejs-locals": ">= 0.0.0",
"express": "^4.17.1",
"express-request-id": "^1.4.1",
"method-override": "^3.0.0",
"morgan": "^1.9.1",
"node-fetch": "^2.6.0",
"passport": "*",
"passport-azure-ad": "^4.1.0",
"simple-oauth2": "^2.2.1"
},
"devDependencies": {
"@types/bunyan": "^1.8.6",
"@types/cookie-parser": "^1.4.2",
"@types/cookie-session": "^2.0.37",
"@types/cookies": "^0.7.2",
"@types/dotenv": "^6.1.1",
"@types/express": "^4.17.1",
"@types/express-request-id": "^1.4.1",
"@types/jest": "^24.0.17",
"@types/method-override": "0.0.31",
"@types/morgan": "^1.7.37",
"@types/node-fetch": "^2.5.0",
"@types/passport": "^1.0.1",
"@types/passport-azure-ad": "^4.0.3",
"@types/simple-oauth2": "^2.2.1",
"concurrently": "^4.1.1",
"dotenv": "^8.1.0",
"jest": "^24.8.0",
"nodemon": "^1.19.1",
"ts-jest": "^24.0.2",
"tslint": "^5.18.0",
"typescript": "^3.6.2"
},
"engines": {
"node": ">= 10.0.0"
}
}
-71
Ver Arquivo
@@ -1,71 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Simple Integration Tests</title>
</head>
<body>
<h2>Simple integration tests</h2>
<p>You must be logged in to use tests</p>
<button onclick="test_get_me()">GET /api/v1.0/me</button><br />
<button onclick="test_get_profile()">GET /api/v1.0/profile</button><br />
<button onclick="test_put_profile()">PUT /api/v1.0/profile { "foo": "bar" }</button><br />
<br />
<button onclick="test_get_connection()">GET /api/v1.0/cloudconnections/connection1</button><br />
<button onclick="test_put_connection()">PUT /api/v1.0/cloudconnections/connection1 { "foo": "bar" }</button><br />
<button onclick="test_put_connection_alt()">PUT /api/v1.0/cloudconnections/connection1 { "foo": "baz" }</button><br />
<button onclick="test_patch_connection()">PATCH /api/v1.0/cloudconnections/connection1 { "updated": ${now} }</button><br />
<button onclick="test_delete_connection()">DELETE /api/v1.0/cloudconnections/connection1</button><br />
<pre id='result'></pre>
<script>
function fetchOptions(method, thing) {
return { method, body: JSON.stringify(thing), headers: { 'Content-Type': 'application/json' } };
}
async function displayResponse(response) {
let json = null;
try { json = await response.json().catch(); } catch (err) { }
let result = (response.ok ? '*success*' : '*failed*') + '\n' + (json ? JSON.stringify(json, undefined, 2) : '');
document.getElementById("result").innerHTML = result;
}
async function test_get_me(e) {
let response = await fetch('/api/v1.0/me');
displayResponse(response);
}
async function test_get_profile(e) {
let response = await fetch('/api/v1.0/profile');
displayResponse(response);
}
async function test_put_profile(e) {
let response = await fetch('/api/v1.0/profile', fetchOptions("PUT", { foo: "bar" }));
displayResponse(response);
}
async function test_get_connection(e) {
let response = await fetch('/api/v1.0/cloudconnections/connection1');
displayResponse(response);
}
async function test_put_connection(e) {
let response = await fetch('/api/v1.0/cloudconnections/connection1', fetchOptions("PUT", { foo: "bar" }));
displayResponse(response);
}
async function test_put_connection_alt(e) {
let response = await fetch('/api/v1.0/cloudconnections/connection1', fetchOptions("PUT", { foo: "baz" }));
displayResponse(response);
}
async function test_patch_connection(e) {
let now = (new Date(Date.now())).toISOString();
let response = await fetch('/api/v1.0/cloudconnections/connection1', fetchOptions("PATCH", { updated: now }));
displayResponse(response);
}
async function test_delete_connection(e) {
let response = await fetch('/api/v1.0/cloudconnections/connection1', fetchOptions("DELETE", undefined));
displayResponse(response);
}
</script>
</body>
</html>
-8
Ver Arquivo
@@ -1,8 +0,0 @@
<% if (!user) { %>
<h2>Welcome! Please log in.</h2>
<a href="/login">Log In</a>
<% } else { %>
<p>Profile ID: <%= user.oid %></p>
<p>Email: <%= user.mail %></p>
<pre><%- JSON.stringify(user, null, 2) %></pre>
<% } %>
-14
Ver Arquivo
@@ -1,14 +0,0 @@
<% if (!user) { %>
<h2>Welcome! Please log in.</h2>
<a href="/login">Log In</a></br>
<a href="https://myapps.microsoft.com">Manage your permissions (organization)</a></br>
<a href="https://account.live.com/consent/Manage">Manage account permissions (personal)</a>
<% } else { %>
<h2>Hello, <%= user.displayName %></h2>
<a href="/account">Account Info</a></br>
<a href="/public/test.html">Run tests</a></br>
<a href="/endsession">End session</a></br>
<a href="/logout">Log Out</a></br>
<a href="https://myapps.microsoft.com">Manage your permissions (organization)</a></br>
<a href="https://account.live.com/consent/Manage">Manage account permissions (personal)</a>
<% } %>
-21
Ver Arquivo
@@ -1,21 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Passport-OpenID Example</title>
</head>
<body>
<% if (!user) { %>
<p>
<a href="/">Home</a> |
<a href="/login">Log In</a>
</p>
<% } else { %>
<p>
<a href="/">Home</a> |
<a href="/account">Account</a> |
<a href="/logout">Log Out</a>
</p>
<% } %>
<%- body %>
</body>
</html>
-50
Ver Arquivo
@@ -1,50 +0,0 @@
// import { default as fetch } from 'node-fetch';
import * as config from '../config';
import { app, server } from '../app';
import { ServerResponse } from 'http';
beforeAll(async (done) => {
if (!server.listening) {
server.on('listening', done())
} else {
done();
}
});
afterAll(async (done) => {
server.close(() => {
console.log('done');
done();
});
});
describe('App Server', () => {
afterAll(async (done) => {
server.close(() => {
console.log('done');
done();
});
});
test('initialized', async (done) => {
expect(app.name).toBeDefined();
expect(server.listening).toBe(true);
done();
});
test('loads app.html', async (done) => {
const response = await fetch(config.baseUrl);
expect(response.status).toBe(200);
done();
});
test('redirects to login', async (done) => {
const response = await fetch(config.baseUrl + '/api/v1.0/me');
expect(response.status).toBe(404);
done();
});
});
-13
Ver Arquivo
@@ -1,13 +0,0 @@
// import { default as fetch } from 'node-fetch';
import * as config from '../config';
describe('App Server', () => {
test('should be closed', async (done) => {
// do nothing
done();
});
});
-364
Ver Arquivo
@@ -1,364 +0,0 @@
import * as bodyParser from 'body-parser';
import * as morgan from 'morgan';
import * as bunyan from 'bunyan';
import * as cookieParser from 'cookie-parser';
import * as express from 'express';
import cookieSession = require('cookie-session');
import * as methodOverride from 'method-override';
import * as passport from 'passport';
import * as passportAzureAD from 'passport-azure-ad';
import * as config from './config';
import * as path from 'path';
import * as express_request_id from 'express-request-id';
import * as simple_oath2 from 'simple-oauth2';
import * as graph from './graph';
import * as oauth2 from './oauth2';
export const log = bunyan.createLogger({
name: 'BUNYAN-LOGGER',
src: true,
});
// -----------------------------------------------------------------------------
// Passport Setup Follows
// -----------------------------------------------------------------------------
// Setup schemas for Passport's User object and the the session persistence format.
// Augument Passport's request.user with the Azure AD oauthToken
declare global {
namespace Express {
interface User {
oid: string;
oauthToken: oauth2.Token;
}
}
}
type FullUserSchema = Express.User; // This now includes the above declaration for User.
interface MinimizedUserSchema {
oid: string;
refresh_token: string;
}
passport.serializeUser((user: FullUserSchema, done) => {
const stored: MinimizedUserSchema = { oid: user.oid, refresh_token: user.oauthToken.refresh_token };
done(null, stored);
});
passport.deserializeUser(async (stored: MinimizedUserSchema, done) => {
if (!stored || !stored.refresh_token) { return done(Error('no user profile')); }
let oauthClient = await oauth2.client(stored);
if (oauthClient.expired()) {
oauthClient = await oauthClient.refresh(); // must reassign here.
}
const profile = await graph.user(oauthClient.token.access_token).catch(reason => { log.error('could not retrieve profile', reason); });
if (!profile) { return done(Error('no user profile')); }
const result = { ...profile, oid: stored.oid, oauthToken: oauthClient.token };
return done(null, result);
});
// -----------------------------------------------------------------------------
// Define the AzureAD OIDCStrategy Strategy
// -----------------------------------------------------------------------------
const OIDCStrategyTemplate = {} as passportAzureAD.IOIDCStrategyOptionWithoutRequest;
const azureStrategyOptions: passportAzureAD.IOIDCStrategyOptionWithRequest = {
identityMetadata: config.creds.identityMetadata,
clientID: config.creds.clientID,
responseType: config.creds.responseType as typeof OIDCStrategyTemplate.responseType,
responseMode: config.creds.responseMode as typeof OIDCStrategyTemplate.responseMode,
redirectUrl: config.creds.redirectUrl,
allowHttpForRedirectUrl: config.creds.allowHttpForRedirectUrl,
clientSecret: config.creds.clientSecret,
validateIssuer: config.creds.validateIssuer,
isB2C: config.creds.isB2C,
issuer: config.creds.issuer,
passReqToCallback: true,
scope: config.creds.scope,
loggingLevel: config.creds.logLevel as typeof OIDCStrategyTemplate.loggingLevel,
nonceLifetime: config.creds.nonceLifetime,
nonceMaxAmount: config.creds.nonceMaxAmount,
useCookieInsteadOfSession: config.creds.useCookieInsteadOfSession,
cookieEncryptionKeys: config.creds.cookieEncryptionKeys,
clockSkew: config.creds.clockSkew,
};
// -----------------------------------------------------------------------------
// Use the Azure OIDCStrategy within Passport.
//
// Strategies in passport require a `verify` function, which accepts credentials
// (in this case, the `oid` claim in id_token), and invoke a callback to find
// the corresponding user object.
//
// The following are the accepted prototypes for the `verify` function
// (1) function(iss, sub, done)
// (2) function(iss, sub, profile, done)
// (3) function(iss, sub, profile, access_token, refresh_token, done)
// (4) function(iss, sub, profile, access_token, refresh_token, params, done)
// (5) function(iss, sub, profile, jwtClaims, access_token, refresh_token, params, done)
// (6) prototype (1)-(5) with an additional `req` parameter as the first parameter
//
// To do prototype (6), passReqToCallback must be set to true in the config.
// -----------------------------------------------------------------------------
passport.use(new passportAzureAD.OIDCStrategy(azureStrategyOptions, processAzureStrategy));
async function processAzureStrategy(req: express.Request,
iss: string, sub: string, profile: passportAzureAD.IProfile, jwtClaims: any,
access_token: string, refresh_token: string, oauthToken: any,
done: passportAzureAD.VerifyCallback) {
if (!profile.oid) {
return done(new Error('No oid found'), null);
}
// asynchronous verification, for effect...
process.nextTick(async () => {
const fullProfile = await graph.user(access_token);
if (!fullProfile) {
return done(Error('no profile'));
}
const oauth = oauth2.client(oauthToken);
const result = { ...fullProfile, oid: profile.oid, oauthToken: oauth.token };
return done(null, result);
});
}
// -----------------------------------------------------------------------------
// Config the express app and all the required middleware
// -----------------------------------------------------------------------------
export const app = express();
app.use(morgan(config.httpLogFormat));
app.set('trust proxy', true);
app.set('views', path.join(__dirname, '../public/views'));
app.set('view engine', 'ejs');
app.use(express_request_id());
app.use(methodOverride());
app.use(cookieParser());
app.use(cookieSession({ keys: config.creds.cookieEncryptionKeys.map(value => value.key), secure: false, maxAge: 1000 * 60 * 60 * 24 * 365 }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(passport.initialize());
app.use(passport.session());
// -----------------------------------------------------------------------------
// Define a couple of authencation request handlers.
// -----------------------------------------------------------------------------
function ensureAuthenticated(req: express.Request, res: express.Response, next: express.NextFunction) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login');
}
function ensureAuthenticatedApi(req: express.Request, res: express.Response, next: express.NextFunction) {
if (req.isAuthenticated()) { return next(); }
res.sendStatus(401).end();
return next();
}
app.get('/', (req, res) => {
const user = { ...req.user, oauthToken: '[removed]'};
res.render('index', { user });
});
app.get('/account', ensureAuthenticated, (req, res, next) => {
const user = { ...req.user, oauthToken: '[removed]'};
res.render('account', { user });
});
app.get('/login', (req, res, next) => {
passport.authenticate('azuread-openidconnect',
{
response: res, // required
customState: 'my_state', // optional. Provide a value if you want to provide custom state value.
failureRedirect: '/',
} as passport.AuthenticateOptions,
)(req, res, next);
},
(req, res) => {
log.info('login was called');
res.redirect('/');
});
// 'GET returnURL'
// `passport.authenticate` will try to authenticate the content returned in
// query (such as authorization code). If authentication fails, user will be
// redirected to '/' (home page); otherwise, it passes to the next middleware.
app.get('/auth/openid/return', (req, res, next) => {
passport.authenticate('azuread-openidconnect',
{
response: res, // required
failureRedirect: '/',
} as passport.AuthenticateOptions,
)(req, res, next);
},
(req, res, next) => {
log.info('received a return from AzureAD.');
res.redirect('/');
});
// 'POST returnURL'
// `passport.authenticate` will try to authenticate the content returned in
// body (such as authorization code). If authentication fails, user will be
// redirected to '/' (home page); otherwise, it passes to the next middleware.
app.post('/auth/openid/return',
(req, res, next) => {
passport.authenticate('azuread-openidconnect',
{
response: res, // required
failureRedirect: '/',
} as passport.AuthenticateOptions,
)(req, res, next);
},
(req, res, next) => {
log.info('received a return from AzureAD.');
res.redirect('/');
});
// 'endsession' route, logout from passport, and destroy the session with AAD.
app.get('/endsession', (req, res) => {
req.session = null;
req.logOut();
res.redirect('/');
});
// 'logout' route, logout from passport, and destroy the session with AAD.
app.get('/logout', (req, res) => {
req.session = null;
// req.session.destroy((err) => {
req.logOut();
res.redirect(config.destroySessionUrl);
});
app.get('/api/v1.0/me', ensureAuthenticatedApi,
async (req, res, next) => {
try {
let oauth = oauth2.client(req.user.oauthToken);
if (oauth.expired()) { oauth = await oauth.refresh(); }
const result = await graph.user(oauth.token.access_token);
res.json(result);
res.end();
next();
} catch (error) {
res.status(error.statusCode).json(error).end();
return next();
}
});
app.get('/api/v1.0/profile', ensureAuthenticatedApi,
async (req, res, next) => {
try {
let oauth = oauth2.client(req.user.oauthToken);
if (oauth.expired()) { oauth = await oauth.refresh(); }
const result = await graph.client(oauth.token.access_token).api('/me/extensions/com.code-with.vott').get();
res.json(result);
res.end();
next();
} catch (error) {
res.status(error.statusCode).json(error).end();
return next();
}
});
app.put('/api/v1.0/profile', ensureAuthenticatedApi,
async (req, res, next) => {
try {
let oauth = oauth2.client(req.user.oauthToken);
if (oauth.expired()) { oauth = await oauth.refresh(); }
const body = { ...req.body, extensionName: 'com.code-with.vott' };
let result = null;
try { // Handle bad graph open extension semantics
result = await graph.client(oauth.token.access_token).api('/me/extensions/').post(body);
} catch (error) {
// if it already exists and we are replacing. Delete and try again.
result = await graph.client(oauth.token.access_token).api('/me/extensions/com.code-with.vott').delete();
result = await graph.client(oauth.token.access_token).api('/me/extensions').post(body);
}
res.json(result);
res.end();
next();
} catch (error) {
res.status(error.statusCode).json(error).end();
return next();
}
});
app.get('/api/v1.0/cloudconnections/:id', ensureAuthenticatedApi,
async (req, res, next) => {
try {
const id = req.params.id; // careful should only be a domain name pattern.
let oauth = oauth2.client(req.user.oauthToken);
if (oauth.expired()) { oauth = await oauth.refresh(); }
const result = await graph.client(oauth.token.access_token).api(`/me/extensions/com.code-with.vott.${id}`).get();
res.json(result);
res.end();
next();
} catch (error) {
res.status(error.statusCode).json(error).end();
return next();
}
});
app.put('/api/v1.0/cloudconnections/:id', ensureAuthenticatedApi,
async (req, res, next) => {
try {
const id = req.params.id; // careful should only be a domain name pattern.
let oauth = oauth2.client(req.user.oauthToken);
if (oauth.expired()) { oauth = await oauth.refresh(); }
const body = { ...req.body, extensionName: `com.code-with.vott.${id}` };
let result = null;
try { // Handle bad graph open extension semantics
result = await graph.client(oauth.token.access_token).api('/me/extensions/').post(body);
} catch (error) {
// if it already exists and we are replacing. Delete and try again.
result = await graph.client(oauth.token.access_token).api(`/me/extensions/com.code-with.vott.${id}`).delete();
result = await graph.client(oauth.token.access_token).api('/me/extensions').post(body);
}
res.json(result);
res.end();
next();
} catch (error) {
res.status(error.statusCode).json(error).end();
return next();
}
});
app.patch('/api/v1.0/cloudconnections/:id', ensureAuthenticatedApi,
async (req, res, next) => {
try {
const id = req.params.id; // careful should only be a domain name pattern.
let oauth = oauth2.client(req.user.oauthToken);
if (oauth.expired()) { oauth = await oauth.refresh(); }
const body = { ...req.body, extensionName: `com.code-with.vott.${id}` };
const result = await graph.client(oauth.token.access_token).api(`/me/extensions/com.code-with.vott.${id}`).patch(body);
res.json(result);
res.end();
next();
} catch (error) {
res.status(error.statusCode).json(error).end();
return next();
}
});
app.delete('/api/v1.0/cloudconnections/:id', ensureAuthenticatedApi,
async (req, res, next) => {
try {
const id = req.params.id; // careful should only be a domain name pattern.
let oauth = oauth2.client(req.user.oauthToken);
if (oauth.expired()) { oauth = await oauth.refresh(); }
const result = await graph.client(oauth.token.access_token).api(`/me/extensions/com.code-with.vott.${id}`).delete();
res.end();
return next();
} catch (error) {
res.status(error.statusCode).json(error).end();
return next();
}
});
app.use('/public', express.static(path.join(__dirname, '../public')));
export const server = app.listen(config.port);
-94
Ver Arquivo
@@ -1,94 +0,0 @@
// tslint:disable-next-line: no-var-requires
require('dotenv').config();
export const baseUrl = process.env.BASE_URL || 'http://localhost:3000/';
export const redirectPath = 'auth/openid/return';
export const port = process.env.PORT || '3000';
export const loggingLevel = process.env.LOGGING_LEVEL || 'info';
export const httpLogFormat = process.env.HTTP_LOG_FORMAT || 'dev';
console.log('config values', process.env.APP_ID, process.env.APP_SECRET, baseUrl, redirectPath, port, loggingLevel, httpLogFormat);
export const creds = {
// Required
identityMetadata: 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration',
// 'https://login.microsoftonline.com/<tenant_name>.onmicrosoft.com/v2.0/.well-known/openid-configuration',
// or equivalently: 'https://login.microsoftonline.com/<tenant_guid>/v2.0/.well-known/openid-configuration'
//
// or you can use the common endpoint
// 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration'
// To use the common endpoint, you have to either turn `validateIssuer` off, or provide the `issuer` value.
// Required, the client ID of your app in AAD
clientID: process.env.APP_ID,
// Required, must be 'code', 'code id_token', 'id_token code' or 'id_token'
// If you want to get access_token, you must use 'code', 'code id_token' or 'id_token code'
responseType: 'code id_token',
// Required
responseMode: 'form_post',
// Required, the reply URL registered in AAD for your app
redirectUrl: baseUrl + redirectPath,
// Required if we use http for redirectUrl
allowHttpForRedirectUrl: process.env.ALLOW_HTTP ? process.env.ALLOW_HTTP === 'true' : true,
// Required if `responseType` is 'code', 'id_token code' or 'code id_token'.
// If app key contains '\', replace it with '\\'.
clientSecret: process.env.APP_SECRET,
// Required to set to false if you don't want to validate issuer
validateIssuer: false,
// Required if you want to provide the issuer(s) you want to validate instead of using the issuer from metadata
// issuer could be a string or an array of strings of the following form: 'https://sts.windows.net/<tenant_guid>/v2.0'
issuer: null as string,
// !Bug - must be false in this sample
// Required to set to true if the `verify` function has 'req' as the first parameter
passReqToCallback: true,
// Recommended to set to true. By default we save state in express session, if this option is set to true, then
// we encrypt state and save it in cookie instead. This option together with { session: false } allows your app
// to be completely express session free.
useCookieInsteadOfSession: true,
logLevel: loggingLevel,
// Required if `useCookieInsteadOfSession` is set to true. You can provide multiple set of key/iv pairs for key
// rollover purpose. We always use the first set of key/iv pair to encrypt cookie, but we will try every set of
// key/iv pair to decrypt cookie. Key can be any string of length 32, and iv can be any string of length 12.
cookieEncryptionKeys: (process.env.COOKIES_SECRETS ? JSON.parse(process.env.COOKIES_SECRETS) :
[
{ key: '12345678901234567890123456789012', iv: '123456789012' },
{ key: 'abcdefghijklmnopqrstuvwxyzabcdef', iv: 'abcdefghijkl' },
]) as Array<{ key: string; iv: string; }>,
// The additional scopes we want besides 'openid'.
// 'profile' scope is required, the rest scopes are optional.
// (1) if you want to receive refresh_token, use 'offline_access' scope
// (2) if you want to get access_token for graph api, use the graph api url like 'https://graph.microsoft.com/mail.read'
scope: ['profile', /* 'offline_access', */ 'https://graph.microsoft.com/user.readwrite'],
// Optional. The lifetime of nonce in session or cookie, the default value is 3600 (seconds).
nonceLifetime: null as number,
// Optional. The max amount of nonce saved in session or cookie, the default value is 10.
nonceMaxAmount: 5,
// Optional. The clock skew allowed in token validation, the default value is 300 seconds.
clockSkew: null as number,
// Optional. Is B2C
isB2C: false,
};
// The url you need to go to destroy the session with AAD
export let destroySessionUrl = 'https://login.microsoftonline.com/common/oauth2/logout?post_logout_redirect_uri=http://localhost:3000';
if (!creds.clientID || !creds.clientSecret) {
console.log('issue with config');
throw Error('Missing configuration. You need a .env file or environment variables for APP_ID and APP_SECRET');
}
-30
Ver Arquivo
@@ -1,30 +0,0 @@
import * as graphClient from '@microsoft/microsoft-graph-client';
export async function user(access_token: string) {
const token = client(access_token);
const result = await token.api('/me').get();
return result;
}
export async function getEvents(access_token: string) {
const token = client(access_token);
const events = await token.api('/me/events')
.select('subject,organizer,start,end')
.orderby('createdDateTime DESC')
.get();
return events;
}
export function client(access_token: string): graphClient.Client {
// Initialize Graph client
const result = graphClient.Client.init({
// Use the provided access token to authenticate
// requests
authProvider: (done: (err: any, access_token: string) => void) => {
done(null, access_token);
},
});
return result;
}
-27
Ver Arquivo
@@ -1,27 +0,0 @@
import * as express from 'express';
import * as simple_oauth2 from 'simple-oauth2';
import * as config from './config';
export const oauth2 = simple_oauth2.create({
client: {
id: config.creds.clientID,
secret: config.creds.clientSecret,
},
auth: {
tokenHost: 'https://login.microsoftonline.com/common',
authorizePath: '/oauth2/v2.0/authorize',
tokenPath: '/oauth2/v2.0/token',
},
});
export interface Token {
refresh_token: string;
access_token?: string;
expires_at?: string | Date;
}
export function client(token: Token) {
token.expires_at = token.expires_at || new Date(0);
const result = oauth2.accessToken.create(token);
return result;
}
-62
Ver Arquivo
@@ -1,62 +0,0 @@
{
"compilerOptions": {
/* Basic Options */
"target": "ES2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
"sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./lib", /* Redirect output structure to the directory. */
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
// "strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
// "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
},
"exclude": [
"node_modules",
"coverage",
"data_models",
"public",
"lib",
"temp",
"jest.config.js",
"src/__tests__"
]
}
-48
Ver Arquivo
@@ -1,48 +0,0 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"linterOptions": {
"exclude": [
"lib",
"public",
"src/routes",
"jest.config.js"
]
},
"jsRules": {},
"rules": {
"no-console": false,
"arrow-parens": false,
"max-classes-per-file": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"align": false,
"interface-name": false,
"quotemark": [
true,
"single",
"avoid-escape",
"avoid-template"
],
"max-line-length": {
"severity": "warning",
"options": [
160,
{
"ignore-pattern": "^import |^export {(.*?)} | //"
}
]
},
"variable-name": {
"options": [
"ban-keywords",
"check-format",
"allow-leading-underscore",
"allow-pascal-case",
"allow-snake-case"
]
}
}
}
+7 -50
Ver Arquivo
@@ -84,10 +84,6 @@ export const english: IAppStrings = {
title: "Security Token",
description: "Used to encrypt sensitive data within project files",
},
useSecurityToken: {
title: "Use Security Token",
description: "When enabled will encrypt sensitive data within provider configuration",
},
save: "Save Project",
sourceConnection: {
title: "Source Connection",
@@ -198,54 +194,15 @@ export const english: IAppStrings = {
},
bing: {
title: "Bing Image Search",
options: {
title: "Bing Image Search Options",
},
endpoint: {
title: "Endpoint",
description: "The endpoint listed within the Bing Search Azure resource",
},
apiKey: {
title: "API Key",
description: "An API key listed within the Bing Search Azure resource",
},
query: {
title: "Query",
description: "The search query used to populate your connection",
},
options: "Bing Image Search Options",
apiKey: "API Key",
query: "Query",
aspectRatio: {
title: "Aspect Ratio",
description: "Filters the results by the specified aspect ratio",
options: {
all: "All",
square: "Square",
wide: "Wide",
tall: "Tall",
},
},
licenseType: {
title: "License Type",
description: "Filters the results by the specified license type",
options: {
all: "All (does not filter any images)",
any: "Any images with any license type",
public: "Public domain",
share: "Free to share and use",
shareCommercially: "Free to share and use commercially",
modify: "Free to modify, share and use",
modifyCommercially: "Free to modify, share and use commercially",
},
},
size: {
title: "Size",
description: "Filters the results by the specified size",
options: {
all: "All",
small: "Small (Less than 200x200)",
medium: "Medium (Less than 500x500)",
large: "Large (Greater than 500x500)",
wallpaper: "Wallpaper (Extra large images)",
},
all: "All",
square: "Square",
wide: "Wide",
tall: "Tall",
},
},
local: {
+7 -50
Ver Arquivo
@@ -85,10 +85,6 @@ export const spanish: IAppStrings = {
title: "Token de seguridad",
description: "Se utiliza para cifrar datos confidenciales dentro de archivos de proyecto",
},
useSecurityToken: {
title: "Usar Token de Seguridad",
description: "Si está habilitado, los datos confidenciales se cifrarán",
},
save: "Guardar el Proyecto",
sourceConnection: {
title: "Conexión de Origen",
@@ -200,54 +196,15 @@ export const spanish: IAppStrings = {
},
bing: {
title: "Búsqueda de Imágenes Bing",
options: {
title: "Opciones de Búsqueda de Imágenes Bing",
},
endpoint: {
title: "Extremo",
description: "El punto de conexión que aparece en el recurso de Bing Search Azure",
},
apiKey: {
title: "Clave API",
description: "Una clave de API que aparece en el recurso de Bing Search Azure",
},
query: {
title: "Consulta",
description: "La consulta de búsqueda utilizada para rellenar la conexión",
},
options: "Opciones de Búsqueda de Imágenes Bing",
apiKey: "Clave API",
query: "Consulta",
aspectRatio: {
title: "Relación de Aspecto",
description: "Filtra los resultados por la relación de aspecto especificada",
options: {
all: "Todos",
square: "Cuadrado",
wide: "Ancho",
tall: "Alto",
},
},
licenseType: {
title: "Tipo de licencia",
description: "Filtra los resultados según el tipo de licencia especificado",
options: {
all: "Todos (no filtra ninguna imagen)",
any: "Cualquier imagen con cualquier tipo de licencia",
public: "Dominio público",
share: "Libre para compartir y usar",
shareCommercially: "Libre para compartir y usar comercialmente",
modify: "Libre de modificar, compartir y usar",
modifyCommercially: "Libre de modificar, compartir y ues comercialmente",
},
},
size: {
title: "Tamaño",
description: "Filtra los resultados según el tamaño especificado",
options: {
all: "Todo",
small: "Pequeño (Menos de 200x200)",
medium: "Medio (Menos de 500x500)",
large: "Grande (mayor de 500x500)",
wallpaper: "Fondo de pantalla (imágenes extra grandes)",
},
all: "Todos",
square: "Cuadrado",
wide: "Ancho",
tall: "Alto",
},
},
local: {
-555
Ver Arquivo
@@ -1,555 +0,0 @@
import { IAppStrings } from "../strings";
/**
* App Strings for Japanese language from Google Translate
*/
export const japanese: IAppStrings = {
appName: "ビジュアル オブジェクトタグ付けツール", // Visual Object Tagging Tool,
common: {
displayName: "表示名", // Display Name,
description: "説明", // Description,
submit: "送信", // Submit,
cancel: "キャンセル", // Cancel,
save: "保存", // Save,
delete: "削除", // Delete,
provider: "プロバイダー", // Provider,
homePage: "ホーム ページ", // Home Page"
},
titleBar: {
help: "ヘルプ", // Help,
minimize: "最小化", // Minimize,
maximize: "最大化", // Maximize,
restore: "戻す", // Restore,
close: "閉じる", // Close"
},
homePage: {
newProject: "新規プロジェクト", // New Project,
openLocalProject: {
title: "ローカルプロジェクトを開く", // Open Local Project"
},
openCloudProject: {
title: "クラウドプロジェクトを開く", // Open Cloud Project,
selectConnection: "接続を選択", // Select a Connection"
},
recentProjects: "最近のプロジェクト", // Recent Projects,
deleteProject: {
title: "プロジェクトを削除", // Delete Project,
confirmation: "プロジェクトを削除してもいいですか", // Are you sure you want to delete project"
},
importProject: {
title: "プロジェクトをインポート", // Import Project,
confirmation: "プロジェクト ${project.file.name} プロジェクト設定を v2 形式に変換してもいいですか",
// Are you sure you want to conver project ${project.file.name} project settings to v2 format?
// We recommend you backup the project file first."
},
messages: {
deleteSuccess: "${project.name} を削除しました", // Successfully deleted ${project.name}"
},
},
appSettings: {
title: "アプリケーション設定", // Application Settings,
storageTitle: "ストレージ設定", // Storage Settings,
uiHelp: "設定の保存場所", // Where your settings are stored,
save: "設定の保存", // Save Settings,
securityToken: {
name: {
title: "名前", // Name"
},
key: {
title: "キー", // Key"
},
},
securityTokens: {
title: "セキュリティ トークン", // Security Tokens,
description: "セキュリティ トークンは、プロジェクト構成内の機密データを暗号化するために使用されます",
// Security tokens are used to encrypt sensitive data within your project configuration"
},
version: {
description: "バージョン:", // Version"
},
commit: "SHA をコミット", // Commit SHA,
devTools: {
description: "問題の診断に役立つアプリケーション開発者ツールを開く", // Open application developer tools to help diagnose issues,
button: "開発者ツールを開く", // Toggle Developer Tools"
},
reload: {
description: "現在の変更をすべて破棄して、アプリをリロード", // Reload the app discarding all current changes,
button: "アプリケーションをリフレッシュ", // Refresh Application"
},
messages: {
saveSuccess: "アプリケーション設定を正常に保存しました", // Successfully saved application settings"
},
},
projectSettings: {
title: "プロジェクト設定", // Project Settings,
securityToken: {
title: "セキュリティ トークン", // Security Token,
description: "プロジェクト ファイル内の機密データを暗号化するために使用されます", // Used to encrypt sensitive data within project file"
},
useSecurityToken: {
title: "セキュリティ トークン", // Use Security Token
description: "有効にすると、プロバイダー構成内の機密データが暗号化されます。",
// When enabled will encrypt sensitive data within provider configuration
},
save: "プロジェクトを保存", // Save Project,
sourceConnection: {
title: "ソース接続", // Source Connection,
description: "アセットのロード元", // Where to load assets fro"
},
targetConnection: {
title: "ターゲット接続", // Target Connection,
description: "プロジェクトとエクスポートされたデータの保存場所", // Where to save the project and exported dat"
},
videoSettings: {
title: "ビデオ設定", // Video Settings,
description: "タグ付けにおけるフレームの抽出割合", // The rate at which frames are extracted for tagging.,
frameExtractionRate: "フレーム抽出率(ビデオ 1 秒あたりのフレーム数)", // Frame Extraction Rate (frames per a video second)"
},
addConnection: "接続を追加", // Add Connection,
messages: {
saveSuccess: "${project.name} プロジェクト設定を正常に保存しました", // Successfully saved ${project.name} project settings"
},
},
projectMetrics: {
title: "プロジェクト メトリック", // Project Metrics,
assetsSectionTitle: "アセット", // Assets,
totalAssetCount: "すべてのアセット", // Total Assets,
visitedAssets: "訪問済みアセット(${count}", // Visited Assets (${count}),
taggedAssets: "タグ付きアセット(${count}", // Tagged Assets (${count}),
nonTaggedAssets: "タグ付けされていないアセット(${count})", // Not Tagged Assets (${count}),
nonVisitedAssets: "未訪問ないアセット(${count}", // Not Visited Assets (${count}),
tagsSectionTitle: "タグ", // Tags & Labels,
totalRegionCount: "タグ付けされたすべての領域", // Total Tagged Regions,
totalTagCount: "すべてのタグ", // Total Tags,
avgTagCountPerAsset: "アセットごとの平均タグ", // Average tags per asset"
},
tags: {
title: "タグ", // Tags,
placeholder: "新しいタグを追加", // Add new tag,
editor: "タグ エディター", // Tags Editor,
modal: {
name: "タグ名", // Tag Name,
color: "タグの色", // Tag Color"
},
colors: {
white: "白", // White,
gray: "グレー", // Gray,
red: "赤", // Red,
maroon: "マルーン", // Maroon,
yellow: "黄", // Yellow,
olive: "オリーブ", // Olive,
lime: "ライム", // Lime,
green: "緑", // Green,
aqua: "アクア", // Aqua,
teal: "ティール", // Teal,
blue: "青", // Blue,
navy: "濃紺", // Navy,
fuschia: "赤紫", // Fuschia,
purple: "紫", // Purple"
},
warnings: {
existingName: "タグ名が既に存在します。別の名前を選んでください", // Tag name already exists. Choose another name,
emptyName: "空のタグ名を持つことはできません", // Cannot have an empty tag name,
unknownTagName: "不明", // Unknown"
},
toolbar: {
add: "新しいタグを追加", // Add new tag,
search: "タグを検索", // Search tags,
edit: "タグを編集", // Edit tag,
lock: "タグをロック", // Lock tag,
moveUp: "タグを上に移動", // Move tag up,
moveDown: "タグを下に移動", // Move tag down,
delete: "タグを削除", // Delete tag"
},
},
connections: {
title: "接続", // Connections,
details: "接続の詳細", // Connection Details,
settings: "接続設定", // Connection Settings,
instructions: "編集する接続を選択してください", // Please select a connection to edit,
save: "接続を保存", // Save Connection,
messages: {
saveSuccess: "${connection.name} を保存しました", // Successfully saved ${connection.name},
deleteSuccess: "${connection.name} を削除しました", // Successfully deleted ${connection.name}"
},
imageCorsWarning: "警告:Web ブラウザーで VoTT を使用する場合、CORS(クロス オリジン リソース共有)の制限により、" +
"Bing 画像検索の一部のアセットが正しくエクスポートされない場合があります。",
// Warning: When using VoTT in a Web browser, some assets from Bing Image Search may no export
// correctly due to CORS (Cross Origin Resource Sharing) restrictions.",
blobCorsWarning: "警告:ソースまたはターゲット接続として使用するには、Azure Blob Storage アカウントで CORS(クロス オリジン リソース共有)を有効にする必要があります。 ",
// Warning: CORS (Cross Domain Resource Sharing) must be enabled on the Azure Blob Storage account, in order
// to use i as a source or target connection. More information on enabling CORS can be found in the {0}",
azDocLinkText: "Azure ドキュメント", // Azure Documentation.,
providers: {
azureBlob: {
title: "Azure Blob Storage", // Azure Blob Storage,
description: "",
accountName: {
title: "アカウント名", // Account Name,
description: "",
},
containerName: {
title: "コンテナー名", // Container Name,
description: "",
},
sas: {
title: "SAS", // SAS,
description: "Blob Storage アカウントの認証に使用される共有アクセス署名",
// Shared access signature used to authenticate to the blob storage account"
},
createContainer: {
title: "コンテナーを作成", // Create Container,
description: "Blob Storage コンテナーがまだ存在しない場合は作成します",
// Creates the blob container if it does not already exist"
},
},
bing: {
title: "Bing 画像検索", // Bing Image Search,
options: {
title: "Bing 画像検索のオプション",
}, // Bing Image Search Options,
endpoint: {
title: "エンドポイント", // Endpoint
description: "Bing検索 Azure リソース内に一覧表示されるエンドポイント",
},
apiKey: {
title: "APIキー", // API Key
description: "Bing検索 Azure リソース内に表示される API キー",
},
query: {
title: "クエリ", // Query
description: "接続の設定に使用する検索クエリ",
},
aspectRatio: {
title: "アスペクト比", // Aspect Ratio,
description: "指定した縦横比で結果をフィルター処理します。",
options: {
all: "すべて", // All,
square: "正方形", // Square,
wide: "横長", // Wide,
tall: "縦長", // Tall"
},
},
licenseType: {
title: "ライセンスの種類",
description: "指定したライセンスの種類で結果をフィルター処理します。",
options: {
all: "すべて (画像をフィルター処理しません)",
any: "任意のライセンスタイプの画像",
public: "パブリック ドメイン",
share: "無料で共有・使用",
shareCommercially: "無料で共有し、商業的に使用する",
modify: "変更、共有、使用が無料",
modifyCommercially: "無料で変更、共有、および商用で使用",
},
},
size: {
title: "サイズ",
description: "結果を指定したサイズでフィルター処理します。",
options: {
all: "すべての",
small: "小 (200x200 未満)",
medium: "中 (500x500 未満)",
large: "大 (500x500 より大きい)",
wallpaper: "壁紙(特大画像)",
},
},
},
local: {
title: "ローカル ファイル システム", // Local File System,
folderPath: "フォルダー パス", // Folder Path,
selectFolder: "フォルダーを選択", // Select Folder,
chooseFolder: "フォルダーを選択", // Choose Folder"
},
},
},
editorPage: {
width: "幅", // Width,
height: "高さ", // Height,
tagged: "タグ付き", // Tagged,
visited: "訪問済み", // Visited,
toolbar: {
select: "選択(V", // Select (V),
pan: "パン", // Pan,
drawRectangle: "長方形を描く", // Draw Rectangle,
drawPolygon: "ポリゴンを描く", // Draw Polygon,
copyRectangle: "長方形をコピー", // Copy Rectangle,
copy: "領域をコピー", // Copy Regions,
cut: "領域をカット", // Cut Regions,
paste: "領域を貼り付け", // Paste Regions,
removeAllRegions: "すべてのリージョンを削除", // Remove All Regions,
previousAsset: "前のアセット", // Previous Asset,
nextAsset: "次のアセット", // Next Asset,
saveProject: "プロジェクトを保存", // Save Project,
exportProject: "プロジェクトをエクスポート", // Export Project,
activeLearning: "アクティブ ラーニング", // Active Learning"
},
videoPlayer: {
previousTaggedFrame: {
tooltip: "前のタグ付きフレーム", // Previous Tagged Frame"
},
nextTaggedFrame: {
tooltip: "次のタグ付きフレーム", // Next Tagged Frame"
},
previousExpectedFrame: {
tooltip: "前のフレーム", // Previous Frame"
},
nextExpectedFrame: {
tooltip: "次のフレーム", // Next Frame"
},
},
help: {
title: "ヘルプ メニューの切り替え", // Toggle Help Menu,
escape: "ヘルプメニューを抜ける", // Escape Help Menu"
},
assetError: "アセットを読み込めません", // Unable to load asset,
tags: {
hotKey: {
apply: "ホット キーでタグを適用", // Apply Tag with Hot Key,
lock: "ホット キーでタグをロック", // Lock Tag with Hot Key"
},
rename: {
title: "タグの名前を変更", // Rename Tag,
confirmation: "このタグの名前を変更してもいいですか",
// Are you sure you want to rename this tag? It will be renamed throughout all assets"
},
delete: {
title: "タグを削除", // Delete Tag,
confirmation: "このタグを削除してもいいですか。このタグはすべてのアセットで削除され、このタグのみのあらゆる領域も削除されます",
// Are you sure you want to delete this tag? It will be deleted throughout all assets
// and any regions where this is the only tag will also be deleted"
},
},
canvas: {
removeAllRegions: {
title: "すべてのリージョンを削除", // Remove All Regions,
confirmation: "すべてのリージョンを削除してもいいですか", // Are you sure you want to remove all regions"
},
},
messages: {
enforceTaggedRegions: {
title: "無効な領域が検出されました", // Invalid region(s) detected,
description: "1 つ以上の領域にタグが付けられていません。次のアセットに進む前に、すべての領域をタグ付けしてください。",
// 1 or more regions have not been tagged. Ensure all regions ar tagged before continuing to next asset"
},
},
},
export: {
title: "エクスポート", // Export,
settings: "エクスポート設定", // Export Settings,
saveSettings: "エクスポート設定を保存", // Save Export Settings,
providers: {
common: {
properties: {
assetState: {
title: "アセットの状態", // Asset State,
description: "エクスポートに含めるアセット", // Which assets to include in the export,
options: {
all: "すべてのアセット", // All Assets,
visited: "訪問済みのアセットのみ", // Only Visited Assets,
tagged: "タグ付きアセットのみ", // Only tagged Assets"
},
},
testTrainSplit: {
title: "テスト/トレーニング分割", // Test / Train Split,
description: "エクスポートされたデータに使用するテスト/トレーニングの分割",
// The test train split to use for exported data"
},
includeImages: {
title: "画像を含める", // Include Images,
description: "ターゲット接続にバイナリ画像アセットを含めるかどうか",
// Whether or not to include binary image assets in target connection"
},
},
},
vottJson: {
displayName: "VoTT JSON", // VoTT JSO"
},
azureCV: {
displayName: "Azure Custom Vision サービス", // Azure Custom Vision Service,
regions: {
australiaEast: "オーストラリア東部", // Australia East,
centralIndia: "インド中部", // Central India,
eastUs: "米国東部", // East US,
eastUs2: "米国東部 2", // East US 2,
japanEast: "東日本", // Japan East,
northCentralUs: "米国中北部", // North Central US,
northEurope: "北ヨーロッパ", // North Europe,
southCentralUs: "アメリカ中南部", // South Central US,
southeastAsia: "東南アジア", // Southeast Asia,
ukSouth: "英国南部", // UK South,
westUs2: "米国西部 2", // West US 2,
westEurope: "西ヨーロッパ", // West Europe"
},
properties: {
apiKey: {
title: "API キー", // API Key"
},
region: {
title: "領域", // Region,
description: "サービスがデプロイされている Azure リージョン", // The Azure region where your service is deployed"
},
classificationType: {
title: "分類タイプ", // Classification Type,
options: {
multiLabel: "画像ごとに複数のタグ", // Multiple tags per image,
multiClass: "画像ごとに単一のタグ", // Single tag per image"
},
},
name: {
title: "プロジェクト名", // Project Name"
},
description: {
title: "プロジェクトの説明", // Project Description"
},
domainId: {
title: "ドメイン", // Domain"
},
newOrExisting: {
title: "新規または既存プロジェクト", // New or Existing Project,
options: {
new: "新規プロジェクト", // New Project,
existing: "既存プロジェクト", // Existing Project"
},
},
projectId: {
title: "プロジェクト名", // Project Name"
},
projectType: {
title: "プロジェクトの種類", // Project Type,
options: {
classification: "分類", // Classification,
objectDetection: "物体検出", // Object Detection"
},
},
},
},
tfRecords: {
displayName: "TensorFlow レコード", // Tensorflow Record"
},
pascalVoc: {
displayName: "Pascal VOC", // Pascal VOC,
exportUnassigned: {
title: "未割り当てをエクスポート", // Export Unassigned,
description: "エクスポートされたデータに未割り当てのタグを含めるかどうか",
// Whether or not to include unassigned tags in exported data"
},
},
cntk: {
displayName: "Microsoft Cognitive ToolkitCNTK", // Microsoft Cognitive Toolkit (CNTK)"
},
csv: {
displayName: "コンマ区切り値(CSV", // Comma Separated Values (CSV)"
},
},
messages: {
saveSuccess: "エクスポート設定を保存しました", // Successfully saved export settings"
},
},
activeLearning: {
title: "アクティブ ラーニング", // Active Learning,
form: {
properties: {
modelPathType: {
title: "モデル プロバイダー", // Model Provider,
description: "トレーニング モデルのロード元", // Where to load the training model from,
options: {
preTrained: "事前トレーニング済みの Coco SSD", // Pre-trained Coco SSD,
customFilePath: "カスタム(ファイル パス)", // Custom (File path),
customWebUrl: "カスタム(URL", // Custom (Url)"
},
},
autoDetect: {
title: "自動検出", // Auto Detect,
description: "アセット間を移動するときに自動的に予測を行うかどうか",
// Whether or not to automatically make predictions as you navigate between assets"
},
modelPath: {
title: "モデル パス", // Model path,
description: "ローカル ファイル システムからモデルを選択します", // Select a model from your local file system"
},
modelUrl: {
title: "モデルURL", // Model URL,
description: "公開 Web URL からモデルを読み込む", // Load your model from a public web URL"
},
predictTag: {
title: "予測タグ", // Predict Tag,
description: "予測にタグを自動的に含めるかどうか", // Whether or not to automatically include tags in predictions"
},
},
},
messages: {
loadingModel: "アクティブ ラーニング モデルを読み込んでいます...", // Loading active learning model...,
errorLoadModel: "アクティブ ラーニング モデルの読み込みエラー", // Error loading active learning model,
saveSuccess: "アクティブ ラーニング設定を保存しました", // Successfully saved active learning settings"
},
},
profile: {
settings: "プロファイル設定", // Profile Settings"
},
errors: {
unknown: {
title: "不明なエラー", // Unknown Error,
message: "アプリで不明なエラーが発生しました。", // The app encountered an unknown error. Please try again"
},
projectUploadError: {
title: "ファイルのアップロード エラー", // Error Uploading File,
message: "ファイルのアップロード中にエラーが発生しました。",
// There was an error uploading the file. Please verify the file is of the correct format and try again."
},
genericRenderError: {
title: "アプリケーションの読み込みエラー", // Error Loading Application,
message: "アプリケーションのレンダリング中にエラーが発生しました。",
// An error occured while rendering the application. Please try again"
},
projectInvalidSecurityToken: {
title: "プロジェクト ファイルの読み込みエラー", // Error loading project file,
message: "プロジェクトが参照するセキュリティ トークンが無効です。",
// The security token referenced by the project is invalid.
// Verify that the security token for the project has been set correctly within your application settings"
},
projectInvalidJson: {
title: "プロジェクト ファイルの解析エラー", // Error parsing project file,
message: "選択したプロジェクト ファイルに有効なJSONが含まれていません。",
// The selected project files does not contain valid JSON Please check the file any try again."
},
projectDeleteError: {
title: "プロジェクトの削除エラー", // Error deleting project,
message: "プロジェクトの削除中にエラーが発生しました。",
// An error occured while deleting the project.
// Validate the project file an security token exist and try again"
},
securityTokenNotFound: {
title: "プロジェクト ファイルの読み込みエラー", // Error loading project file,
message: "プロジェクトが参照するセキュリティ トークンが現在のアプリケーション設定に見つかりません。",
// The security token referenced by the project cannot be found in your current application settings.
// Verify the security token exists and try to reload the project."
},
canvasError: {
title: "キャンバスの読み込みエラー", // Error loading canvas,
message: "キャンバスのロード中にエラーが発生しました。プロジェクトのアセットを確認して、再試行してください。",
// There was an error loading the canvas, check the project's assets and try again."
},
importError: {
title: "V1 プロジェクトのインポート エラー", // Error importing V1 project,
message: "V1 プロジェクトのインポート中にエラーが発生しました。",
// There was an error importing the V1 project. Check the project file and try again"
},
pasteRegionTooBigError: {
title: "領域の貼り付けエラー", // Error pasting region,
message: "このアセットに対して領域が大きすぎます。別のリージョンをコピーしてください",
// Region too big for this asset. Try copying another region"
},
exportFormatNotFound: {
title: "プロジェクトのエクスポート エラー", // Error exporting project,
message: "プロジェクトにエクスポート形式がありません。",
// Project is missing export format. Please select an export format in the export setting page."
},
activeLearningPredictionError: {
title: "アクティブ ラーニングのエラー", // Active Learning Error,
message: "現在のアセットの領域を予測中にエラーが発生しました。",
// An error occurred while predicting regions in the current asset.
// Please verify your active learning configuration and try again"
},
},
};
-556
Ver Arquivo
@@ -1,556 +0,0 @@
import { IAppStrings } from "../strings";
/**
* App Strings for Korean language from Google Translate
*/
export const korean: IAppStrings = {
appName: "비주얼 객체 태깅 도구", // Visual Object Tagging Tool,
common: {
displayName: "프로젝트 이름", // Display Name,
description: "설명", // Description,
submit: "제출", // Submit,
cancel: "취소", // Cancel,
save: "저장", // Save,
delete: "삭제", // Delete,
provider: "공급자", // Provider,
homePage: "홈페이지", // Home Page"
},
titleBar: {
help: "도움", // Help,
minimize: "최소화", // Minimize,
maximize: "최대화", // Maximize,
restore: "되돌리기", // Restore,
close: "닫기", // Close"
},
homePage: {
newProject: "새로운 프로젝트", // New Project,
openLocalProject: {
title: "로컬 프로젝트 열기", // Open Local Project"
},
openCloudProject: {
title: "클라우드 프로젝트 열기", // Open Cloud Project,
selectConnection: "Connection 선택", // Select a Connection
},
recentProjects: "최근 프로젝트", // Recent Projects,
deleteProject: {
title: "프로젝트 삭제", // Delete Project,
confirmation: "프로젝트를 삭제 하시겠습니까?", // Are you sure you want to delete project
},
importProject: {
title: "프로젝트 가져 오기", // Import Project,
confirmation: "${project.file.name} 프로젝트 설정을 v2 형식으로 수정 하시겠습니까? 수정하시기 전에 프로젝트 파일을 백업해두시기 바랍니다.",
// Are you sure you want to conver project ${project.file.name} project settings to v2 format?
// We recommend you backup the project file first."
},
messages: {
deleteSuccess: "${project.name}을 삭제했습니다", // Successfully deleted ${project.name}"
},
},
appSettings: {
title: "애플리케이션 설정", // Application Settings,
storageTitle: "저장소 설정", // Storage Settings,
uiHelp: "설정이 저장된 위치", // Where your settings are stored,
save: "설정 저장", // Save Settings,
securityToken: {
name: {
title: "이름", // Name
},
key: {
title: "키", // Key
},
},
securityTokens: {
title: "보안 토큰", // Security Tokens,
description: "보안 토큰은 프로젝트 구성 내에서 중요한 데이터를 암호화하는 데 사용됩니다",
// Security tokens are used to encryp sensitive data within your project configuration"
},
version: {
description: "버전:", // Version"
},
commit: "커밋 SHA", // Commit SHA,
devTools: {
description: "이슈 진단을 돕기 위한 개발자 도구 열기", // Open application developer tools to help diagnose issues,
button: "개발자 도구 전환", // Toggle Developer Tools
},
reload: {
description: "모든 변경사항을 버리고 애플리케이션을 재시작 합니다", // Reload the app discarding all current changes,
button: "애플리케이션 새로고침", // Refresh Application
},
messages: {
saveSuccess: "애플리케이션 설정이 성공적으로 저장되었습니다", // Successfully saved application settings
},
},
projectSettings: {
title: "프로젝트 설정", // Project Settings,
securityToken: {
title: "보안 토큰", // Security Token,
description: "프로젝트 파일 내에서 중요한 데이터를 암호화하는 데 사용", // Used to encrypt sensitive data within project file
},
useSecurityToken: {
title: "보안 토큰 사용", // Use Security Token
description: "활성화되면 공급자 구성 내에서 중요한 데이터를 암호화합니다.",
// When enabled will encrypt sensitive data within provider configuration
},
save: "프로젝트 저장", // Save Project,
sourceConnection: {
title: "소스 연결", // Source Connection,
description: "Asset 저장 경로", // Where to load assets from
},
targetConnection: {
title: "대상 연결", // Target Connection,
description: "프로젝트 및 내 보낸 데이터를 저장할 위치", // Where to save the project and exported data
},
videoSettings: {
title: "비디오 설정", // Video Settings,
description: "태그 지정을 위해 프레임을 추출하는 비율", // The rate at which frames are extracted for tagging
frameExtractionRate: "프레임 추출 속도 (비디오 초당 프레임)", // Frame Extraction Rate (frames per a video second)
},
addConnection: "연결 추가", // Add Connection,
messages: {
saveSuccess: "${project.name} 프로젝트 설정을 성공적으로 저장했습니다", // Successfully saved ${project.name} project settings
},
},
projectMetrics: {
title: "프로젝트 매트릭", // Project Metrics,
assetsSectionTitle: "Asset", // Assets,
totalAssetCount: "총 Asset", // Total Assets,
visitedAssets: "검토한 Asset (${count})", // Visited Assets (${count}),
taggedAssets: "태그된 Asset (${count})", // Tagged Assets (${count}),
nonTaggedAssets: "태그가 없는 Asset (${count})", // Not Tagged Assets (${count}),
nonVisitedAssets: "검토하지 않은 Asset (${count})", // Not Visited Assets (${count}),
tagsSectionTitle: "태그", // Tags & Labels,
totalRegionCount: "태그된 지역 수", // Total Tagged Regions,
totalTagCount: "태그 숫자", // Total Tags,
avgTagCountPerAsset: "Asset 당 평균 태그 숫자", // Average tags per asset"
},
tags: {
title: "태그", // Tags,
placeholder: "새 태그 추가", // Add new tag,
editor: "태그 편집기", // Tags Editor,
modal: {
name: "태그 이름", // Tag Name,
color: "태그 색상", // Tag Color"
},
colors: {
white: "하얀색", // White,
gray: "회색", // Gray,
red: "빨간색", // Red,
maroon: "밤색", // Maroon,
yellow: "노랑색", // Yellow,
olive: "올리브색", // Olive,
lime: "라임색", // Lime,
green: "초록색", // Green,
aqua: "아쿠아", // Aqua,
teal: "물오리", // Teal,
blue: "파랑색", // Blue,
navy: "군청색", // Navy,
fuschia: "푸시아", // Fuschia,
purple: "보라색", // Purple"
},
warnings: {
existingName: "태그 이름이 이미 존재합니다. 다른 이름을 입력하십시오", // Tag name already exists. Choose another name,
emptyName: "빈 태그 이름을 가질 수 없습니다", // Cannot have an empty tag name,
unknownTagName: "알 수 없는 태그 이름", // Unknown"
},
toolbar: {
add: "새 태그 추가", // Add new tag,
search: "태크 검색", // Search tags,
edit: "태그 편집", // Edit tag,
lock: "태그 잠금", // Lock tag,
moveUp: "태그를 위로 이동", // Move tag up,
moveDown: "태그를 아래로 이동", // Move tag down,
delete: "태그 삭제", // Delete tag"
},
},
connections: {
title: "연결 설정", // Connections,
details: "설명", // Connection Details,
settings: "설정", // Connection Settings,
instructions: "편집 할 연결 정보를 선택하십시오", // Please select a connection to edit,
save: "저장", // Save Connection,
messages: {
saveSuccess: "${connection.name}을 성공적으로 저장했습니다", // Successfully saved ${connection.name},
deleteSuccess: "${connection.name}을 삭제했습니다.", // Successfully deleted ${connection.name}"
},
imageCorsWarning: "경고 : 웹 브라우저에서 VoTT를 사용하는 경우 CORS (Cross Origin Resource Sharing) " +
"제한으로 인해 Bing Image Search의 일부 정보가 제대로 내보내지지 않을 수 있습니다.",
// Warning: When using VoTT in a Web browser, some assets from Bing Image Search may no export
// correctly due to CORS (Cross Origin Resource Sharing) restrictions.",
blobCorsWarning: "경고 : 소스 또는 대상 연결로 사용하려면, Azure Blob Storage 계정에서 CORS(Cross Domain Resource Sharing) " +
"설정을 활성화 해야 합니다. CORS 설정에 대한 자세한 정보는 {0}에서 찾을 수 있습니다.",
// Warning: CORS (Cross Domain Resource Sharing) must be enabled on the Azure Blob Storage account, in order
// to use i as a source or target connection. More information on enabling CORS can be found in the {0}",
azDocLinkText: "Azure 설명서.", // Azure Documentation.,
providers: {
azureBlob: {
title: "Azure Blob 저장소", // Azure Blob Storage,
description: "",
accountName: {
title: "계정 이름", // Account Name,
description: "",
},
containerName: {
title: "컨테이너 이름", // Container Name,
description: "",
},
sas: {
title: "SAS", // SAS,
description: "Blob Storage 계정을 인증하는 데 사용되는 공유 액세스 서명",
// Shared access signature used to authenticate to the blob storage account"
},
createContainer: {
title: "컨테이너 만들기", // Create Container,
description: "Blob 컨테이너가 없으면 새로 생성합니다.",
// Creates the blob container if it does not already exist"
},
},
bing: {
title: "Bing 이미지 검색", // Bing Image Search,
options: {
title: "Bing 이미지 검색 옵션",
}, // Bing Image Search Options,
endpoint: {
title: "끝점",
description: "Bing 검색 Azure 리소스 내에 나열된 끝점",
},
apiKey: {
title: "API 키",
description: "Bing 검색 Azure 리소스 내에 나열된 API 키",
}, // API Key,
query: {
title: "쿼리",
description: "연결을 채우는 데 사용되는 검색 쿼리",
}, // Query,
aspectRatio: {
title: "종횡비", // Aspect Ratio,
description: "지정된 종횡비로 결과를 필터링합니다.",
options: {
all: "모두", // All,
square: "정사각형", // Square,
wide: "넓은", // Wide,
tall: "긴", // Tall"
},
},
licenseType: {
title: "라이센스 유형",
description: "지정된 라이센스 유형으로 결과 필터링",
options: {
all: "모든 (이미지를 필터링하지 않음)",
any: "라이센스 유형이 있는 모든 이미지",
public: "퍼블릭 도메인",
share: "무료 공유 및 사용",
shareCommercially: "상업적으로 자유롭게 공유하고 사용할 수 있습니다.",
modify: "자유롭게 수정, 공유 및 사용",
modifyCommercially: "상업적으로 자유롭게 수정, 공유 및 사용",
},
},
size: {
title: "크기",
description: "지정된 크기로 결과를 필터링합니다.",
options: {
all: "모든",
small: "스몰(200x200 미만)",
medium: "중간(500x500 미만)",
large: "대형(500x500 이상)",
wallpaper: "배경 화면 (초대형 이미지)",
},
},
},
local: {
title: "로컬 파일 시스템", // Local File System,
folderPath: "경로", // Folder Path,
selectFolder: "폴더 선택", // Select Folder,
chooseFolder: "선택", // Choose Folder"
},
},
},
editorPage: {
width: "너비", // Width,
height: "높이", // Height,
tagged: "태그", // Tagged,
visited: "방문", // Visited,
toolbar: {
select: "선택 (V)", // Select (V),
pan: "팬", // Pan,
drawRectangle: "사각형 그리기", // Draw Rectangle,
drawPolygon: "다각형 그리기", // Draw Polygon,
copyRectangle: "사각형 복사", // Copy Rectangle,
copy: "영역 복사", // Copy Regions,
cut: "영역 잘라내기", // Cut Regions,
paste: "영역 붙여 넣기", // Paste Regions,
removeAllRegions: "모든 지역 제거", // Remove All Regions,
previousAsset: "이전 Asset", // Previous Asset,
nextAsset: "다음 Asset", // Next Asset,
saveProject: "프로젝트 저장", // Save Project,
exportProject: "프로젝트 내보내기", // Export Project,
activeLearning: "Active Learning", // Active Learning"
},
videoPlayer: {
previousTaggedFrame: {
tooltip: "이전 태그 된 프레임", // Previous Tagged Frame"
},
nextTaggedFrame: {
tooltip: "다음 태그 된 프레임", // Next Tagged Frame"
},
previousExpectedFrame: {
tooltip: "이전 프레임", // Previous Frame"
},
nextExpectedFrame: {
tooltip: "다음 프레임", // Next Frame"
},
},
help: {
title: "도움말", // Toggle Help Menu,
escape: "나가기", // Escape Help Menu"
},
assetError: "Asset을 불러올 수 없습니다", // Unable to load asset,
tags: {
hotKey: {
apply: "단축키로 태그 적용", // Apply Tag with Hot Key,
lock: "단축키가 있는 태그 잠금", // Lock Tag with Hot Key"
},
rename: {
title: "태그 이름 바꾸기", // Rename Tag,
confirmation: "이 태그의 이름을 바꾸시겠습니까? 모든 Asset에서 이름이 변경됩니다",
// Are you sure you want to rename this tag? It will be renamed throughout all assets"
},
delete: {
title: "태그 삭제", // Delete Tag,
confirmation: "이 태그를 삭제 하시겠습니까? 모든 Asset 및 태그가 유일한 지역 인 모든 지역에서 삭제됩니다.",
// Are you sure you want to delete this tag? It will be deleted throughout all assets
// and any regions where this is the only tag will also be deleted"
},
},
canvas: {
removeAllRegions: {
title: "모든 지역 제거", // Remove All Regions,
confirmation: "모든 지역을 삭제 하시겠습니까?", // Are you sure you want to remove all regions"
},
},
messages: {
enforceTaggedRegions: {
title: "유효하지 않은 지역이 감지되었습니다.", // Invalid region(s) detected,
description: "1 개 이상의 지역이 태그되어야 합니다. 다음 작업을 계속 진행하기 위해 모든 지역에 태그가 지정되어 있는지 확인하십시오.",
// 1 or more regions have not been tagged. Ensure all regions ar tagged before continuing to next asset"
},
},
},
export: {
title: "내보내기", // Export,
settings: "내보내기 설정", // Export Settings,
saveSettings: "내보내기 설정 저장", // Save Export Settings,
providers: {
common: {
properties: {
assetState: {
title: "Asset 상태", // Asset State,
description: "내보내기에 포함 할 Asset", // Which assets to include in the export,
options: {
all: "모든 Asset", // All Assets,
visited: "방문한 Asset만", // Only Visited Assets,
tagged: "태그된 Asset만", // Only tagged Assets"
},
},
testTrainSplit: {
title: "테스트용 / 학습용 분할", // Test / Train Split,
description: "내보내는 데이터에 테스트용 / 학습용 분할",
// The test train split to use for exported data"
},
includeImages: {
title: "이미지 포함", // Include Images,
description: "대상 연결에 이진 이미지 Asset을 포함할지 여부",
// Whether or not to include binary image assets in target connection"
},
},
},
vottJson: {
displayName: "VoTT JSON", // VoTT JSON
},
azureCV: {
displayName: "Azure Custom Vision 서비스", // Azure Custom Vision Service,
regions: {
australiaEast: "호주 동부", // Australia East,
centralIndia: "중앙 인도", // Central India,
eastUs: "미국 동부", // East US,
eastUs2: "미국 동부 2", // East US 2,
japanEast: "일본 동부", // Japan East,
northCentralUs: "미국 중북부", // North Central US,
northEurope: "북유럽", // North Europe,
southCentralUs: "미국 중남부", // South Central US,
southeastAsia: "동남아시아", // Southeast Asia,
ukSouth: "영국 남부", // UK South,
westUs2: "미국 서부 2", // West US 2,
westEurope: "서유럽", // West Europe"
},
properties: {
apiKey: {
title: "API 키", // API Key"
},
region: {
title: "지역", // Region,
description: "서비스가 배포 된 Azure 지역", // The Azure region where your service is deployed"
},
classificationType: {
title: "분류 유형", // Classification Type,
options: {
multiLabel: "이미지 당 여러 태그", // Multiple tags per image,
multiClass: "이미지 당 단일 태그", // Single tag per image"
},
},
name: {
title: "프로젝트 이름", // Project Name"
},
description: {
title: "설명", // Project Description"
},
domainId: {
title: "도메인", // Domain"
},
newOrExisting: {
title: "신규 또는 기존 프로젝트", // New or Existing Project,
options: {
new: "새로운 프로젝트", // New Project,
existing: "기존 프로젝트", // Existing Project"
},
},
projectId: {
title: "프로젝트 이름", // Project Name"
},
projectType: {
title: "프로젝트 유형", // Project Type,
options: {
classification: "분류", // Classification,
objectDetection: "물체 감지", // Object Detection"
},
},
},
},
tfRecords: {
displayName: "TensorFlow 기록", // Tensorflow Record"
},
pascalVoc: {
displayName: "Pascal VOC", // Pascal VOC,
exportUnassigned: {
title: "할당되지 않은 태그 내보내기", // Export Unassigned,
description: "내보내는 데이터에 할당되지 않은 태그를 포함할지 여부",
// Whether or not to include unassigned tags in exported data"
},
},
cntk: {
displayName: "Microsoft Cognitive ToolkitCNTK", // Microsoft Cognitive Toolkit (CNTK)"
},
csv: {
displayName: "쉼표로 구분 된 값(CSV", // Comma Separated Values (CSV)"
},
},
messages: {
saveSuccess: "내보내기 설정이 성공적으로 저장되었습니다", // Successfully saved export settings"
},
},
activeLearning: {
title: "Active Learning", // Active Learning,
form: {
properties: {
modelPathType: {
title: "모델 제공자", // Model Provider,
description: "학습 모델을 불러올 위치", // Where to load the training model from,
options: {
preTrained: "미리 학습된 Coco SSD", // Pre-trained Coco SSD,
customFilePath: "사용자 정의 (파일 경로)", // Custom (File path),
customWebUrl: "사용자 정의 (URL)", // Custom (Url)"
},
},
autoDetect: {
title: "자동 감지", // Auto Detect,
description: "Asset 간을 탐색 할 때 자동 예측 여부",
// Whether or not to automatically make predictions as you navigate between assets"
},
modelPath: {
title: "모델 경로", // Model path,
description: "로컬 파일 시스템에서 모델을 선택하십시오.", // Select a model from your local file system"
},
modelUrl: {
title: "모델 URL", // Model URL,
description: "URL에서 모델 불러오기", // Load your model from a public web URL"
},
predictTag: {
title: "태그 예측", // Predict Tag,
description: "예측에 태그를 자동으로 포함할지 여부", // Whether or not to automatically include tags in predictions"
},
},
},
messages: {
loadingModel: "Active Learning 모델 불러오는 중 ...", // Loading active learning model...,
errorLoadModel: "Active Learning 모델을 불러오는 중 오류가 발생했습니다", // Error loading active learning model,
saveSuccess: "Active Learning 모델 설정을 성공적으로 저장했습니다", // Successfully saved active learning settings"
},
},
profile: {
settings: "프로필 설정", // Profile Settings"
},
errors: {
unknown: {
title: "알 수 없는 오류", // Unknown Error,
message: "애플리케이션에 알 수 없는 오류가 발생했습니다. 다시 시도하십시오.", // The app encountered an unknown error. Please try again"
},
projectUploadError: {
title: "파일 업로드 오류", // Error Uploading File,
message: "파일을 업로드하는 중에 오류가 발생했습니다. 파일이 올바른 형식인지 확인한 후 다시 시도하십시오.",
// There was an error uploading the file. Please verify the file is of the correct format and try again."
},
genericRenderError: {
title: "응용 프로그램 로딩 오류", // Error Loading Application,
message: "응용 프로그램을 렌더링하는 중에 오류가 발생했습니다. 다시 시도하십시오",
// An error occured while rendering the application. Please try again"
},
projectInvalidSecurityToken: {
title: "프로젝트 파일을 로드하는 중 오류가 발생했습니다", // Error loading project file,
message: "프로젝트에서 참조한 보안 토큰이 유효하지 않습니다.응용 프로그램 설정 내에서 프로젝트의 보안 토큰이 올바르게 설정되었는지 확인하십시오",
// The security token referenced by the project is invalid.
// Verify that the security token for the project has been set correctly within your application settings"
},
projectInvalidJson: {
title: "프로젝트 파일 파싱 오류", // Error parsing project file,
message: "선택한 프로젝트 파일에 유효한 JSON이 포함되어 있지 않습니다. 파일을 다시 확인하십시오.",
// The selected project files does not contain valid JSON Please check the file any try again."
},
projectDeleteError: {
title: "프로젝트 삭제 오류", // Error deleting project,
message: "프로젝트를 삭제하는 중에 오류가 발생했습니다. 프로젝트 파일에 보안 토큰이 존재하는지 확인한 후 다시 시도하십시오.",
// An error occured while deleting the project.
// Validate the project file an security token exist and try again"
},
securityTokenNotFound: {
title: "프로젝트 파일을 로드하는 중 오류가 발생했습니다", // Error loading project file,
message: "프로젝트가 참조하는 보안 토큰을 현재 애플리케이션 설정에서 찾을 수 없습니다. 보안 토큰이 있는지 확인하고 프로젝트를 다시로드하십시오.",
// The security token referenced by the project cannot be found in your current application settings.
// Verify the security token exists and try to reload the project."
},
canvasError: {
title: "캔버스 불러 오기 오류", // Error loading canvas,
message: "캔버스를 로드하는 중에 오류가 발생했습니다. 프로젝트 Asset을 확인한 후 다시 시도하십시오.",
// There was an error loading the canvas, check the project's assets and try again."
},
importError: {
title: "V1 프로젝트 가져 오기 오류", // Error importing V1 project,
message: "V1 프로젝트를 가져 오는 중에 오류가 발생했습니다. 프로젝트 파일을 확인하고 다시 시도하십시오.",
// There was an error importing the V1 project. Check the project file and try again"
},
pasteRegionTooBigError: {
title: "지역 붙여 넣기 오류", // Error pasting region,
message: "이 Asset에 비해 지역이 너무 큽니다. 다른 지역을 복사 해보십시오.",
// Region too big for this asset. Try copying another region"
},
exportFormatNotFound: {
title: "프로젝트 내보내기 오류", // Error exporting project,
message: "프로젝트에 내보내기 형식이 없습니다. 내보내기 설정 페이지에서 내보내기 형식을 선택하십시오.",
// Project is missing export format. Please select an export format in the export setting page."
},
activeLearningPredictionError: {
title: "Active Learning 오류", // Active Learning Error,
message: "현재 Asset의 지역을 예측하는 동안 오류가 발생했습니다. Active Learning 구성을 확인하고 다시 시도하십시오",
// An error occurred while predicting regions in the current asset.
// Please verify your active learning configuration and try again"
},
},
};
-553
Ver Arquivo
@@ -1,553 +0,0 @@
import { IAppStrings } from "../strings";
/**
* App Strings for Simplified Chinese (zh-ch)
*/
export const chinese: IAppStrings = {
appName: "视觉对象标记工具", // Visual Object Tagging Tool
common: {
displayName: "显示名称", // Display Name
description: "描述", // Description
submit: "提交", // Submit
cancel: "取消", // Cancel
save: "保存", // Save
delete: "删除", // Delete
provider: "提供者", // Provider
homePage: "主页", // Home Page
},
titleBar: {
help: "帮助", // Help
minimize: "最小化", // Minimize
maximize: "最大化", // Maximize
restore: "复原", // Restore
close: "关闭", // Close
},
homePage: {
newProject: "新项目", // New Project
openLocalProject: {
title: "打开本地项目", // Open Local Project
},
openCloudProject: {
title: "打开云端项目", // Open Cloud Project
selectConnection: "选择一个连接", // Select a Connection
},
recentProjects: "最近的项目", // Recent Projects
deleteProject: {
title: "删除项目", // Delete Project
confirmation: "确定要删除项目吗", // Are you sure you want to delete project
},
importProject: {
title: "导入项目", // Import Project
confirmation: "您确定要将项目${project.file.name}设置转换为v2格式吗?我们建议您首先备份项目文件。",
// Are you sure you want to convert project ${project.file.name} project settings to v2 format?
// We recommend you backup the project file first.
},
messages: {
deleteSuccess: "已成功删除${project.name}", // Successfully deleted ${project.name}
},
},
appSettings: {
title: "应用程序设置", // Application Settings
storageTitle: "储存设置", // Storage Settings
uiHelp: "您的设置储存在哪里", // Where your settings are stored
save: "保存设置", // Save Settings
securityToken: {
name: {
title: "名称", // Name
},
key: {
title: "密钥", // Key
},
},
securityTokens: {
title: "安全令牌", // Security Tokens
description: "安全令牌用于加密项目配置中的敏感数据",
// Security tokens are used to encrypt sensitive data within your project configuration
},
version: {
description: "版本:", // Version:
},
commit: "提交SHA", // Commit SHA
devTools: {
description: "打开应用程序开发者工具以帮助诊断问题", // Open application developer tools to help diagnose issues
button: "切换至开发者工具", // Toggle Developer Tools
},
reload: {
description: "重新加载应用,放弃当前所有更改", // Reload the app discarding all current changes
button: "刷新应用", // Refresh Application
},
messages: {
saveSuccess: "成功保存应用程序设置", // Successfully saved application settings
},
},
projectSettings: {
title: "项目设定", // Project Settings
securityToken: {
title: "安全令牌", // Security Token
description: "用于加密项目文件中的敏感数据", // Used to encrypt sensitive data within project files
},
useSecurityToken: {
title: "使用安全令牌", // Use Security Token
description: "启用后将在提供者配置内加密敏感数据",
// When enabled will encrypt sensitive data within provider configuration
},
save: "保存项目", // Save Project
sourceConnection: {
title: "源连接", // Source Connection
description: "从何处加载素材", // Where to load assets from
},
targetConnection: {
title: "目标连接", // Target Connection
description: "在哪里保存项目和导出数据", // Where to save the project and exported data
},
videoSettings: {
title: "视频设定", // Video Settings
description: "提取帧以进行标记的速率", // The rate at which frames are extracted for tagging.
frameExtractionRate: "帧提取率(每视频每秒的帧数)", // Frame Extraction Rate (frames per a video second)
},
addConnection: "添加连接", // Add Connection
messages: {
saveSuccess: "成功保存${project.name}项目设置", // Successfully saved ${project.name} project settings
},
},
projectMetrics: {
title: "项目指标", // Project Metrics
assetsSectionTitle: "素材", // Assets
totalAssetCount: "素材总数", // Total Assets
visitedAssets: "已访问素材(${count}", // Visited Assets (${count})
taggedAssets: "已标记素材(${count}", // Tagged Assets (${count})
nonTaggedAssets: "未标记素材(${count}", // Not Tagged Assets (${count})
nonVisitedAssets: "未访问素材(${count}", // Not Visited Assets (${count})
tagsSectionTitle: "标记与标签", // Tags & Labels
totalRegionCount: "标记区域总数", // Total Tagged Regions
totalTagCount: "标签总数", // Total Tags
avgTagCountPerAsset: "每个素材的平均标签数", // Average tags per asset
},
tags: {
title: "标签", // Tags
placeholder: "添加标签", // Add new tag
editor: "编辑标签", // Tags Editor
modal: {
name: "标签名称", // Tag Name
color: "标签颜色", // Tag Color
},
colors: {
white: "白色", // White
gray: "灰色", // Gray
red: "红色", // Red
maroon: "栗色", // Maroon
yellow: "黄色", // Yellow
olive: "橄榄色", // Olive
lime: "青色", // Lime
green: "绿色", // Green
aqua: "浅绿色", // Aqua
teal: "蓝绿色", // Teal
blue: "蓝色", // Blue
navy: "海军蓝色", // Navy
fuschia: "紫红色", // Fuschia
purple: "紫色", // Purple
},
warnings: {
existingName: "标签名称已存在。请选择另一个名字", // Tag name already exists. Choose another name
emptyName: "标签名称不能为空", // Cannot have an empty tag name
unknownTagName: "未知", // Unknown
},
toolbar: {
add: "添加标签", // Add new tag
search: "搜索标签", // Search tags
edit: "编辑标签", // Edit tag
lock: "锁定标签", // Lock tag
moveUp: "向上移动标签", // Move tag up
moveDown: "向下移动标签", // Move tag down
delete: "删除标签", // Delete tag
},
},
connections: {
title: "连接数", // Connections
details: "连接详细信息", // Connection Details
settings: "连接设定", // Connection Settings
instructions: "请选择一个连接进行编辑", // Please select a connection to edit
save: "保存连接", // Save Connection
messages: {
saveSuccess: "已成功保存${connection.name}", // Successfully saved ${connection.name}
deleteSuccess: "已成功删除${connection.name}", // Successfully deleted ${connection.name}
},
imageCorsWarning: "警告:在Web浏览器中使用VoTT时,由于CORS(跨源资源共享)限制,来自Bing Image Search的某些素材可能无法正确导出。",
// Warning: When using VoTT in a Web browser, some assets from Bing Image Search may not export correctly
// due to CORS (Cross Origin Resource Sharing) restrictions.
blobCorsWarning: "警告:必须在Azure Blob存储帐户上启用CORS(跨域资源共享),才能将其用作源或目标连接。 {0}中提供了有关启用CORS的更多信息。",
// Warning: CORS (Cross Domain Resource Sharing) must be enabled on the Azure Blob Storage account,
// in order to use it as a source or target connection.
// More information on enabling CORS can be found in the {0}
azDocLinkText: "Azure文档", // Azure Documentation.
providers: {
azureBlob: {
title: "Azure Blob存储", // Azure Blob Storage
description: "",
accountName: {
title: "用户名", // Account Name
description: "",
},
containerName: {
title: "容器名称", // Container Name
description: "",
},
sas: {
title: "SAS", // SAS
description: "用于验证Blob存储帐户的共享访问签名",
// Shared access signature used to authenticate to the blob storage account
},
createContainer: {
title: "创建容器", // Create Container
description: "创建blob容器(如果尚不存在)",
// Creates the blob container if it does not already exist
},
},
bing: {
title: "必应图片搜索", // Bing Image Search
options: {
title: "必应图像搜索选项",
}, // Bing Image Search Options
endpoint: {
title: "端点",
description: "必应搜索 Azure 资源中列出的终结点",
},
apiKey: {
title: "API密钥",
description: "必应搜索 Azure 资源中列出的 API 密钥",
}, // API Key
query: {
title: "查询",
description: "用于填充连接的搜索查询",
}, // Query
aspectRatio: {
title: "长宽比", // Aspect Ratio
description: "按指定的纵横比筛选结果",
options: {
all: "所有", // All
square: "正方形", // Square
wide: "宽", // Wide
tall: "高", // Tall
},
},
licenseType: {
title: "许可证类型",
description: "按指定的许可证类型筛选结果",
options: {
all: "全部(不过滤任何图像)",
any: "任何许可证类型的图像",
public: "公有领域",
share: "免费分享和使用",
shareCommercially: "免费共享和使用商业",
modify: "免费修改、共享和使用",
modifyCommercially: "可自由修改、共享和在商业上使用",
},
},
size: {
title: "大小",
description: "按指定大小筛选结果",
options: {
all: "所有",
small: "小(小于200x200",
medium: "中等(小于 500x500",
large: "大(大于 500x500",
wallpaper: "壁纸(超大图像)",
},
},
},
local: {
title: "本地文件系统", // Local File System
folderPath: "文件夹路径", // Folder Path
selectFolder: "选择文件夹", // Select Folder
chooseFolder: "选择文件夹", // Choose Folder
},
},
},
editorPage: {
width: "宽度", // Width
height: "高度", // Height
tagged: "已标记", // Tagged
visited: "已访问", // Visited
toolbar: {
select: "选择[V]", // Select (V)
pan: "泛", // Pan
drawRectangle: "绘制矩形", // Draw Rectangle
drawPolygon: "绘制多边形", // Draw Polygon
copyRectangle: "复制矩形", // Copy Rectangle
copy: "复制区域", // Copy Regions
cut: "剪切区域", // Cut Regions
paste: "粘贴区域", // Paste Regions
removeAllRegions: "删除所有区域", // Remove All Regions
previousAsset: "以前的素材", // Previous Asset
nextAsset: "下一项素材", // Next Asset
saveProject: "保存项目", // Save Project
exportProject: "导出项目", // Export Project
activeLearning: "主动学习", // Active Learning
},
videoPlayer: {
previousTaggedFrame: {
tooltip: "上一个已标记的帧", // Previous Tagged Frame
},
nextTaggedFrame: {
tooltip: "下一个已标记的帧", // Next Tagged Frame
},
previousExpectedFrame: {
tooltip: "上一帧", // Previous Frame
},
nextExpectedFrame: {
tooltip: "下一帧", // Next Frame
},
},
help: {
title: "切换帮助菜单", // Toggle Help Menu
escape: "退出帮助菜单", // Escape Help Menu
},
assetError: "无法加载素材", // Unable to load asset
tags: {
hotKey: {
apply: "使用快捷键应用标签", // Apply Tag with Hot Key
lock: "使用快捷键锁定标签", // Lock Tag with Hot Key
},
rename: {
title: "重新命名标签", // Rename Tag
confirmation: "您确定要重新命名此标签吗?它将在所有素材中被重新命名",
// Are you sure you want to rename this tag? It will be renamed throughout all assets
},
delete: {
title: "删除标签", // Delete Tag
confirmation: "您确定要删除此标签吗?它将在所有素材中被删除,并且仅使用此标签标记的任何区域也将被删除",
// Are you sure you want to delete this tag? It will be deleted throughout all assets
// and any regions where this is the only tag will also be deleted
},
},
canvas: {
removeAllRegions: {
title: "删除所有区域", // Remove All Regions
confirmation: "您确定要删除所有区域吗?", // Are you sure you want to remove all regions?
},
},
messages: {
enforceTaggedRegions: {
title: "检测到无效的区域", // Invalid region(s) detected
description: "1个或多个区域尚未被标记。在继续下一个素材之前,请确保所有区域均已标记。",
// 1 or more regions have not been tagged.
// Ensure all regions are tagged before continuing to next asset.
},
},
},
export: {
title: "导出", // Export
settings: "导出设置", // Export Settings
saveSettings: "保存导出设置", // Save Export Settings
providers: {
common: {
properties: {
assetState: {
title: "素材状态", // Asset State
description: "导出中包括哪些素材", // Which assets to include in the export
options: {
all: "所有素材", // All Assets
visited: "仅已访问素材", // Only Visited Assets
tagged: "仅已标记素材", // Only tagged Assets
},
},
testTrainSplit: {
title: "测试/训练用数据分离", // Test / Train Split
description: "导出时分离测试/训练用数据", // The test train split to use for exported data
},
includeImages: {
title: "包含图片", // Include Images
description: "是否在目标连接中包括二值化图像素材",
// Whether or not to include binary image assets in target connection
},
},
},
vottJson: {
displayName: "VoTT JSON", // VoTT JSON
},
azureCV: {
displayName: "Azure自定义视觉服务", // Azure Custom Vision Service
regions: {
australiaEast: "澳大利亚东部", // Australia East
centralIndia: "印度中部", // Central India
eastUs: "美国东部", // East US
eastUs2: "美国东部2", // East US 2
japanEast: "日本东部", // Japan East
northCentralUs: "美国中北部", // North Central US
northEurope: "欧州北部", // North Europe
southCentralUs: "美国中南部", // South Central US
southeastAsia: "东南亚", // Southeast Asia
ukSouth: "英国南部", // UK South
westUs2: "美国西部2", // West US 2
westEurope: "欧州西部", // West Europe
},
properties: {
apiKey: {
title: "API密钥", // API Key
},
region: {
title: "区域", // Region
description: "部署服务的Azure区域", // The Azure region where your service is deployed
},
classificationType: {
title: "分类类型", // Classification Type
options: {
multiLabel: "每个图像多个标签", // Multiple tags per image
multiClass: "每个图像一个标签", // Single tag per image
},
},
name: {
title: "项目名", // Project Name
},
description: {
title: "项目简介", // Project Description
},
domainId: {
title: "域", // Domain
},
newOrExisting: {
title: "新项目或现有项目", // New or Existing Project
options: {
new: "新项目", // New Project
existing: "现有项目", // Existing Project
},
},
projectId: {
title: "项目名", // Project Name
},
projectType: {
title: "项目类型", // Project Type
options: {
classification: "分类", // Classification
objectDetection: "物体识别", // Object Detection
},
},
},
},
tfRecords: {
displayName: "Tensorflow记录", // Tensorflow Records
},
pascalVoc: {
displayName: "Pascal VOC", // Pascal VOC
exportUnassigned: {
title: "导出未分配", // Export Unassigned
description: "是否在导出的数据中包括未被分配的标签", // Whether or not to include unassigned tags in exported data
},
},
cntk: {
displayName: "Microsoft Cognitive ToolkitCNTK)", // Microsoft Cognitive Toolkit (CNTK)
},
csv: {
displayName: "逗号分隔值 (CSV)", // Comma Separated Values (CSV)
},
},
messages: {
saveSuccess: "成功保存导出设置", // Successfully saved export settings
},
},
activeLearning: {
title: "主动学习", // Active Learning
form: {
properties: {
modelPathType: {
title: "模型提供者", // Model Provider
description: "从何处加载训练模型", // Where to load the training model from
options: {
preTrained: "预先训练 Coco SSD", // Pre-trained Coco SSD
customFilePath: "自定义(文件路径)", // Custom (File path)
customWebUrl: "自定义 (URL)", // Custom (Url)
},
},
autoDetect: {
title: "自动识别", // Auto Detect
description: "在素材之间导航时是否自动进行预测",
// Whether or not to automatically make predictions as you navigate between assets
},
modelPath: {
title: "模型路径", // Model path
description: "从本地文件系统中选择模型", // Select a model from your local file system
},
modelUrl: {
title: "模型 URL", // Model URL
description: "从公共网址加载模型", // Load your model from a public web URL
},
predictTag: {
title: "预测标签", // Predict Tag
description: "是否在预测中自动包含标签", // Whether or not to automatically include tags in predictions
},
},
},
messages: {
loadingModel: "正在加载主动学习模型...", // Loading active learning model...
errorLoadModel: "加载主动学习模型时出错", // Error loading active learning model
saveSuccess: "成功保存了主动学习设置", // Successfully saved active learning settings
},
},
profile: {
settings: "个人资料设置", // Profile Settings
},
errors: {
unknown: {
title: "未知错误", // Unknown Error
message: "该应用程序遇到未知错误。请重试。", // The app encountered an unknown error. Please try again.
},
projectUploadError: {
title: "上传文件时出错", // Error Uploading File
message: "上传文件时出错。请确认文件格式正确,然后重试。",
// There was an error uploading the file. Please verify the file is of the correct format and try again.
},
genericRenderError: {
title: "加载应用程序时出错", // Error Loading Application
message: "呈现应用程序时发生错误。请重试",
// An error occured while rendering the application. Please try again
},
projectInvalidSecurityToken: {
title: "加载项目文件时出错", // Error loading project file
message: "项目引用的安全令牌无效。请验证是否在您的应用程序设置中正确设置了项目的安全令牌",
// The security token referenced by the project is invalid.
// Verify that the security token for the project has been set correctly within your application settings
},
projectInvalidJson: {
title: "解析项目文件时出错", // Error parsing project file
message: "所选的项目文件不包含有效的JSON。请检查该文件, 然后重试。",
// The selected project files does not contain valid JSON. Please check the file any try again.
},
projectDeleteError: {
title: "删除项目时出错", // Error deleting project
message: "删除项目时发生错误。验证项目文件和安全令牌是否存在,然后重试",
// An error occured while deleting the project.
// Validate the project file and security token exist and try again
},
securityTokenNotFound: {
title: "加载项目文件时出错", // Error loading project file
message: "在当前的应用程序设置中找不到该项目引用的安全令牌。验证安全令牌是否存在,然后尝试重新加载项目。",
// The security token referenced by the project cannot be found in your current application settings.
// Verify the security token exists and try to reload the project.
},
canvasError: {
title: "加载画布时出错", // Error loading canvas
message: "加载画布时发生错误,请检查项目的素材,然后重试。",
// There was an error loading the canvas, check the project's assets and try again.
},
importError: {
title: "导入V1项目时出错", // Error importing V1 project
message: "导入V1项目时出错。检查项目文件,然后重试",
// There was an error importing the V1 project. Check the project file and try again
},
pasteRegionTooBigError: {
title: "粘贴区域时出错", // Error pasting region
message: "区域对于该素材过大。请尝试复制其他区域", // Region too big for this asset. Try copying another region
},
exportFormatNotFound: {
title: "导出项目时出错", // Error exporting project
message: "项目缺少导出格式。请在导出设置页面中选择一种导出格式。",
// Project is missing export format. Please select an export format in the export setting page.
},
activeLearningPredictionError: {
title: "主动学习错误", // Active Learning Error
message: "预测当前素材中的区域时发生错误。请验证您的主动学习配置,然后重试",
// An error occurred while predicting regions in the current asset.
// Please verify your active learning configuration and try again
},
},
};
-558
Ver Arquivo
@@ -1,558 +0,0 @@
import { IAppStrings } from "../strings";
/**
* App Strings for Traditional Chinese (zh-tw)
*/
export const chinesetw: IAppStrings = {
appName: "VOTT視覺物件標記工具", // Visual Object Tagging Tool
common: {
displayName: "顯示名稱", // Display Name
description: "說明", // Description
submit: "送出", // Submit
cancel: "取消", // Cancel
save: "儲存", // Save
delete: "刪除", // Delete
provider: "提供者", // Provider
homePage: "首頁", // Home Page
},
titleBar: {
help: "說明", // Help
minimize: "最小化", // Minimize
maximize: "最大化", // Maximize
restore: "還原", // Restore
close: "關閉", // Close
},
homePage: {
newProject: "新專案", // New Project
openLocalProject: {
title: "打開本機專案", // Open Local Project
},
openCloudProject: {
title: "打開雲端專案", // Open Cloud Project
selectConnection: "選擇連線", // Select a Connection
},
recentProjects: "最近的專案", // Recent Projects
deleteProject: {
title: "刪除專案", // Delete Project
confirmation: "確定要刪除專案?", // Are you sure you want to delete project
},
importProject: {
title: "匯入專案", // Import Project
confirmation: "您確定要將專案${project.file.name}的設定轉換為v2格式嗎?我們建議您首先備份專案文件。",
// Are you sure you want to convert project ${project.file.name} project settings to v2 format?
// We recommend you backup the project file first.
},
messages: {
deleteSuccess: "已成功刪除${project.name}專案", // Successfully deleted ${project.name}
},
},
appSettings: {
title: "應用程式設定", // Application Settings
storageTitle: "儲存空間設定", // Storage Settings
uiHelp: "您的設定存放在哪裡", // Where your settings are stored
save: "保存設定", // Save Settings
securityToken: {
name: {
title: "名稱", // Name
},
key: {
title: "鍵", // Key
},
},
securityTokens: {
title: "安全性權杖", // Security Tokens
description: "安全性權杖用於加密專案組態中的敏感資料",
// Security tokens are used to encrypt sensitive data within your project configuration
},
version: {
description: "版本:", // Version:
},
commit: "提交SHA", // Commit SHA
devTools: {
description: "打開應用程式開發工具以幫助診斷問題", // Open application developer tools to help diagnose issues
button: "切換開發工具", // Toggle Developer Tools
},
reload: {
description: "重新載入應用程式,放棄所有目前做的修改", // Reload the app discarding all current changes
button: "重新整理應用程式", // Refresh Application
},
messages: {
saveSuccess: "已成功保存應用程式設定", // Successfully saved application settings
},
},
projectSettings: {
title: "專案設定", // Project Settings
securityToken: {
title: "安全性權杖", // Security Token
description: "用於加密專案檔案中的敏感資料", // Used to encrypt sensitive data within project files
},
useSecurityToken: {
title: "使用安全令牌", // Use Security Token
description: "啟用後將在提供者配置內加密敏感數據",
// When enabled will encrypt sensitive data within provider configuration
},
save: "保存專案", // Save Project
sourceConnection: {
title: "來源連線", // Source Connection
description: "從何處載入資料", // Where to load assets from
},
targetConnection: {
title: "目標連線", // Target Connection
description: "在哪裡保存專案和匯出的資料", // Where to save the project and exported data
},
videoSettings: {
title: "影片設定", // Video Settings
description: "設定影片標記的速率", // The rate at which frames are extracted for tagging.
frameExtractionRate: "影像取樣率(影像每秒的畫面數)", // Frame Extraction Rate (frames per a video second)
},
addConnection: "新增連線", // Add Connection
messages: {
saveSuccess: "已成功保存${project.name}專案設定", // Successfully saved ${project.name} project settings
},
},
projectMetrics: {
title: "專案相關指標", // Project Metrics
assetsSectionTitle: "圖像數據", // Assets
// As for this VOTT tool, translate "Assets" to "Image data" in Traditional Chinese,
// as "Asset" can be confusing if directly translated.
totalAssetCount: "圖像數據總數", // Total Assets
visitedAssets: "已檢視的圖像數據(${count}", // Visited Assets (${count})
taggedAssets: "已標記的圖像數據(${count}", // Tagged Assets (${count})
nonTaggedAssets: "未標記的圖像數據(${count}", // Not Tagged Assets (${count})
nonVisitedAssets: "未檢視的圖像數據(${count}", // Not Visited Assets (${count})
tagsSectionTitle: "標記和標籤",
// Tags & Labels, so it can actually be same translation to Tags and Labels in Traditional Chinese,
// to differentiate, having slightly different translation for both keywords.
totalRegionCount: "已標記區域總數", // Total Tagged Regions
totalTagCount: "標記總數", // Total Tags
avgTagCountPerAsset: "每個圖像數據的平均標記數", // Average tags per asset
},
tags: {
title: "標記", // Tags
placeholder: "新增標記", // Add new tag
editor: "標記編輯器", // Tags Editor
modal: {
name: "標記名稱", // Tag Name
color: "標記顏色", // Tag Color
},
colors: {
white: "白色", // White
gray: "灰色", // Gray
red: "紅色", // Red
maroon: "栗色", // Maroon
yellow: "黃色", // Yellow
olive: "橄欖", // Olive
lime: "酸橙", // Lime
green: "綠色", // Green
aqua: "水色", // Aqua
teal: "藍綠色", // Teal
blue: "藍色", // Blue
navy: "海軍", // Navy
fuschia: "紫紅色", // Fuschia
purple: "紫色", // Purple
},
warnings: {
existingName: "標記名稱已存在。請選擇其他名字", // Tag name already exists. Choose another name
emptyName: "標記名稱不能為空白", // Cannot have an empty tag name
unknownTagName: "未命名", // Unknown
},
toolbar: {
add: "新增標記", // Add new tag
search: "尋找標記", // Search tags
edit: "編輯標記", // Edit tag
lock: "鎖定標記", // Lock tag
moveUp: "向上移動標記", // Move tag up
moveDown: "向下移動標記", // Move tag down
delete: "刪除標記", // Delete tag
},
},
connections: {
title: "連線", // Connections
details: "連線細節", // Connection Details
settings: "連線設定", // Connection Settings
instructions: "請選擇一個連線進行編輯", // Please select a connection to edit
save: "保存連線", // Save Connection
messages: {
saveSuccess: "已成功儲存${connection.name}", // Successfully saved ${connection.name}
deleteSuccess: "已成功刪除${connection.name}", // Successfully deleted ${connection.name}
},
imageCorsWarning: "警告:在Web瀏覽器中使用VoTT時,由於CORS(跨源資源共享)限制,來自Bing Image Search的某些圖像數據可能無法正確匯出。",
// Warning: When using VoTT in a Web browser, some assets from Bing Image Search may not export correctly
// due to CORS (Cross Origin Resource Sharing) restrictions.
blobCorsWarning: "警告:必須在Azure Blob儲存體帳戶上啟用CORS(跨域資源共享),才能將其用作來源或目標連接。 {0}中提供了有關啟用CORS的更多資訊。",
// Warning: CORS (Cross Domain Resource Sharing) must be enabled on the Azure Blob Storage account,
// in order to use it as a source or target connection.
// More information on enabling CORS can be found in the {0}
azDocLinkText: "Azure說明文件", // Azure Documentation.
providers: {
azureBlob: {
title: "Azure Blob 儲存體", // Azure Blob Storage
description: "",
accountName: {
title: "帳戶名", // Account Name
description: "",
},
containerName: {
title: "容器名稱", // Container Name
description: "",
},
sas: {
title: "SAS", // SAS
description: "用於驗證Blob儲存體帳戶的共用存取簽章",
// Shared access signature used to authenticate to the blob storage account
},
createContainer: {
title: "新增容器", // Create Container
description: "新增blob容器(如果還不存在時)", // Creates the blob container if it does not already exist
},
},
bing: {
title: "Bing 影像搜尋", // Bing Image Search
options: {
title: "Bing 影像搜尋選項",
}, // Bing Image Search Options
endpoint: {
title: "Endpoint",
description: "必應搜索 Azure 資源中列出的終結點",
},
apiKey: {
title: "API密鑰",
description: "必應搜索 Azure 資源中列出的 API 金鑰",
}, // API Key
query: {
title: "查詢",
description: "用於填充連接的搜索查詢",
}, // Query
aspectRatio: {
title: "長寬比", // Aspect Ratio
description: "按指定的縱橫比篩選結果",
options: {
all: "所有", // All
square: "矩形", // Square
wide: "寬", // Wide
tall: "高", // Tall
},
},
licenseType: {
title: "許可證類型",
description: "按指定的許可證類型篩選結果",
options: {
all: "全部(不過濾任何影像)",
any: "任何許可證類型的圖像",
public: "公有領域",
share: "免費分享和使用",
shareCommercially: "免費共用和使用商業",
modify: "免費修改、共用和使用",
modifyCommercially: "可自由修改、共用和在商業上使用",
},
},
size: {
title: "大小",
description: "按指定大小篩選結果",
options: {
all: "所有",
small: "小(小於200x200)",
medium: "中等(小於 500x500)",
large: "大(大於 500x500)",
wallpaper: "桌布(超大影像)",
},
},
},
local: {
title: "本機檔案系統", // Local File System
folderPath: "資料夾路徑", // Folder Path
selectFolder: "選擇資料夾", // Select Folder
chooseFolder: "選取資料夾", // Choose Folder
},
},
},
editorPage: {
width: "寬度", // Width
height: "高度", // Height
tagged: "已標記", // Tagged
visited: "已檢視", // Visited
toolbar: {
select: "選擇 (V)", // Select (V)
pan: "全景", // Pan
drawRectangle: "繪製矩形", // Draw Rectangle
drawPolygon: "繪製多邊形", // Draw Polygon
copyRectangle: "複製矩形", // Copy Rectangle
copy: "複製區域", // Copy Regions
cut: "剪下區域", // Cut Regions
paste: "貼上區域", // Paste Regions
removeAllRegions: "刪除所有區域", // Remove All Regions
previousAsset: "以前的圖像數據", // Previous Asset
nextAsset: "下一個圖像數據", // Next Asset
saveProject: "儲存專案", // Save Project
exportProject: "匯出專案", // Export Project
activeLearning: "主動學習", // Active Learning
},
videoPlayer: {
previousTaggedFrame: {
tooltip: "上一個標記的畫面", // Previous Tagged Frame
},
nextTaggedFrame: {
tooltip: "下一個標記的畫面", // Next Tagged Frame
},
previousExpectedFrame: {
tooltip: "上一個畫面", // Previous Frame
},
nextExpectedFrame: {
tooltip: "下一個畫面", // Next Frame
},
},
help: {
title: "切換輔助說明選單", // Toggle Help Menu
escape: "離開輔助說明選單", // Escape Help Menu
},
assetError: "無法載入圖像數據", // Unable to load asset
tags: {
hotKey: {
apply: "使用快捷鍵來套用標記", // Apply Tag with Hot Key
lock: "用快捷鍵來鎖定標記", // Lock Tag with Hot Key
},
rename: {
title: "重新命名標記", // Rename Tag
confirmation: "您確定要重新命名此標記嗎?它將在所有圖像數據中被重新命名",
// Are you sure you want to rename this tag? It will be renamed throughout all assets
},
delete: {
title: "刪除標記", // Delete Tag
confirmation: "您確定要刪除此標記嗎?它將在所有圖像數據中被刪除,並且只有使用此標記的任何區域也將被刪除",
// Are you sure you want to delete this tag? It will be deleted throughout all assets
// and any regions where this is the only tag will also be deleted
},
},
canvas: {
removeAllRegions: {
title: "刪除所有區域", // Remove All Regions
confirmation: "您確定要刪除所有區域嗎?", // Are you sure you want to remove all regions?
},
},
messages: {
enforceTaggedRegions: {
title: "檢測到無效的區域", // Invalid region(s) detected
description: "一個或多個區域尚未被標記。在繼續下一個圖像數據之前,請確保所有區域均已標記。",
// 1 or more regions have not been tagged.
// Ensure all regions are tagged before continuing to next asset.
},
},
},
export: {
title: "匯出", // Export
settings: "匯出設定", // Export Settings
saveSettings: "儲存匯出設定", // Save Export Settings
providers: {
common: {
properties: {
assetState: {
title: "圖像數據狀態", // Asset State
description: "匯出項目中包括哪些圖像數據", // Which assets to include in the export
options: {
all: "所有圖像數據", // All Assets
visited: "只有已檢視的圖像數據", // Only Visited Assets
tagged: "只有已標記的圖像數據", // Only tagged Assets
},
},
testTrainSplit: {
title: "測試/訓練分割", // Test / Train Split
description: "測試訓練分割以用於匯出數據", // The test train split to use for exported data
},
includeImages: {
title: "包含圖像", // Include Images
description: "是否在目標連接中包括二進位圖像數據",
// Whether or not to include binary image assets in target connection
},
},
},
vottJson: {
displayName: "VoTT JSON", // VoTT JSON
},
azureCV: {
displayName: "Azure自訂視覺服務", // Azure Custom Vision Service
regions: {
// reference to https://azure.microsoft.com/zh-tw/global-infrastructure/geographies/
// for official translation
australiaEast: "澳大利亞東部", // Australia East
centralIndia: "印度中部", // Central India
eastUs: "美國東部", // East US
eastUs2: "美國東部 2", // East US 2
japanEast: "日本東部", // Japan East
northCentralUs: "美國中北部", // North Central US
northEurope: "北歐", // North Europe
southCentralUs: "美國中南部", // South Central US
southeastAsia: "東南亞", // Southeast Asia
ukSouth: "英國南部", // UK South
westUs2: "美國西部 2", // West US 2
westEurope: "西歐", // West Europe
},
properties: {
apiKey: {
title: "API密鑰", // API Key
},
region: {
title: "區域", // Region
description: "部署服務的Azure區域", // The Azure region where your service is deployed
},
classificationType: {
title: "分類類型", // Classification Type
options: {
multiLabel: "每個圖像多個標記", // Multiple tags per image
multiClass: "每個圖像一個標記", // Single tag per image
},
},
name: {
title: "專案名", // Project Name
},
description: {
title: "專案簡介", // Project Description
},
domainId: {
title: "領域", // Domain
},
newOrExisting: {
title: "新增專案或既有專案", // New or Existing Project
options: {
new: "新增專案", // New Project
existing: "既有專案", // Existing Project
},
},
projectId: {
title: "專案名稱", // Project Name
},
projectType: {
title: "專案類型", // Project Type
options: {
classification: "分類", // Classification
objectDetection: "物件偵測", // Object Detection
},
},
},
},
tfRecords: {
displayName: "Tensorflow記錄", // Tensorflow Records
},
pascalVoc: {
displayName: "Pascal VOC", // Pascal VOC
exportUnassigned: {
title: "匯出未指定的項目", // Export Unassigned
description: "是否在已匯出的數據中包括未指定的標記", // Whether or not to include unassigned tags in exported data
},
},
cntk: {
displayName: "Microsoft Cognitive ToolkitCNTK)", // Microsoft Cognitive Toolkit (CNTK)
},
csv: {
displayName: "逗號分隔格式 (CSV)", // Comma Separated Values (CSV)
},
},
messages: {
saveSuccess: "已成功儲存匯出設定", // Successfully saved export settings
},
},
activeLearning: {
title: "主動學習", // Active Learning
form: {
properties: {
modelPathType: {
title: "模型提供者", // Model Provider
description: "從何處載入訓練模型", // Where to load the training model from
options: {
preTrained: "預先訓練Coco SSD", // Pre-trained Coco SSD
customFilePath: "自訂(檔案路徑)", // Custom (File path)
customWebUrl: "自訂 (URL)", // Custom (Url)
},
},
autoDetect: {
title: "自動偵測", // Auto Detect
description: "在圖像數據之間瀏覽時是否自動進行預測",
// Whether or not to automatically make predictions as you navigate between assets
},
modelPath: {
title: "模型路徑", // Model path
description: "從本機檔案系統中選擇模型", // Select a model from your local file system
},
modelUrl: {
title: "模型網址", // Model URL
description: "從公共網址載入模型", // Load your model from a public web URL
},
predictTag: {
title: "預測標記", // Predict Tag
description: "是否在預測中自動包含標記", // Whether or not to automatically include tags in predictions
},
},
},
messages: {
loadingModel: "正在載入主動學習模型...", // Loading active learning model...
errorLoadModel: "載入主動學習模型時出現錯誤", // Error loading active learning model
saveSuccess: "已成功儲存主動學習設定", // Successfully saved active learning settings
},
},
profile: {
settings: "個人資料設定", // Profile Settings
},
errors: {
unknown: {
title: "未知錯誤", // Unknown Error
message: "該應用程式遇到未知錯誤。請再試一遍。", // The app encountered an unknown error. Please try again.
},
projectUploadError: {
title: "上傳檔案時出現錯誤", // Error Uploading File
message: "上傳檔案時出現錯誤。請確認檔案格式正確,然後重試。",
// There was an error uploading the file. Please verify the file is of the correct format and try again.
},
genericRenderError: {
title: "載入應用程序時出現錯誤", // Error Loading Application
message: "轉譯應用程序時發生錯誤。請再試一遍", // An error occured while rendering the application. Please try again
},
projectInvalidSecurityToken: {
title: "載入專案文件時出現錯誤", // Error loading project file
message: "專案使用的安全性認證無效。請驗證是否在您的應用程式設定中正確的設定了專案的安全性認證",
// The security token referenced by the project is invalid. Verify that the security token
// for the project has been set correctly within your application settings
},
projectInvalidJson: {
title: "解析專案文件時出現錯誤", // Error parsing project file
message: "所選擇的專案文件不包含有效的JSON格式。請確認該專案檔案並且重試。",
// The selected project files does not contain valid JSON. Please check the file any try again.
},
projectDeleteError: {
title: "刪除專案時出現錯誤", // Error deleting project
message: "刪除專案時發生錯誤。請確認專案檔案和安全性認證是否存在,然後重試",
// An error occured while deleting the project.
// Validate the project file and security token exist and try again
},
securityTokenNotFound: {
title: "載入專案檔案時出現錯誤", // Error loading project file
message: "在當前的應用程式設定中找不到該專案所使用的安全性認證。請確認安全性認證是否存在,然後嘗試重新載入專案。",
// The security token referenced by the project cannot be found in your current application settings.
// Verify the security token exists and try to reload the project.
},
canvasError: {
title: "載入畫面時出現錯誤", // Error loading canvas
message: "載入畫面時發生錯誤,請檢查專案的圖像數據,然後重試。",
// There was an error loading the canvas, check the project's assets and try again.
},
importError: {
title: "匯入V1格式專案時出現錯誤", // Error importing V1 project
message: "匯入V1格式專案時出現錯誤。請檢查專案檔案,然後重試",
// There was an error importing the V1 project. Check the project file and try again
},
pasteRegionTooBigError: {
title: "貼上區域時發生錯誤", // Error pasting region
message: "此區域對於這一個圖像數據太大了。請嘗試複製其他的區域",
// Region too big for this asset. Try copying another region
},
exportFormatNotFound: {
title: "匯出專案時出現錯誤", // Error exporting project
message: "專案設定中缺少匯出格式。請在匯出設定畫面中選擇一種匯出格式。",
// Project is missing export format. Please select an export format in the export setting page.
},
activeLearningPredictionError: {
title: "主動學習錯誤", // Active Learning Error
message: "在預測當前圖像數據中的區域時發生錯誤。請確認您的主動學習相關設定,然後重試",
// An error occurred while predicting regions in the current asset.
// Please verify your active learning configuration and try again
},
},
};
-2
Ver Arquivo
@@ -277,7 +277,6 @@ export default class MockFactory {
id: `project-${name}`,
name: `Project ${name}`,
version: appInfo.version,
useSecurityToken: true,
securityToken: `Security-Token-${name}`,
assets: {},
exportFormat: MockFactory.exportFormat(),
@@ -398,7 +397,6 @@ export default class MockFactory {
public static createLocalFileSystemOptions(): ILocalFileSystemProxyOptions {
return {
folderPath: "C:\\projects\\vott\\project",
relativePath: false,
};
}
+1 -16
Ver Arquivo
@@ -1,19 +1,8 @@
import { strings, addLocValues, IAppStrings, interpolate, interpolateJson } from "./strings";
import { english } from "./localization/en-us";
import { spanish } from "./localization/es-cl";
import { japanese } from "./localization/ja";
import { chinesetw } from "./localization/zh-tw";
import { korean } from "./localization/ko-kr";
import { chinese } from "./localization/zh-ch";
const languages = [
"en",
"es",
"ja",
"tw",
"ko",
"ch",
];
const languages = ["en", "es"];
describe("Localization tests", () => {
@@ -21,10 +10,6 @@ describe("Localization tests", () => {
return {
en: english,
es: spanish,
ja: japanese,
tw: chinesetw,
ko: korean,
ch: chinese,
}[language];
}
+9 -61
Ver Arquivo
@@ -1,10 +1,6 @@
import LocalizedStrings, { LocalizedStringsMethods } from "react-localization";
import { english } from "./localization/en-us";
import { spanish } from "./localization/es-cl";
import { japanese } from "./localization/ja";
import { chinesetw } from "./localization/zh-tw";
import { korean } from "./localization/ko-kr";
import { chinese } from "./localization/zh-ch";
/**
* Interface for all required strings in application
@@ -90,10 +86,6 @@ export interface IAppStrings {
title: string;
description: string;
},
useSecurityToken: {
title: string;
description: string;
},
save: string;
sourceConnection: {
title: string;
@@ -200,56 +192,17 @@ export interface IAppStrings {
}
},
bing: {
title: string,
endpoint: {
title: string,
description?: string,
},
apiKey: {
title: string,
description?: string,
},
query: {
title: string,
description?: string,
},
options: {
title: string,
},
title: string;
options: string;
apiKey: string;
query: string;
aspectRatio: {
title: string;
description?: string,
options: {
all: string;
square: string;
wide: string;
tall: string;
}
},
size: {
title: string,
description?: string,
options: {
all: string,
small: string,
medium: string,
large: string,
wallpaper: string,
},
},
licenseType: {
title: string,
description?: string,
options: {
all: string,
any: string,
public: string,
share: string,
shareCommercially: string,
modify: string,
modifyCommercially: string,
},
},
all: string;
square: string;
wide: string;
tall: string;
}
},
local: {
title: string;
@@ -499,13 +452,8 @@ interface IErrorMetadata {
interface IStrings extends LocalizedStringsMethods, IAppStrings { }
export const strings: IStrings = new LocalizedStrings({
// TODO: Need to comment out other languages which will not be used
en: english,
es: spanish,
ja: japanese,
tw: chinesetw,
ko: korean,
ch: chinese,
});
/**
+1 -1
Ver Arquivo
@@ -59,7 +59,7 @@ export function encodeFileURI(path: string, additionalEncodings?: boolean): stri
const encodings = {
"\#": "%23",
"\?": "%3F",
};
};
const encodedURI = `file:${encodeURI(normalizeSlashes(path))}`;
if (additionalEncodings) {
return encodedURI.replace(matchString, (match) => encodings[match]);
+29 -28
Ver Arquivo
@@ -1,8 +1,7 @@
import {
app, ipcMain, BrowserWindow, BrowserWindowConstructorOptions,
Menu, MenuItemConstructorOptions,
Menu, MenuItemConstructorOptions, MenuItem,
} from "electron";
import registerMixins from "../registerMixins";
import { IpcMainProxy } from "./common/ipcMainProxy";
import LocalFileSystem from "./providers/storage/localFileSystem";
@@ -19,13 +18,16 @@ function createWindow() {
titleBarStyle: "hidden",
backgroundColor: "#272B30",
show: false,
// Node Integration is now disabled by default
// https://github.com/electron/electron/pull/16235
webPreferences: {
nodeIntegration: true,
},
};
const staticUrl = process.env.ELECTRON_START_URL || `file:///${__dirname}/index.html`;
if (process.env.ELECTRON_START_URL) {
windowOptions.webPreferences = {
webSecurity: false,
};
windowOptions.webPreferences.webSecurity = false;
}
mainWindow = new BrowserWindow(windowOptions);
@@ -71,20 +73,21 @@ function onToggleDevTools() {
*/
function registerContextMenu(browserWindow: BrowserWindow): void {
const selectionMenu = Menu.buildFromTemplate([
{ role: "copy", accelerator: "CmdOrCtrl+C" },
{ type: "separator" },
{ role: "selectall", accelerator: "CmdOrCtrl+A" },
new MenuItem({ role: "copy", accelerator: "CmdOrCtrl+C" }),
new MenuItem({ role: "copy", accelerator: "CmdOrCtrl+C" }),
new MenuItem({ type: "separator" }),
new MenuItem({ role: "selectAll", accelerator: "CmdOrCtrl+A" }),
]);
const inputMenu = Menu.buildFromTemplate([
{ role: "undo", accelerator: "CmdOrCtrl+Z" },
{ role: "redo", accelerator: "CmdOrCtrl+Shift+Z" },
{ type: "separator" },
{ role: "cut", accelerator: "CmdOrCtrl+X" },
{ role: "copy", accelerator: "CmdOrCtrl+C" },
{ role: "paste", accelerator: "CmdOrCtrl+V" },
{ type: "separator" },
{ role: "selectall", accelerator: "CmdOrCtrl+A" },
new MenuItem({ role: "undo", accelerator: "CmdOrCtrl+Z" }),
new MenuItem({ role: "redo", accelerator: "CmdOrCtrl+Shift+Z" }),
new MenuItem({ type: "separator" }),
new MenuItem({ role: "cut", accelerator: "CmdOrCtrl+X" }),
new MenuItem({ role: "copy", accelerator: "CmdOrCtrl+C" }),
new MenuItem({ role: "paste", accelerator: "CmdOrCtrl+V" }),
new MenuItem({ type: "separator" }),
new MenuItem({ role: "selectAll", accelerator: "CmdOrCtrl+A" }),
]);
browserWindow.webContents.on("context-menu", (e, props) => {
@@ -108,16 +111,16 @@ function registerContextMenu(browserWindow: BrowserWindow): void {
},
{ role: "editMenu" },
{
label: "View", submenu: [
{ role: "reload" },
{ type: "separator" },
{ role: "toggleDevTools" },
{ role: "toggleFullScreen" },
{ type: "separator" },
{ role: "resetZoom" },
{ role: "zoomIn" },
{ role: "zoomOut" },
],
label: "View", submenu: Menu.buildFromTemplate([
new MenuItem({ role: "reload" }),
new MenuItem({ type: "separator" }),
new MenuItem({ role: "toggleDevTools" }),
new MenuItem({ role: "togglefullscreen" }),
new MenuItem({ type: "separator" }),
new MenuItem({ role: "resetZoom" }),
new MenuItem({ role: "zoomIn" }),
new MenuItem({ role: "zoomOut" }),
]),
},
{ role: "windowMenu" },
];
@@ -125,8 +128,6 @@ function registerContextMenu(browserWindow: BrowserWindow): void {
Menu.setApplicationMenu(menu);
}
registerMixins();
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
@@ -1,10 +1,7 @@
import fs from "fs";
import path, { relative, sep } from "path";
import path from "path";
import shortid from "shortid";
import LocalFileSystem from "./localFileSystem";
import mockFs from "mock-fs";
import { AssetService } from "../../../services/assetService";
import registerMixins from "../../../registerMixins";
jest.mock("electron", () => ({
dialog: {
@@ -13,57 +10,13 @@ jest.mock("electron", () => ({
}));
import { dialog } from "electron";
registerMixins();
describe("LocalFileSystem Storage Provider", () => {
let localFileSystem: LocalFileSystem = null;
const sourcePath = path.join("path", "to", "my", "source");
const sourceFilePaths = [
path.join(sourcePath, "file1.jpg"),
path.join(sourcePath, "file2.jpg"),
path.join(sourcePath, "subDir1", "file3.jpg"),
path.join(sourcePath, "subDir1", "file4.jpg"),
path.join(sourcePath, "subDir2", "file5.jpg"),
path.join(sourcePath, "subDir2", "file6.jpg"),
path.join(sourcePath, "subDir2", "subSubDir2", "file7.jpg"),
path.join(sourcePath, "subDir2", "subSubDir2", "file8.jpg"),
];
beforeEach(() => {
localFileSystem = new LocalFileSystem(null);
});
beforeAll(() => {
mockFs({
path: {
to: {
my: {
source: {
"file1.jpg": "contents",
"file2.jpg": "contents",
"subDir1": {
"file3.jpg": "contents",
"file4.jpg": "contents",
},
"subDir2": {
"file5.jpg": "contents",
"file6.jpg": "contents",
"subSubDir2": {
"file7.jpg": "contents",
"file8.jpg": "contents",
},
},
},
},
},
},
});
});
afterAll(() => {
mockFs.restore();
});
it("writes, reads and deletes a file as text", async () => {
const filePath = path.join(process.cwd(), "test-output", `${shortid.generate()}.json`);
const contents = {
@@ -120,7 +73,7 @@ describe("LocalFileSystem Storage Provider", () => {
it("selectContainer opens a dialog and resolves with selected path", async () => {
const expectedContainerPath = "/path/to/container";
const mockMethod = dialog.showOpenDialog as jest.Mock;
mockMethod.mockReturnValue([expectedContainerPath]);
mockMethod.mockReturnValue(Promise.resolve({ filePaths: [expectedContainerPath] }));
const result = await localFileSystem.selectContainer();
expect(result).toEqual(expectedContainerPath);
@@ -128,30 +81,13 @@ describe("LocalFileSystem Storage Provider", () => {
it("selectContainer rejects when a folder path is not returned", async () => {
const mockMethod = dialog.showOpenDialog as jest.Mock;
mockMethod.mockReturnValue([]);
mockMethod.mockReturnValue(Promise.resolve({ filePaths: [] }));
await expect(localFileSystem.selectContainer()).rejects.not.toBeNull();
const result = await localFileSystem.selectContainer();
expect(result).toEqual("");
});
it("deleting file that doesn't exist resolves successfully", async () => {
await expect(localFileSystem.deleteFile("/path/to/fake/file.txt")).resolves.not.toBeNull();
});
it("getAssets gets all files recursively using path relative to the source", async () => {
AssetService.createAssetFromFilePath = jest.fn(() => []);
await localFileSystem.getAssets(sourcePath, true);
const calls: any[] = (AssetService.createAssetFromFilePath as any).mock.calls;
expect(calls).toHaveLength(8);
expect(calls).toEqual(sourceFilePaths.map((path) => [
path, undefined, path.replace(`${sourcePath}${sep}`, ""),
]));
});
it("getAssets gets all files recursively using absolute path", async () => {
AssetService.createAssetFromFilePath = jest.fn(() => []);
await localFileSystem.getAssets(sourcePath, false);
const calls: any[] = (AssetService.createAssetFromFilePath as any).mock.calls;
expect(calls).toHaveLength(8);
expect(calls).toEqual(sourceFilePaths.map((path) => [path, undefined, path]));
});
});
+17 -30
Ver Arquivo
@@ -3,30 +3,27 @@ import fs from "fs";
import path from "path";
import rimraf from "rimraf";
import { IStorageProvider } from "../../../providers/storage/storageProviderFactory";
import { IAsset, AssetType, StorageType, IConnection } from "../../../models/applicationState";
import { IAsset, AssetType, StorageType } from "../../../models/applicationState";
import { AssetService } from "../../../services/assetService";
import { strings } from "../../../common/strings";
import { ILocalFileSystemProxyOptions } from "../../../providers/storage/localFileSystemProxy";
export default class LocalFileSystem implements IStorageProvider {
public storageType: StorageType.Local;
constructor(private browserWindow: BrowserWindow) { }
public selectContainer(): Promise<string> {
return new Promise<string>((resolve, reject) => {
const filePaths = dialog.showOpenDialog(this.browserWindow, {
title: strings.connections.providers.local.selectFolder,
buttonLabel: strings.connections.providers.local.chooseFolder,
properties: ["openDirectory", "createDirectory"],
});
if (!filePaths || filePaths.length !== 1) {
return reject();
}
resolve(filePaths[0]);
public async selectContainer(): Promise<string> {
const dialogResult = await dialog.showOpenDialog(this.browserWindow, {
title: strings.connections.providers.local.selectFolder,
buttonLabel: strings.connections.providers.local.chooseFolder,
properties: ["openDirectory", "createDirectory"],
});
if (!dialogResult.filePaths || dialogResult.filePaths.length !== 1) {
return "";
}
return dialogResult.filePaths[0];
}
public readText(filePath: string): Promise<string> {
@@ -93,15 +90,8 @@ export default class LocalFileSystem implements IStorageProvider {
});
}
public async listFiles(folderPath: string): Promise<string[]> {
const normalizedPath = path.normalize(folderPath);
const files = await this.listItems(normalizedPath, (stats) => !stats.isDirectory());
const directories = await this.listItems(normalizedPath, (stats) => stats.isDirectory());
await directories.forEachAsync(async (directory) => {
const directoryFiles = await this.listFiles(directory);
directoryFiles.forEach((file) => files.push(file));
});
return files;
public listFiles(folderPath: string): Promise<string[]> {
return this.listItems(path.normalize(folderPath), (stats) => !stats.isDirectory());
}
public listContainers(folderPath: string): Promise<string[]> {
@@ -144,12 +134,9 @@ export default class LocalFileSystem implements IStorageProvider {
});
}
public async getAssets(sourceConnectionFolderPath?: string, relativePath: boolean = false): Promise<IAsset[]> {
const files = await this.listFiles(path.normalize(sourceConnectionFolderPath));
return files.map((filePath) => AssetService.createAssetFromFilePath(
filePath,
undefined,
relativePath ? path.relative(sourceConnectionFolderPath, filePath) : filePath))
public async getAssets(folderPath?: string): Promise<IAsset[]> {
return (await this.listFiles(path.normalize(folderPath)))
.map((filePath) => AssetService.createAssetFromFilePath(filePath))
.filter((asset) => asset.type !== AssetType.Unknown);
}
+1 -2
Ver Arquivo
@@ -107,8 +107,7 @@ export interface IProject {
id: string;
name: string;
version: string;
useSecurityToken: boolean;
securityToken?: string;
securityToken: string;
description?: string;
tags: ITag[];
sourceConnection: IConnection;
@@ -18,7 +18,7 @@ describe("Load default model from filesystem with TF io.IOHandler", () => {
return Promise.resolve([]);
});
const handler = new ElectronProxyHandler("folder", false);
const handler = new ElectronProxyHandler("folder");
try {
const model = await tf.loadGraphModel(handler);
} catch (_) {
@@ -4,8 +4,8 @@ import { LocalFileSystemProxy, ILocalFileSystemProxyOptions } from "../../provid
export class ElectronProxyHandler implements tfc.io.IOHandler {
protected readonly provider: LocalFileSystemProxy;
constructor(folderPath: string, relativePath: boolean) {
const options: ILocalFileSystemProxyOptions = { folderPath, relativePath };
constructor(folderPath: string) {
const options: ILocalFileSystemProxyOptions = { folderPath };
this.provider = new LocalFileSystemProxy(options);
}
+1 -1
Ver Arquivo
@@ -53,7 +53,7 @@ export class ObjectDetection {
const response = await axios.get(modelFolderPath + "/classes.json");
this.jsonClasses = JSON.parse(JSON.stringify(response.data));
} else {
const handler = new ElectronProxyHandler(modelFolderPath, false);
const handler = new ElectronProxyHandler(modelFolderPath);
this.model = await tf.loadGraphModel(handler);
this.jsonClasses = await handler.loadClasses();
}
+1 -1
Ver Arquivo
@@ -94,7 +94,7 @@ describe("Azure Custom Vision Export Provider", () => {
expect(customVisionMock).toBeCalledWith({
apiKey: providerOptions.apiKey,
baseUrl: `https://${providerOptions.region}.api.cognitive.microsoft.com/customvision/v3.3/Training`,
baseUrl: `https://${providerOptions.region}.api.cognitive.microsoft.com/customvision/v2.2/Training`,
});
});
+1 -1
Ver Arquivo
@@ -74,7 +74,7 @@ export class AzureCustomVisionProvider extends ExportProvider<IAzureCustomVision
const cusomVisionServiceOptions: IAzureCustomVisionServiceOptions = {
apiKey: options.apiKey,
baseUrl: `https://${options.region}.api.cognitive.microsoft.com/customvision/v3.3/Training`,
baseUrl: `https://${options.region}.api.cognitive.microsoft.com/customvision/v2.2/Training`,
};
this.customVisionService = new AzureCustomVisionService(cusomVisionServiceOptions);
}
+2 -2
Ver Arquivo
@@ -9,7 +9,7 @@
"ui:widget": "externalPicker",
"ui:options": {
"method": "GET",
"url": "https://${props.formContext.providerOptions.region}.api.cognitive.microsoft.com/customvision/v3.3/Training/projects",
"url": "https://${props.formContext.providerOptions.region}.api.cognitive.microsoft.com/customvision/v2.2/Training/projects",
"authHeaderName": "Training-key",
"authHeaderValue": "${props.formContext.providerOptions.apiKey}",
"keySelector": "${item.id}",
@@ -20,7 +20,7 @@
"ui:widget": "externalPicker",
"ui:options": {
"method": "GET",
"url": "https://${props.formContext.providerOptions.region}.api.cognitive.microsoft.com/customvision/v3.3/Training/domains",
"url": "https://${props.formContext.providerOptions.region}.api.cognitive.microsoft.com/customvision/v2.2/Training/domains",
"authHeaderName": "Training-key",
"authHeaderValue": "${props.formContext.providerOptions.apiKey}",
"keySelector": "${item.id}",
@@ -13,7 +13,7 @@ describe("Azure Custom Vision Service", () => {
beforeEach(() => {
customVisionOptions = {
baseUrl: "https://southcentralus.api.cognitive.microsoft.com/customvision/v3.3/Training",
baseUrl: "https://southcentralus.api.cognitive.microsoft.com/customvision/v2.2/Training",
apiKey: "ABC123",
};
customVisionService = new AzureCustomVisionService(customVisionOptions);
@@ -25,7 +25,7 @@ class TestAssetProvider implements IAssetProvider {
public initialize(): Promise<void> {
throw new Error("Method not implemented");
}
public getAssets(): Promise<IAsset[]> {
public getAssets(containerName?: string): Promise<IAsset[]> {
throw new Error("Method not implemented.");
}
}
@@ -10,7 +10,6 @@ import getHostProcess, { HostProcessType } from "../../common/hostProcess";
export interface IAssetProvider {
initialize?(): Promise<void>;
getAssets(containerName?: string): Promise<IAsset[]>;
addDefaultPropsToNewConnection?(connection: IConnection): IConnection;
}
/**
+2 -3
Ver Arquivo
@@ -5,7 +5,6 @@ import {
TokenCredential, AnonymousCredential, ContainerURL,
StorageURL, ServiceURL, Credential, Aborter, BlockBlobURL,
} from "@azure/storage-blob";
import { BlobDeleteResponse } from "@azure/storage-blob/typings/lib/generated/lib/models";
/**
* Options for Azure Cloud Storage
@@ -191,8 +190,8 @@ export class AzureBlobStorage implements IStorageProvider {
* @param containerName - Container from which to retrieve assets. Defaults to
* container specified in Azure Cloud Storage options
*/
public async getAssets(): Promise<IAsset[]> {
const { containerName } = this.options;
public async getAssets(containerName?: string): Promise<IAsset[]> {
containerName = (containerName) ? containerName : this.options.containerName;
const files = await this.listFiles(containerName);
const result: IAsset[] = [];
for (const file of files) {
+8 -62
Ver Arquivo
@@ -1,29 +1,19 @@
{
"type": "object",
"title": "${strings.connections.providers.bing.options.title}",
"title": "${strings.connections.providers.bing.options}",
"required": ["apiKey","query"],
"properties": {
"endpoint": {
"type": "string",
"title": "Endpoint",
"description": "The endpoint from your Bing Search Azure resource",
"default": "https://api.bing.microsoft.com/",
"pattern": "^https?\\\\://[a-zA-Z0-9\\\\-\\\\.]+\\\\.[a-zA-Z]{2,3}(/\\\\S*)?$"
},
"apiKey": {
"type": "string",
"title": "${strings.connections.providers.bing.apiKey.title}",
"description": "${strings.connections.providers.bing.apiKey.description}"
"title": "${strings.connections.providers.bing.apiKey}"
},
"query": {
"type": "string",
"title": "${strings.connections.providers.bing.query.title}",
"description": "${strings.connections.providers.bing.query.description}"
"title": "${strings.connections.providers.bing.query}"
},
"aspectRatio": {
"type": "string",
"title": "${strings.connections.providers.bing.aspectRatio.title}",
"description": "${strings.connections.providers.bing.aspectRatio.description}",
"enum": [
"all",
"square",
@@ -32,55 +22,11 @@
],
"default": "all",
"enumNames": [
"${strings.connections.providers.bing.aspectRatio.options.all}",
"${strings.connections.providers.bing.aspectRatio.options.square}",
"${strings.connections.providers.bing.aspectRatio.options.wide}",
"${strings.connections.providers.bing.aspectRatio.options.tall}"
]
},
"size": {
"type": "string",
"title": "${strings.connections.providers.bing.size.title}",
"description": "${strings.connections.providers.bing.size.description}",
"enum": [
"All",
"Small",
"Medium",
"Large",
"Wallpaper"
],
"default": "All",
"enumNames": [
"${strings.connections.providers.bing.size.options.all}",
"${strings.connections.providers.bing.size.options.small}",
"${strings.connections.providers.bing.size.options.medium}",
"${strings.connections.providers.bing.size.options.large}",
"${strings.connections.providers.bing.size.options.wallpaper}"
]
},
"licenseType": {
"type": "string",
"title": "${strings.connections.providers.bing.licenseType.title}",
"description": "${strings.connections.providers.bing.licenseType.description}",
"enum": [
"All",
"Any",
"Public",
"Share",
"ShareCommercially",
"Modify",
"ModifyCommercially"
],
"default": "All",
"enumNames": [
"${strings.connections.providers.bing.licenseType.options.all}",
"${strings.connections.providers.bing.licenseType.options.any}",
"${strings.connections.providers.bing.licenseType.options.public}",
"${strings.connections.providers.bing.licenseType.options.share}",
"${strings.connections.providers.bing.licenseType.options.shareCommercially}",
"${strings.connections.providers.bing.licenseType.options.modify}",
"${strings.connections.providers.bing.licenseType.options.modifyCommercially}"
"${strings.connections.providers.bing.aspectRatio.all}",
"${strings.connections.providers.bing.aspectRatio.square}",
"${strings.connections.providers.bing.aspectRatio.wide}",
"${strings.connections.providers.bing.aspectRatio.tall}"
]
}
}
}
}
+5 -40
Ver Arquivo
@@ -1,22 +1,15 @@
import axios from "axios";
import {
BingImageSearch,
IBingImageSearchOptions,
BingImageSearchAspectRatio,
BingImageSearchSize,
BingImageSearchLicenseType,
} from "./bingImageSearch";
import { BingImageSearch, IBingImageSearchOptions, BingImageSearchAspectRatio } from "./bingImageSearch";
import { IAsset, AssetType, AssetState } from "../../models/applicationState";
import MD5 from "md5.js";
describe("Bing Image Search", () => {
const defaultOptions: IBingImageSearchOptions = {
const options: IBingImageSearchOptions = {
apiKey: "ABC123",
query: "Waterfalls",
aspectRatio: BingImageSearchAspectRatio.All,
size: BingImageSearchSize.All,
licenseType: BingImageSearchLicenseType.All,
};
const provider = new BingImageSearch(options);
const assets = [
{ contentUrl: "http://images.com/image1.jpg" },
@@ -33,36 +26,9 @@ describe("Bing Image Search", () => {
});
});
it("calls the Bing image search API with default API url", async () => {
const provider = new BingImageSearch(defaultOptions);
it("calls the Bing image search API", async () => {
// tslint:disable-next-line:max-line-length
const expectedUrl = `${BingImageSearch.DefaultApiUrl}/v7.0/images/search?q=${defaultOptions.query}&aspect=${defaultOptions.aspectRatio}&license=${defaultOptions.licenseType}&size=${defaultOptions.size}`;
const expectedHeaders = {
headers: {
"Ocp-Apim-Subscription-Key": defaultOptions.apiKey,
"Accept": "application/json",
},
};
await provider.getAssets();
expect(axios.get).toBeCalledWith(expectedUrl, expectedHeaders);
});
it("calls the Bing image search API with custom configuration", async () => {
const options: IBingImageSearchOptions = {
...defaultOptions,
apiKey: "XYZ123",
query: "Custom",
endpoint: "https://api.bing.microsoft.com",
aspectRatio: BingImageSearchAspectRatio.Square,
licenseType: BingImageSearchLicenseType.Public,
size: BingImageSearchSize.Large,
};
const provider = new BingImageSearch(options);
// tslint:disable-next-line:max-line-length
const expectedUrl = `${options.endpoint}/v7.0/images/search?q=${options.query}&aspect=${options.aspectRatio}&license=${options.licenseType}&size=${options.size}`;
const expectedUrl = `https://api.cognitive.microsoft.com/bing/v7.0/images/search?q=${options.query}&aspect=${options.aspectRatio}`;
const expectedHeaders = {
headers: {
"Ocp-Apim-Subscription-Key": options.apiKey,
@@ -85,7 +51,6 @@ describe("Bing Image Search", () => {
size: null,
};
const provider = new BingImageSearch(defaultOptions);
const assets = await provider.getAssets();
expect(assets.length).toEqual(assets.length);
expect(assets[0]).toEqual(expectedAsset);
+3 -28
Ver Arquivo
@@ -7,18 +7,14 @@ import { createQueryString } from "../../common/utils";
/**
* Options for Bing Image Search
* @member endpoint - The endpoint to use for the Bing Search API
* @member apiKey - Bing Search API Key (Cognitive Services)
* @member query - Query for Bing Search
* @member aspectRatio - Aspect Ratio for desired images
*/
export interface IBingImageSearchOptions {
endpoint?: string;
apiKey: string;
query: string;
aspectRatio: BingImageSearchAspectRatio;
size?: BingImageSearchSize;
licenseType?: BingImageSearchLicenseType;
}
/**
@@ -31,29 +27,11 @@ export enum BingImageSearchAspectRatio {
All = "All",
}
export enum BingImageSearchLicenseType {
All = "All",
Any = "Any",
Public = "Public",
Share = "Share",
ShareCommercially = "ShareCommercially",
Modify = "Modify",
ModifyCommercially = "ModifyCommercially",
}
export enum BingImageSearchSize {
All = "All",
Small = "Small",
Medium = "Medium",
Large = "Large",
Wallpaper = "Wallpaper",
}
/**
* Asset Provider for Bing Image Search
*/
export class BingImageSearch implements IAssetProvider {
public static DefaultApiUrl = "https://api.cognitive.microsoft.com/bing";
private static SEARCH_URL = "https://api.cognitive.microsoft.com/bing/v7.0/images/search";
constructor(private options: IBingImageSearchOptions) {
Guard.null(options);
@@ -66,14 +44,11 @@ export class BingImageSearch implements IAssetProvider {
const query = {
q: this.options.query,
aspect: this.options.aspectRatio,
license: this.options.licenseType || BingImageSearchLicenseType.All,
size: this.options.size || BingImageSearchSize.All,
};
const baseUrl = this.options.endpoint || BingImageSearch.DefaultApiUrl;
const apiUrl = `${baseUrl}/v7.0/images/search?${createQueryString(query)}`;
const url = `${BingImageSearch.SEARCH_URL}?${createQueryString(query)}`;
const response = await axios.get(apiUrl, {
const response = await axios.get(url, {
headers: {
"Ocp-Apim-Subscription-Key": this.options.apiKey,
"Accept": "application/json",
@@ -2,7 +2,6 @@ import { IpcRendererProxy } from "../../common/ipcRendererProxy";
import { LocalFileSystemProxy, ILocalFileSystemProxyOptions } from "./localFileSystemProxy";
import { StorageProviderFactory } from "./storageProviderFactory";
import registerProviders from "../../registerProviders";
import MockFactory from "../../common/mockFactory";
describe("LocalFileSystem Proxy Storage Provider", () => {
it("Provider is registered with the StorageProviderFactory", () => {
@@ -20,7 +19,6 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
let provider: LocalFileSystemProxy = null;
const options: ILocalFileSystemProxyOptions = {
folderPath: "/test",
relativePath: false,
};
beforeEach(() => {
@@ -124,40 +122,5 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
expect(IpcRendererProxy.send).toBeCalledWith("LocalFileSystem:listContainers", [expectedContainerPath]);
expect(actualFolders).toEqual(expectedFolders);
});
it("sends relative path argument according to options", async () => {
const sendFunction = jest.fn();
IpcRendererProxy.send = sendFunction;
await provider.getAssets();
const { folderPath, relativePath } = options;
expect(IpcRendererProxy.send).toBeCalledWith("LocalFileSystem:getAssets", [folderPath, relativePath]);
sendFunction.mockReset();
const newFolderPath = "myFolder";
const newRelativePath = true;
const relativeProvider = new LocalFileSystemProxy({
folderPath: newFolderPath,
relativePath: newRelativePath,
});
await relativeProvider.getAssets();
expect(IpcRendererProxy.send).toBeCalledWith("LocalFileSystem:getAssets", [newFolderPath, newRelativePath]);
});
it("adds default props to a new connection", () => {
const connection = MockFactory.createTestConnection();
delete connection.providerOptions["relativePath"];
expect(connection).not.toHaveProperty("providerOptions.relativePath");
delete connection.id;
expect(provider.addDefaultPropsToNewConnection(connection))
.toHaveProperty("providerOptions.relativePath", true);
});
it("does not add default props to existing connection", () => {
const connection = MockFactory.createTestConnection();
delete connection.providerOptions["relativePath"];
expect(provider.addDefaultPropsToNewConnection(connection))
.not.toHaveProperty("providerOptions.relativePath");
});
});
});
+4 -24
Ver Arquivo
@@ -1,7 +1,7 @@
import { IpcRendererProxy } from "../../common/ipcRendererProxy";
import { IStorageProvider } from "./storageProviderFactory";
import { IAssetProvider } from "./assetProviderFactory";
import { IAsset, IConnection, StorageType } from "../../models/applicationState";
import { IAsset, StorageType } from "../../models/applicationState";
const PROXY_NAME = "LocalFileSystem";
@@ -11,7 +11,6 @@ const PROXY_NAME = "LocalFileSystem";
*/
export interface ILocalFileSystemProxyOptions {
folderPath: string;
relativePath: boolean;
}
/**
@@ -27,7 +26,6 @@ export class LocalFileSystemProxy implements IStorageProvider, IAssetProvider {
if (!this.options) {
this.options = {
folderPath: null,
relativePath: false,
};
}
}
@@ -127,26 +125,8 @@ export class LocalFileSystemProxy implements IStorageProvider, IAssetProvider {
* Retrieve assets from directory
* @param folderName - Directory containing assets
*/
public getAssets(): Promise<IAsset[]> {
const { folderPath, relativePath } = this.options;
return IpcRendererProxy.send(`${PROXY_NAME}:getAssets`, [folderPath, relativePath]);
}
/**
* Adds default properties to new connections
*
* Currently adds `relativePath: true` to the providerOptions. Pre-existing connections
* will only use absolute path
*
* @param connection Connection
*/
public addDefaultPropsToNewConnection(connection: IConnection): IConnection {
return connection.id ? connection : {
...connection,
providerOptions: {
...connection.providerOptions,
relativePath: true,
} as any,
};
public getAssets(folderName?: string): Promise<IAsset[]> {
const folderPath = [this.options.folderPath, folderName].join("/");
return IpcRendererProxy.send(`${PROXY_NAME}:getAssets`, [folderPath]);
}
}
@@ -51,7 +51,7 @@ class TestStorageProvider implements IStorageProvider {
public deleteContainer(folderPath: string): Promise<void> {
throw new Error("Method not implemented.");
}
public getAssets(): Promise<IAsset[]> {
public getAssets(containerName?: string): Promise<IAsset[]> {
throw new Error("Method not implemented.");
}
}
@@ -21,7 +21,7 @@ describe("Alert component", () => {
it("Renders nothing if not activated", () => {
const wrapper = createComponent(defaultProps);
expect(wrapper.html()).toBeNull();
expect(wrapper.html()).toEqual("");
});
it("Renders modal when activated", () => {
@@ -161,7 +161,7 @@ describe("Video Asset Component", () => {
};
setupMoveFrameTest(currentAsset);
wrapper.find(CustomVideoPlayerButton).at(3).simulate("click");
wrapper.find(CustomVideoPlayerButton).at(3).find("button").simulate("click");
expect(videoPlayerMock.prototype.pause).toBeCalled();
expect(videoPlayerMock.prototype.seek).toBeCalledWith(expectedAsset.timestamp);
@@ -174,7 +174,7 @@ describe("Video Asset Component", () => {
onBeforeAssetChangedHandler.mockImplementationOnce(() => false);
setupMoveFrameTest(currentAsset);
wrapper.find(CustomVideoPlayerButton).at(3).simulate("click");
wrapper.find(CustomVideoPlayerButton).at(3).find("button").simulate("click");
expect(videoPlayerMock.prototype.pause).not.toBeCalled();
expect(videoPlayerMock.prototype.seek).not.toBeCalled();
@@ -190,8 +190,8 @@ describe("Video Asset Component", () => {
setupMoveFrameTest(currentAsset);
wrapper.find(CustomVideoPlayerButton).at(3).simulate("click");
wrapper.find(CustomVideoPlayerButton).at(2).simulate("click");
wrapper.find(CustomVideoPlayerButton).at(3).find("button").simulate("click");
wrapper.find(CustomVideoPlayerButton).at(2).find("button").simulate("click");
expect(videoPlayerMock.prototype.pause).toBeCalled();
expect(videoPlayerMock.prototype.seek).toBeCalledWith(expectedAsset.timestamp);
@@ -203,7 +203,7 @@ describe("Video Asset Component", () => {
onBeforeAssetChangedHandler.mockImplementationOnce(() => false);
setupMoveFrameTest(currentAsset);
wrapper.find(CustomVideoPlayerButton).at(3).simulate("click");
wrapper.find(CustomVideoPlayerButton).at(3).find("button").simulate("click");
expect(videoPlayerMock.prototype.pause).not.toBeCalled();
expect(videoPlayerMock.prototype.seek).not.toBeCalled();
@@ -214,8 +214,8 @@ describe("Video Asset Component", () => {
const currentAsset = childAssets[0];
setupMoveFrameTest(currentAsset);
wrapper.find(CustomVideoPlayerButton).at(3).simulate("click");
wrapper.find(CustomVideoPlayerButton).at(1).simulate("click");
wrapper.find(CustomVideoPlayerButton).at(3).find("button").simulate("click");
wrapper.find(CustomVideoPlayerButton).at(1).find("button").simulate("click");
expect(videoPlayerMock.prototype.pause).toBeCalled();
expect(videoPlayerMock.prototype.seek).toBeCalledWith(
@@ -227,7 +227,7 @@ describe("Video Asset Component", () => {
onBeforeAssetChangedHandler.mockImplementationOnce(() => false);
setupMoveFrameTest(currentAsset);
wrapper.find(CustomVideoPlayerButton).at(3).simulate("click");
wrapper.find(CustomVideoPlayerButton).at(3).find("button").simulate("click");
expect(videoPlayerMock.prototype.pause).not.toBeCalled();
expect(videoPlayerMock.prototype.seek).not.toBeCalled();
@@ -237,7 +237,7 @@ describe("Video Asset Component", () => {
const currentAsset = childAssets[4];
setupMoveFrameTest(currentAsset);
wrapper.find(CustomVideoPlayerButton).at(0).simulate("click");
wrapper.find(CustomVideoPlayerButton).at(0).find("button").simulate("click");
expect(videoPlayerMock.prototype.pause).toBeCalled();
expect(videoPlayerMock.prototype.seek).toBeCalledWith(
@@ -249,7 +249,7 @@ describe("Video Asset Component", () => {
onBeforeAssetChangedHandler.mockImplementationOnce(() => false);
setupMoveFrameTest(currentAsset);
wrapper.find(CustomVideoPlayerButton).at(0).simulate("click");
wrapper.find(CustomVideoPlayerButton).at(0).find("button").simulate("click");
expect(videoPlayerMock.prototype.pause).not.toBeCalled();
expect(videoPlayerMock.prototype.seek).not.toBeCalled();
@@ -22,7 +22,7 @@ describe("Confirm component", () => {
it("Renders nothing if not activiated", () => {
const wrapper = createComponent(defaultProps);
expect(wrapper.html()).toBeNull();
expect(wrapper.html()).toEqual("");
});
it("Renders modal when activiated", () => {
@@ -97,4 +97,4 @@ export class ConnectionPicker extends React.Component<IConnectionPickerProps, IC
}
}
export const ConnectionPickerWithRouter = withRouter<IConnectionPickerProps>(ConnectionPicker);
export const ConnectionPickerWithRouter = withRouter(ConnectionPicker);
@@ -2,7 +2,7 @@ import React from "react";
import { IActiveLearningFormProps, ActiveLearningForm, IActiveLearningFormState } from "./activeLearningForm";
import { ReactWrapper, mount } from "enzyme";
import { ModelPathType, IActiveLearningSettings } from "../../../../models/applicationState";
import Form from "react-jsonschema-form";
import Form, { IChangeEvent } from "react-jsonschema-form";
describe("Active Learning Form", () => {
const onChangeHandler = jest.fn();
@@ -21,6 +21,17 @@ describe("Active Learning Form", () => {
onCancel: onCancelHandler,
};
const defaultEvent: IChangeEvent = {
edit: false,
errorSchema: null,
errors: [],
idSchema: null,
schema: null,
status: null,
uiSchema: null,
formData: null,
};
function createComponent(props?: IActiveLearningFormProps)
: ReactWrapper<IActiveLearningFormProps, IActiveLearningFormState> {
props = props || defaultProps;
@@ -72,16 +83,16 @@ describe("Active Learning Form", () => {
};
// Set type to URL
wrapper.find(Form).props().onChange({ formData: { modelPathType: ModelPathType.Url } });
wrapper.find(Form).props().onChange({ ...defaultEvent, formData: { modelPathType: ModelPathType.Url } });
// Set the remaining settings
wrapper.find(Form).props().onChange({ formData });
wrapper.find(Form).props().onChange({ ...defaultEvent, formData });
expect(wrapper.state().formData).toEqual(formData);
expect(onChangeHandler).toBeCalledWith(formData);
});
it("submits form data to the registered submit handler", () => {
const wrapper = createComponent();
wrapper.find(Form).props().onSubmit({ formData: defaultProps.settings });
wrapper.find(Form).props().onSubmit({ ...defaultEvent, formData: defaultProps.settings });
expect(onSubmitHandler).toBeCalledWith(defaultProps.settings);
});
@@ -1,6 +1,6 @@
import React from "react";
import Form, { ISubmitEvent, IChangeEvent, Widget } from "react-jsonschema-form";
import { IActiveLearningSettings, ModelPathType } from "../../../../models/applicationState";
import { IActiveLearningSettings } from "../../../../models/applicationState";
import { strings, addLocValues } from "../../../../common/strings";
import CustomFieldTemplate from "../../common/customField/customFieldTemplate";
import LocalFolderPicker from "../../common/localFolderPicker/localFolderPicker";
@@ -11,7 +11,6 @@ import ConnectionForm from "./connectionForm";
import ConnectionItem from "./connectionItem";
import "./connectionsPage.scss";
import { toast } from "react-toastify";
import { AssetProviderFactory } from "../../../../providers/storage/assetProviderFactory";
/**
* Properties for Connection Page
@@ -135,20 +134,12 @@ export default class ConnectionPage extends React.Component<IConnectionPageProps
}
private onFormSubmit = async (connection: IConnection) => {
connection = this.addDefaultPropsIfNewConnection(connection);
await this.props.actions.saveConnection(connection);
toast.success(interpolate(strings.connections.messages.saveSuccess, { connection }));
this.props.history.goBack();
}
private addDefaultPropsIfNewConnection(connection: IConnection): IConnection {
const assetProvider = AssetProviderFactory.createFromConnection(connection);
return !connection.id && assetProvider.addDefaultPropsToNewConnection
? assetProvider.addDefaultPropsToNewConnection(connection)
: connection;
}
private onFormCancel() {
this.props.history.goBack();
}
@@ -6,6 +6,11 @@
"type": "string",
"pattern": "^[^\\\\\\\\/:*?\\\\\\\"<>|]*$"
},
"securityToken": {
"title": "${strings.projectSettings.securityToken.title}",
"description": "${strings.projectSettings.securityToken.description}",
"type": "string"
},
"sourceConnection": {
"title": "${strings.projectSettings.sourceConnection.title}",
"description": "${strings.projectSettings.sourceConnection.description}",
@@ -39,41 +44,6 @@
"tags": {
"title": "${strings.tags.title}",
"type": "array"
},
"useSecurityToken": {
"title": "${strings.projectSettings.useSecurityToken.title}",
"description": "${strings.projectSettings.useSecurityToken.description}",
"type": "boolean",
"default": true
}
},
"dependencies": {
"useSecurityToken": {
"oneOf": [
{
"properties": {
"useSecurityToken": {
"enum": [
true
]
},
"securityToken": {
"title": "${strings.projectSettings.securityToken.title}",
"description": "${strings.projectSettings.securityToken.description}",
"type": "string"
}
}
},
{
"properties": {
"useSecurityToken": {
"enum": [
false
]
}
}
}
]
}
},
"required": [
@@ -263,8 +263,8 @@ describe("Project Form Component", () => {
onCancel: onCancelHandler,
});
const newTagName = "My new tag";
wrapper.find("input#tagInputField").last().simulate("change", { target: { value: newTagName } });
wrapper.find("input#tagInputField").last().simulate("keyDown", { keyCode: 13 });
wrapper.find("input").last().simulate("change", { target: { value: newTagName } });
wrapper.find("input").last().simulate("keyDown", { keyCode: 13 });
const tags = wrapper.state().formData.tags;
expect(tags).toHaveLength(1);
@@ -5,11 +5,10 @@ import { addLocValues, strings } from "../../../../common/strings";
import { IConnection, IProject, ITag, IAppSettings } from "../../../../models/applicationState";
import { StorageProviderFactory } from "../../../../providers/storage/storageProviderFactory";
import { ConnectionPickerWithRouter } from "../../common/connectionPicker/connectionPicker";
import { CustomField, CustomWidget } from "../../common/customField/customField";
import { CustomField } from "../../common/customField/customField";
import CustomFieldTemplate from "../../common/customField/customFieldTemplate";
import { ISecurityTokenPickerProps, SecurityTokenPicker } from "../../common/securityTokenPicker/securityTokenPicker";
import "vott-react/dist/css/tagsInput.css";
import Checkbox from "rc-checkbox";
import { IConnectionProviderPickerProps } from "../../common/connectionProviderPicker/connectionProviderPicker";
import LocalFolderPicker from "../../common/localFolderPicker/localFolderPicker";
@@ -55,11 +54,6 @@ export interface IProjectFormState {
export default class ProjectForm extends React.Component<IProjectFormProps, IProjectFormState> {
private widgets = {
localFolderPicker: (LocalFolderPicker as any) as Widget,
checkbox: CustomWidget(Checkbox, (props) => ({
checked: props.value,
onChange: (value) => props.onChange(value.target.checked),
disabled: props.disabled,
})),
};
private tagsInput: React.RefObject<TagsInput>;
@@ -2,9 +2,6 @@
"securityToken": {
"ui:field": "securityToken"
},
"useSecurityToken": {
"ui:widget": "checkbox"
},
"description": {
"ui:widget": "textarea"
},
@@ -60,7 +60,6 @@ describe("Project settings page", () => {
const store = createReduxStore(MockFactory.initialState());
const props = MockFactory.projectSettingsProps();
const saveProjectSpy = jest.spyOn(props.projectActions, "saveProject");
const ensureSecurityTokenSpy = jest.spyOn(props.applicationActions, "ensureSecurityToken");
projectServiceMock.prototype.save = jest.fn((project) => Promise.resolve(project));
@@ -69,7 +68,6 @@ describe("Project settings page", () => {
await MockFactory.flushUi();
expect(saveProjectSpy).toBeCalled();
expect(ensureSecurityTokenSpy).toBeCalled();
});
it("Throws an error when a user tries to create a duplicate project", async () => {
@@ -116,7 +114,6 @@ describe("Project settings page", () => {
const store = createReduxStore(initialState);
const props = MockFactory.projectSettingsProps();
const saveProjectSpy = jest.spyOn(props.projectActions, "saveProject");
const ensureSecurityTokenSpy = jest.spyOn(props.applicationActions, "ensureSecurityToken");
const saveAppSettingsSpy = jest.spyOn(props.applicationActions, "saveAppSettings");
projectServiceMock.prototype.save = jest.fn((project) => Promise.resolve(project));
@@ -136,11 +133,6 @@ describe("Project settings page", () => {
securityToken: `${project.name} Token`,
});
expect(ensureSecurityTokenSpy).toBeCalledWith({
...project,
securityToken: `${project.name} Token`,
});
expect(localStorage.removeItem).toBeCalledWith("projectForm");
});
@@ -47,7 +47,7 @@ function mapDispatchToProps(dispatch) {
};
}
const projectFormKey = "projectForm";
const projectFormTempKey = "projectForm";
/**
* @name - Project Settings Page
@@ -64,17 +64,14 @@ export default class ProjectSettingsPage extends React.Component<IProjectSetting
// If we are creating a new project check to see if there is a partial
// project already created in local storage
if (this.props.match.url === "/projects/create") {
const projectJson = localStorage.getItem(projectFormKey);
const projectJson = localStorage.getItem(projectFormTempKey);
if (projectJson) {
this.setState({ project: JSON.parse(projectJson) });
}
} else if (!this.props.project && projectId) {
const projectToLoad = this.props.recentProjects.find((project) => project.id === projectId);
if (projectToLoad) {
if (projectToLoad.useSecurityToken) {
await this.props.applicationActions.ensureSecurityToken(projectToLoad);
}
await this.props.applicationActions.ensureSecurityToken(projectToLoad);
await this.props.projectActions.loadProject(projectToLoad);
}
}
@@ -122,19 +119,16 @@ export default class ProjectSettingsPage extends React.Component<IProjectSetting
*/
private onFormChange = (project: IProject) => {
if (this.isPartialProject(project)) {
localStorage.setItem(projectFormKey, JSON.stringify(project));
localStorage.setItem(projectFormTempKey, JSON.stringify(project));
}
}
private onFormSubmit = async (project: IProject) => {
const isNew = !(!!project.id);
if (project.useSecurityToken) {
await this.props.applicationActions.ensureSecurityToken(project);
}
await this.props.applicationActions.ensureSecurityToken(project);
await this.props.projectActions.saveProject(project);
localStorage.removeItem(projectFormKey);
localStorage.removeItem(projectFormTempKey);
toast.success(interpolate(strings.projectSettings.messages.saveSuccess, { project }));
@@ -146,7 +140,7 @@ export default class ProjectSettingsPage extends React.Component<IProjectSetting
}
private onFormCancel = () => {
localStorage.removeItem(projectFormKey);
localStorage.removeItem(projectFormTempKey);
this.props.history.goBack();
}
+4 -5
Ver Arquivo
@@ -3,7 +3,6 @@ import Menu, { MenuItem, SubMenu, Divider } from "rc-menu";
import { PlatformType } from "../../../common/hostProcess";
import "./titleBar.scss";
import { strings } from "../../../common/strings";
import { HelpMenu } from "./helpMenu";
export interface ITitleBarProps extends React.Props<TitleBar> {
icon?: string | JSX.Element;
@@ -88,23 +87,23 @@ export class TitleBar extends React.Component<ITitleBarProps, ITitleBarState> {
{this.state.platform === PlatformType.Windows &&
<ul>
<li title={strings.titleBar.minimize} className="btn-window-minimize"
onClick={this.minimizeWindow}>
onClick={this.minimizeWindow}>
<i className="far fa-window-minimize" />
</li>
{!this.state.maximized &&
<li title={strings.titleBar.maximize} className="btn-window-maximize"
onClick={this.maximizeWindow}>
onClick={this.maximizeWindow}>
<i className="far fa-window-maximize" />
</li>
}
{this.state.maximized &&
<li title={strings.titleBar.restore} className="btn-window-restore"
onClick={this.unmaximizeWindow}>
onClick={this.unmaximizeWindow}>
<i className="far fa-window-restore" />
</li>
}
<li title={strings.titleBar.close} className="btn-window-close"
onClick={this.closeWindow}>
onClick={this.closeWindow}>
<i className="fas fa-times" />
</li>
</ul>
+6 -4
Ver Arquivo
@@ -12,9 +12,11 @@ import {
IProject,
} from "../../models/applicationState";
import { createAction, createPayloadAction, IPayloadAction } from "./actionCreators";
import { IExportResults } from "../../providers/export/exportProvider";
import { ExportAssetState, IExportResults } from "../../providers/export/exportProvider";
import { appInfo } from "../../common/appInfo";
import { strings } from "../../common/strings";
import { IExportFormat } from "vott-react";
import { IVottJsonExportProviderOptions } from "../../providers/export/vottJson";
/**
* Actions to be performed in relation to projects
@@ -46,7 +48,7 @@ export function loadProject(project: IProject):
const projectToken = appState.appSettings.securityTokens
.find((securityToken) => securityToken.name === project.securityToken);
if (project.useSecurityToken && !projectToken) {
if (!projectToken) {
throw new AppError(ErrorCode.SecurityTokenNotFound, "Security Token Not Found");
}
const loadedProject = await projectService.load(project, projectToken);
@@ -74,7 +76,7 @@ export function saveProject(project: IProject)
const projectToken = appState.appSettings.securityTokens
.find((securityToken) => securityToken.name === project.securityToken);
if (project.useSecurityToken && !projectToken) {
if (!projectToken) {
throw new AppError(ErrorCode.SecurityTokenNotFound, "Security Token Not Found");
}
@@ -102,7 +104,7 @@ export function deleteProject(project: IProject)
const projectToken = appState.appSettings.securityTokens
.find((securityToken) => securityToken.name === project.securityToken);
if (project.useSecurityToken && !projectToken) {
if (!projectToken) {
throw new AppError(ErrorCode.SecurityTokenNotFound, "Security Token Not Found");
}
+1 -1
Ver Arquivo
@@ -40,7 +40,7 @@ export const reducer = (state: IProject = null, action: AnyAction): IProject =>
const updatedAssets = { ...state.assets } || {};
updatedAssets[action.payload.asset.id] = { ...action.payload.asset };
const assetTags = new Set();
const assetTags = new Set<string>();
action.payload.regions.forEach((region) => region.tags.forEach((tag) => assetTags.add(tag)));
const newTags: ITag[] = state.tags ? [...state.tags] : [];
-17
Ver Arquivo
@@ -9,7 +9,6 @@ import HtmlFileReader from "../common/htmlFileReader";
import { encodeFileURI } from "../common/utils";
import _ from "lodash";
import registerMixins from "../registerMixins";
import MD5 from "md5.js";
describe("Asset Service", () => {
describe("Static Methods", () => {
@@ -25,22 +24,6 @@ describe("Asset Service", () => {
expect(asset.format).toEqual("jpg");
});
it("creates an asset using file path as identifier", () => {
const path = "c:/dir1/dir2/asset1.jpg";
const asset = AssetService.createAssetFromFilePath(path);
const expectedIdenfifier = `file:${path}`;
const expectedId = new MD5().update(expectedIdenfifier).digest("hex");
expect(asset.id).toEqual(expectedId);
});
it("creates an asset using passed in identifier", () => {
const path = "C:\\dir1\\dir2\\asset1.jpg";
const identifier = "asset1.jpg";
const asset = AssetService.createAssetFromFilePath(path, undefined, identifier);
const expectedId = new MD5().update(identifier).digest("hex");
expect(asset.id).toEqual(expectedId);
});
it("creates an asset from an encoded file", () => {
const path = "C:\\dir1\\dir2\\asset%201.jpg";
const asset = AssetService.createAssetFromFilePath(path);
+13 -16
Ver Arquivo
@@ -23,15 +23,13 @@ export class AssetService {
/**
* Create IAsset from filePath
* @param assetFilePath - filepath of asset
* @param assetFileName - name of asset
* @param filePath - filepath of asset
* @param fileName - name of asset
*/
public static createAssetFromFilePath(
assetFilePath: string,
assetFileName?: string,
assetIdentifier?: string): IAsset {
Guard.empty(assetFilePath);
const normalizedPath = assetFilePath.toLowerCase();
public static createAssetFromFilePath(filePath: string, fileName?: string): IAsset {
Guard.empty(filePath);
const normalizedPath = filePath.toLowerCase();
// If the path is not already prefixed with a protocol
// then assume it comes from the local file system
@@ -39,18 +37,17 @@ export class AssetService {
!normalizedPath.startsWith("https://") &&
!normalizedPath.startsWith("file:")) {
// First replace \ character with / the do the standard url encoding then encode unsupported characters
assetFilePath = encodeFileURI(assetFilePath, true);
filePath = encodeFileURI(filePath, true);
}
assetIdentifier = assetIdentifier || assetFilePath;
const md5Hash = new MD5().update(assetIdentifier).digest("hex");
const pathParts = assetFilePath.split(/[\\\/]/);
const md5Hash = new MD5().update(filePath).digest("hex");
const pathParts = filePath.split(/[\\\/]/);
// Example filename: video.mp4#t=5
// fileNameParts[0] = "video"
// fileNameParts[1] = "mp4"
// fileNameParts[2] = "t=5"
assetFileName = assetFileName || pathParts[pathParts.length - 1];
const fileNameParts = assetFileName.split(".");
fileName = fileName || pathParts[pathParts.length - 1];
const fileNameParts = fileName.split(".");
const extensionParts = fileNameParts[fileNameParts.length - 1].split(/[\?#]/);
const assetFormat = extensionParts[0];
@@ -61,8 +58,8 @@ export class AssetService {
format: assetFormat,
state: AssetState.NotVisited,
type: assetType,
name: assetFileName,
path: assetFilePath,
name: fileName,
path: filePath,
size: null,
};
}
-1
Ver Arquivo
@@ -57,7 +57,6 @@ export default class ImportService implements IImportService {
id: shortid.generate(),
name: projectInfo.file.name.split(".")[0],
version: packageJson.version,
useSecurityToken: true,
securityToken: `${projectInfo.file.name.split(".")[0]} Token`,
description: "Converted V1 Project",
tags: parsedTags,
+21 -43
Ver Arquivo
@@ -9,13 +9,13 @@ import {
import { constants } from "../common/constants";
import { ExportProviderFactory } from "../providers/export/exportProviderFactory";
import { generateKey } from "../common/crypto";
import * as utils from "../common/utils";
import { encryptProject, decryptProject } from "../common/utils";
import { ExportAssetState } from "../providers/export/exportProvider";
import { IVottJsonExportProviderOptions } from "../providers/export/vottJson";
import { IPascalVOCExportProviderOptions } from "../providers/export/pascalVOC";
describe("Project Service", () => {
let projectService: IProjectService = null;
let projectSerivce: IProjectService = null;
let testProject: IProject = null;
let projectList: IProject[] = null;
let securityToken: ISecurityToken = null;
@@ -33,41 +33,27 @@ describe("Project Service", () => {
StorageProviderFactory.create = jest.fn(() => storageProviderMock);
ExportProviderFactory.create = jest.fn(() => exportProviderMock);
const encryptSpy = jest.spyOn(utils, "encryptProject");
const decryptSpy = jest.spyOn(utils, "decryptProject");
beforeEach(() => {
securityToken = {
name: "TestToken",
key: generateKey(),
};
testProject = MockFactory.createTestProject("TestProject");
projectService = new ProjectService();
projectSerivce = new ProjectService();
storageProviderMock.writeText.mockClear();
storageProviderMock.deleteFile.mockClear();
encryptSpy.mockClear();
decryptSpy.mockClear();
});
it("Load decrypts any project settings using the specified key", async () => {
const encryptedProject = utils.encryptProject(testProject, securityToken);
const decryptedProject = await projectService.load(encryptedProject, securityToken);
const encryptedProject = encryptProject(testProject, securityToken);
const decryptedProject = await projectSerivce.load(encryptedProject, securityToken);
expect(decryptedProject).toEqual(testProject);
expect(decryptSpy).toBeCalledWith(encryptedProject, securityToken);
});
it("Does not decrypt project when a security token is not in use", async () => {
const project: IProject = { ...testProject, useSecurityToken: false };
const loadedProject = await projectService.load(project);
expect(loadedProject).toEqual(project);
expect(decryptSpy).not.toBeCalled();
});
it("Saves calls project storage provider to write project", async () => {
const result = await projectService.save(testProject, securityToken);
const result = await projectSerivce.save(testProject, securityToken);
const encryptedProject: IProject = {
...testProject,
@@ -86,7 +72,6 @@ describe("Project Service", () => {
};
expect(result).toEqual(encryptedProject);
expect(encryptSpy).toBeCalledWith(testProject, securityToken);
expect(StorageProviderFactory.create).toBeCalledWith(
testProject.targetConnection.providerType,
testProject.targetConnection.providerOptions,
@@ -97,16 +82,9 @@ describe("Project Service", () => {
expect.any(String));
});
it("Does not encrypt project during save when a security token is not in use", async () => {
const projectToSave: IProject = { ...testProject, useSecurityToken: false };
await projectService.save(projectToSave);
expect(encryptSpy).not.toBeCalled();
});
it("sets default export settings when not defined", async () => {
testProject.exportFormat = null;
const result = await projectService.save(testProject, securityToken);
const result = await projectSerivce.save(testProject, securityToken);
const vottJsonExportProviderOptions: IVottJsonExportProviderOptions = {
assetState: ExportAssetState.Visited,
@@ -118,14 +96,14 @@ describe("Project Service", () => {
providerOptions: vottJsonExportProviderOptions,
};
const decryptedProject = utils.decryptProject(result, securityToken);
const decryptedProject = decryptProject(result, securityToken);
expect(decryptedProject.exportFormat).toEqual(expectedExportFormat);
});
it("sets default active learning setting when not defined", async () => {
testProject.activeLearningSettings = null;
const result = await projectService.save(testProject, securityToken);
const result = await projectSerivce.save(testProject, securityToken);
const activeLearningSettings: IActiveLearningSettings = {
autoDetect: false,
@@ -138,7 +116,7 @@ describe("Project Service", () => {
it("initializes tags to empty array if not defined", async () => {
testProject.tags = null;
const result = await projectService.save(testProject, securityToken);
const result = await projectSerivce.save(testProject, securityToken);
expect(result.tags).toEqual([]);
});
@@ -149,7 +127,7 @@ describe("Project Service", () => {
providerOptions: null,
};
await projectService.save(testProject, securityToken);
await projectSerivce.save(testProject, securityToken);
expect(ExportProviderFactory.create).toBeCalledWith(
testProject.exportFormat.providerType,
@@ -162,7 +140,7 @@ describe("Project Service", () => {
it("Save throws error if writing to storage provider fails", async () => {
const expectedError = "Error writing to storage provider";
storageProviderMock.writeText.mockImplementationOnce(() => Promise.reject(expectedError));
await expect(projectService.save(testProject, securityToken)).rejects.toEqual(expectedError);
await expect(projectSerivce.save(testProject, securityToken)).rejects.toEqual(expectedError);
});
it("Save throws error if storage provider cannot be created", async () => {
@@ -170,11 +148,11 @@ describe("Project Service", () => {
const createMock = StorageProviderFactory.create as jest.Mock;
createMock.mockImplementationOnce(() => { throw expectedError; });
await expect(projectService.save(testProject, securityToken)).rejects.toEqual(expectedError);
await expect(projectSerivce.save(testProject, securityToken)).rejects.toEqual(expectedError);
});
it("Delete calls project storage provider to delete project", async () => {
await projectService.delete(testProject);
await projectSerivce.delete(testProject);
expect(StorageProviderFactory.create).toBeCalledWith(
testProject.targetConnection.providerType,
@@ -189,7 +167,7 @@ describe("Project Service", () => {
storageProviderMock.deleteFile
.mockImplementationOnce(() => Promise.reject(expectedError));
await expect(projectService.delete(testProject)).rejects.toEqual(expectedError);
await expect(projectSerivce.delete(testProject)).rejects.toEqual(expectedError);
});
it("Delete call fails if storage provider cannot be created", async () => {
@@ -197,20 +175,20 @@ describe("Project Service", () => {
const createMock = StorageProviderFactory.create as jest.Mock;
createMock.mockImplementationOnce(() => { throw expectedError; });
await expect(projectService.delete(testProject)).rejects.toEqual(expectedError);
await expect(projectSerivce.delete(testProject)).rejects.toEqual(expectedError);
});
it("isDuplicate returns false when called with a unique project", async () => {
testProject = MockFactory.createTestProject("TestProject");
projectList = MockFactory.createTestProjects();
expect(projectService.isDuplicate(testProject, projectList)).toEqual(false);
expect(projectSerivce.isDuplicate(testProject, projectList)).toEqual(false);
});
it("isDuplicate returns true when called with a duplicate project", async () => {
testProject = MockFactory.createTestProject("1");
testProject.id = undefined;
projectList = MockFactory.createTestProjects();
expect(projectService.isDuplicate(testProject, projectList)).toEqual(true);
expect(projectSerivce.isDuplicate(testProject, projectList)).toEqual(true);
});
it("deletes all asset metadata files when project is deleted", async () => {
@@ -221,7 +199,7 @@ describe("Project Service", () => {
testProject.assets = _.keyBy(assets, (asset) => asset.id);
await projectService.delete(testProject);
await projectSerivce.delete(testProject);
expect(storageProviderMock.deleteFile.mock.calls).toHaveLength(assets.length + 1);
});
@@ -237,8 +215,8 @@ describe("Project Service", () => {
} as IPascalVOCExportProviderOptions,
};
const encryptedProject = utils.encryptProject(testProject, securityToken);
const decryptedProject = await projectService.load(encryptedProject, securityToken);
const encryptedProject = encryptProject(testProject, securityToken);
const decryptedProject = await projectSerivce.load(encryptedProject, securityToken);
expect(decryptedProject.exportFormat.providerType).toEqual("pascalVOC");
expect(decryptedProject.exportFormat.providerOptions).toEqual(testProject.exportFormat.providerOptions);
+6 -10
Ver Arquivo
@@ -19,8 +19,8 @@ import { IExportFormat } from "vott-react";
* @member delete - Delete a project
*/
export interface IProjectService {
load(project: IProject, securityToken?: ISecurityToken): Promise<IProject>;
save(project: IProject, securityToken?: ISecurityToken): Promise<IProject>;
load(project: IProject, securityToken: ISecurityToken): Promise<IProject>;
save(project: IProject, securityToken: ISecurityToken): Promise<IProject>;
delete(project: IProject): Promise<void>;
isDuplicate(project: IProject, projectList: IProject[]): boolean;
}
@@ -49,13 +49,11 @@ export default class ProjectService implements IProjectService {
* @param project The project JSON to load
* @param securityToken The security token used to decrypt sensitive project settings
*/
public load(project: IProject, securityToken?: ISecurityToken): Promise<IProject> {
public load(project: IProject, securityToken: ISecurityToken): Promise<IProject> {
Guard.null(project);
try {
const loadedProject = project.useSecurityToken
? decryptProject(project, securityToken)
: { ...project };
const loadedProject = decryptProject(project, securityToken);
// Ensure tags is always initialized to an array
if (!loadedProject.tags) {
@@ -86,7 +84,7 @@ export default class ProjectService implements IProjectService {
* @param project - Project to save
* @param securityToken - Security Token to encrypt
*/
public async save(project: IProject, securityToken?: ISecurityToken): Promise<IProject> {
public async save(project: IProject, securityToken: ISecurityToken): Promise<IProject> {
Guard.null(project);
if (!project.id) {
@@ -112,9 +110,7 @@ export default class ProjectService implements IProjectService {
const storageProvider = StorageProviderFactory.createFromConnection(project.targetConnection);
await this.saveExportSettings(project);
project = project.useSecurityToken
? encryptProject(project, securityToken)
: { ...project };
project = encryptProject(project, securityToken);
await storageProvider.writeText(
`${project.name}${constants.projectFileExtension}`,

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais