├── .gitignore
├── Ac.Net.Authentication
├── .gitignore
├── APSClientHelper.cs
├── Ac.Net.Authentication.csproj
├── EncryptionHelper.cs
├── FodyWeavers.xml
├── Html.cs
├── Models
│ ├── AuthParameters.cs
│ ├── IAuthParamProvider.cs
│ ├── ITokenManager.cs
│ └── TokenData.cs
└── ThreeLeggedTokenManager.cs
├── ApsSettings.Data
├── ApsSettings.Data.csproj
├── DataContext.cs
├── DataUtils
│ └── SettingsUtiltities.cs
├── Migrations
│ ├── 20230824162133_InitialMigration.Designer.cs
│ ├── 20230824162133_InitialMigration.cs
│ ├── 20230831165452_AddBulkDownloadTables.Designer.cs
│ ├── 20230831165452_AddBulkDownloadTables.cs
│ ├── 20230831172112_EditBulkDownloadTables.Designer.cs
│ ├── 20230831172112_EditBulkDownloadTables.cs
│ ├── 20230831192257_MoreChangesToDownloadTables.Designer.cs
│ ├── 20230831192257_MoreChangesToDownloadTables.cs
│ ├── 20230831192723_RemoveFieldFromDownloadJobTable.Designer.cs
│ ├── 20230831192723_RemoveFieldFromDownloadJobTable.cs
│ ├── 20230831205634_ChangeProp.Designer.cs
│ ├── 20230831205634_ChangeProp.cs
│ ├── 20230901155709_AddObjectIdColToDownloadFilesTable.Designer.cs
│ ├── 20230901155709_AddObjectIdColToDownloadFilesTable.cs
│ ├── 20231113165259_DownloadVirtualStatus.Designer.cs
│ ├── 20231113165259_DownloadVirtualStatus.cs
│ └── DataContextModelSnapshot.cs
└── Models
│ ├── BatchJob.cs
│ ├── BulkDownload.cs
│ ├── BulkDownloadFile.cs
│ ├── BulkOpsConfig.cs
│ ├── BulkUpload.cs
│ ├── BulkUploadAutodeskMirror.cs
│ ├── BulkUploadAutodeskMirrorFile.cs
│ ├── BulkUploadFile.cs
│ ├── BulkUploadPreset.cs
│ └── Settings.cs
├── Bulk-Uploader-FrontEnd
├── AppSettings.cs
├── Bulk-File-Manager.csproj
├── Bulk-Uploader-FrontEnd.sln
├── ClientApp
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ └── autodesk-logo-blk.svg
│ ├── src
│ │ ├── App.css
│ │ ├── App.tsx
│ │ ├── Components
│ │ │ ├── AuthenticationButton.tsx
│ │ │ ├── BatchStatus.tsx
│ │ │ ├── BatchUiEntry.tsx
│ │ │ ├── BrowseCloudLocation.tsx
│ │ │ ├── ColoredStatusButton.tsx
│ │ │ ├── FolderSelector.tsx
│ │ │ ├── Layout.tsx
│ │ │ ├── Loading.tsx
│ │ │ ├── ResponsiveAppBar.tsx
│ │ │ ├── SimpleDate.tsx
│ │ │ └── projectTree.tsx
│ │ ├── Images
│ │ │ └── AutodeskIcon.tsx
│ │ ├── Models
│ │ │ ├── BatchEntry.ts
│ │ │ ├── BatchJob.ts
│ │ │ ├── BatchProcessPayload.ts
│ │ │ └── ProjectDownload.ts
│ │ ├── Pages
│ │ │ ├── Batches
│ │ │ │ └── BatchLists.tsx
│ │ │ ├── BulkDownloads
│ │ │ │ ├── BulkDownloadSingleHeader.tsx
│ │ │ │ ├── DownloadJobFileList.tsx
│ │ │ │ ├── DownloadSingle.tsx
│ │ │ │ ├── DownloadSingleStatus.tsx
│ │ │ │ ├── DownloadsCreate.tsx
│ │ │ │ └── DownloadsList.tsx
│ │ │ ├── BulkUploads
│ │ │ │ ├── BulkUploadCreate.tsx
│ │ │ │ ├── BulkUploadList.tsx
│ │ │ │ ├── BulkUploadSingle.tsx
│ │ │ │ ├── BulkUploadSingleHeader.tsx
│ │ │ │ └── BulkUploadSingleStatus.tsx
│ │ │ ├── InitialSetupPage.tsx
│ │ │ ├── LoadingPage.tsx
│ │ │ ├── SelectionsPage.tsx
│ │ │ ├── SettingsPage.tsx
│ │ │ └── UtilitiesPage.tsx
│ │ ├── Utilities
│ │ │ ├── Hooks
│ │ │ │ ├── useBatches.ts
│ │ │ │ ├── useBulkDownload.ts
│ │ │ │ ├── useBulkDownloadStatus.ts
│ │ │ │ ├── useBulkDownloads.ts
│ │ │ │ ├── useBulkUpload.ts
│ │ │ │ ├── useBulkUploadPresets.ts
│ │ │ │ ├── useBulkUploadStatus.ts
│ │ │ │ ├── useBulkUploads.ts
│ │ │ │ ├── useDataManagement.ts
│ │ │ │ ├── useMessageListener.tsx
│ │ │ │ ├── useScripting.ts
│ │ │ │ ├── useSettings.ts
│ │ │ │ └── useThreeLegged.ts
│ │ │ ├── RenderTree.ts
│ │ │ └── SimpleREST.ts
│ │ ├── assets
│ │ │ └── react.svg
│ │ ├── index.css
│ │ ├── main.tsx
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── Controllers
│ ├── BatchProcessingController.cs
│ ├── BulkDownloadController.cs
│ ├── BulkUploadController.cs
│ ├── BulkUploadPresetController.cs
│ ├── DataManagementController.cs
│ ├── ScriptingController.cs
│ ├── SettingsController.cs
│ └── UtilityControllers.cs
├── Excel
│ └── ExcelUtils.cs
├── Flows
│ ├── BatchFlows.cs
│ └── UtilityFlows.cs
├── HangfireJobs.cs
├── Helpers
│ ├── APSClientHelper.cs
│ ├── APSHelpers.cs
│ ├── CreateSelfSignedCertificate.cs
│ ├── FileMetricsHelper.cs
│ ├── FolderHelpers.cs
│ ├── JointTokenManager.cs
│ └── TwoLeggedTokenManager.cs
├── Jobs
│ └── DownloaderHangfireJobs.cs
├── Models
│ ├── Account.cs
│ ├── BusinessUnit.cs
│ ├── ErrorMessage.cs
│ ├── Project.cs
│ ├── SimpleFile.cs
│ └── SimpleFolder.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Resources
│ └── autodesk_icon.ico
├── Startup.cs
├── Utils
│ ├── BatchJobController.cs
│ ├── BatchManager.cs
│ ├── BatchRegistry.cs
│ ├── FlurlSettings.cs
│ ├── MappingProfiles.cs
│ ├── PluginManager.cs
│ ├── PollyService.cs
│ └── SettingsProvider.cs
├── Views
│ ├── MainForm.Designer.cs
│ ├── MainForm.cs
│ └── MainForm.resx
├── appsettings.Development.json
├── appsettings.json
├── electron.manifest.json
└── wwwroot
│ ├── css
│ └── site.css
│ ├── favicon.ico
│ ├── js
│ └── site.js
│ └── lib
│ ├── bootstrap
│ ├── LICENSE
│ └── dist
│ │ ├── css
│ │ ├── bootstrap-grid.css
│ │ ├── bootstrap-grid.css.map
│ │ ├── bootstrap-grid.min.css
│ │ ├── bootstrap-grid.min.css.map
│ │ ├── bootstrap-grid.rtl.css
│ │ ├── bootstrap-grid.rtl.css.map
│ │ ├── bootstrap-grid.rtl.min.css
│ │ ├── bootstrap-grid.rtl.min.css.map
│ │ ├── bootstrap-reboot.css
│ │ ├── bootstrap-reboot.css.map
│ │ ├── bootstrap-reboot.min.css
│ │ ├── bootstrap-reboot.min.css.map
│ │ ├── bootstrap-reboot.rtl.css
│ │ ├── bootstrap-reboot.rtl.css.map
│ │ ├── bootstrap-reboot.rtl.min.css
│ │ ├── bootstrap-reboot.rtl.min.css.map
│ │ ├── bootstrap-utilities.css
│ │ ├── bootstrap-utilities.css.map
│ │ ├── bootstrap-utilities.min.css
│ │ ├── bootstrap-utilities.min.css.map
│ │ ├── bootstrap-utilities.rtl.css
│ │ ├── bootstrap-utilities.rtl.css.map
│ │ ├── bootstrap-utilities.rtl.min.css
│ │ ├── bootstrap-utilities.rtl.min.css.map
│ │ ├── bootstrap.css
│ │ ├── bootstrap.css.map
│ │ ├── bootstrap.min.css
│ │ ├── bootstrap.min.css.map
│ │ ├── bootstrap.rtl.css
│ │ ├── bootstrap.rtl.css.map
│ │ ├── bootstrap.rtl.min.css
│ │ └── bootstrap.rtl.min.css.map
│ │ └── js
│ │ ├── bootstrap.bundle.js
│ │ ├── bootstrap.bundle.js.map
│ │ ├── bootstrap.bundle.min.js
│ │ ├── bootstrap.bundle.min.js.map
│ │ ├── bootstrap.esm.js
│ │ ├── bootstrap.esm.js.map
│ │ ├── bootstrap.esm.min.js
│ │ ├── bootstrap.esm.min.js.map
│ │ ├── bootstrap.js
│ │ ├── bootstrap.js.map
│ │ ├── bootstrap.min.js
│ │ └── bootstrap.min.js.map
│ ├── jquery-validation-unobtrusive
│ ├── LICENSE.txt
│ ├── jquery.validate.unobtrusive.js
│ └── jquery.validate.unobtrusive.min.js
│ ├── jquery-validation
│ ├── LICENSE.md
│ └── dist
│ │ ├── additional-methods.js
│ │ ├── additional-methods.min.js
│ │ ├── jquery.validate.js
│ │ └── jquery.validate.min.js
│ └── jquery
│ ├── LICENSE.txt
│ └── dist
│ ├── jquery.js
│ ├── jquery.min.js
│ └── jquery.min.map
├── BulkUploaderUtils
├── AppSettings.cs
├── BulkUploaderUtils.csproj
├── Excel
│ └── ExcelUtils.cs
├── Flows
│ ├── DownloadFlows.cs
│ └── UtilityFlows.cs
├── Helpers
│ ├── CreateSelfSignedCertificate.cs
│ ├── FileMetricsHelper.cs
│ ├── FolderHelpers.cs
│ ├── ForgeHelpers.cs
│ ├── HubHelper.cs
│ └── TokenManager.cs
├── Models
│ ├── Account.cs
│ ├── BusinessUnit.cs
│ ├── ErrorMessage.cs
│ ├── FolderPermission.cs
│ ├── Forge
│ │ ├── ForgeAttributesBatchGet.cs
│ │ ├── ForgeBim360Project.cs
│ │ ├── ForgeBusinessUnits.cs
│ │ ├── ForgeCheckPermissionResponse.cs
│ │ ├── ForgeCreateFolder.cs
│ │ ├── ForgeFirstVersionResponse.cs
│ │ ├── ForgeFolderContents.cs
│ │ ├── ForgeGetDataHooksResponse.cs
│ │ ├── ForgeHub.cs
│ │ ├── ForgeItemTipResponse.cs
│ │ ├── ForgeMetadata.cs
│ │ ├── ForgeMetadataProperties.cs
│ │ ├── ForgeProjectUsers.cs
│ │ ├── ForgeProjects.cs
│ │ ├── ForgeS3UploadCompletion.cs
│ │ ├── ForgeSignedS3Upload.cs
│ │ ├── ForgeSignedS3Url.cs
│ │ ├── ForgeStorageCreation.cs
│ │ ├── ForgeSupportedFormats.cs
│ │ ├── ForgeTopFolders.cs
│ │ ├── ForgeUserInformation.cs
│ │ ├── ForgeVersions.cs
│ │ ├── ForgeWebhookBody.cs
│ │ └── ForgeWebhookResponse.cs
│ ├── ForgeBatchS3Download.cs
│ ├── Project.cs
│ ├── SimpleFile.cs
│ ├── SimpleFolder.cs
│ ├── SimpleHook.cs
│ └── ThreeLeggedToken.cs
└── Utils
│ ├── FlurlSettings.cs
│ ├── MappingProfiles.cs
│ └── PollyService.cs
├── Documentation
├── 20230817-Upload-Download-Coordination.drawio
├── BalsamiqWireframes.bmpr
├── Readme_img.png
├── Thumnail1.gif
├── Thumnail_bulk.gif
├── User_guide_0.png
├── User_guide_1.png
├── User_guide_2.png
├── User_guide_3.png
├── User_guide_4.png
├── User_guide_5.png
├── User_guide_6.png
├── Wireframes.pdf
├── developer-guide.md
└── user-guide.md
├── LICENSE
├── License.md
├── MigrationPlugin
├── Batches
│ ├── ApsDownloadBatch.cs
│ ├── ApsMigrateBatch.cs
│ └── ApsUploadBatch.cs
├── Controllers
│ ├── BatchBasicMigrationController.cs
│ ├── BatchDownloadController.cs
│ ├── BatchUploadController.cs
│ └── MigrationPluginControllers.cs
├── Excel
│ └── ExcelUtils.cs
├── MigrationPlugin.csproj
├── Models
│ ├── BatchJob.cs
│ └── IBatchRegistry.cs
└── Plugin
│ └── MigrationPluginObj.cs
├── PackageApplication.ps1
├── PluginBase
├── ISettingsProvider.cs
├── Models
│ ├── BatchDetails.cs
│ ├── BatchJob.cs
│ ├── BatchProcessPayload.cs
│ ├── BatchProcessResults.cs
│ ├── IBatchRegistry.cs
│ ├── IJobExecutionPlugin.cs
│ ├── JobTaskAggregator.cs
│ ├── StepHistory.cs
│ └── TaskStep.cs
└── PluginBase.csproj
├── README.md
├── TestPlugin
├── Batches
│ └── TestDownloadBatch.cs
├── Controllers
│ └── TestBatchController.cs
├── ExelUtils
│ └── ExcelUtils.cs
├── Models
│ └── TestDownload.cs
├── Plugin
│ └── TestPluginObj.cs
└── TestPlugin.csproj
└── bulk-file-manager.sln
/Ac.Net.Authentication/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | obj
--------------------------------------------------------------------------------
/Ac.Net.Authentication/APSClientHelper.cs:
--------------------------------------------------------------------------------
1 | using Autodesk.Authentication;
2 | using Autodesk.SDKManager;
3 |
4 | namespace Ac.Net.Authentication
5 | {
6 | internal class APSClientHelper
7 | {
8 | internal static SDKManager SdkManager => SdkManagerBuilder.Create().Build();
9 | internal static AuthenticationClient AuthClient => new AuthenticationClient(SdkManager);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Ac.Net.Authentication/Ac.Net.Authentication.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | true
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 | all
14 | runtime; build; native; contentfiles; analyzers; buildtransitive
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Ac.Net.Authentication/EncryptionHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Security.Cryptography;
5 | using System.Text;
6 |
7 | namespace Ac.Net.Authentication
8 | {
9 | public static class EncryptionHelper
10 | {
11 | private static readonly string key = "sshsroshelpdotnetmania2422key1d3";
12 |
13 | public static string Encrypt(string text)
14 | {
15 | byte[] iv = new byte[16];
16 | byte[] array;
17 | using (Aes aes = Aes.Create())
18 | {
19 | aes.Key = Encoding.UTF8.GetBytes(key);
20 | aes.IV = iv;
21 | ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
22 | using (MemoryStream ms = new MemoryStream())
23 | {
24 | using (CryptoStream cryptoStream = new CryptoStream((Stream)ms, encryptor, CryptoStreamMode.Write))
25 | {
26 | using (StreamWriter streamWriter = new StreamWriter((Stream)cryptoStream))
27 | {
28 | streamWriter.Write(text);
29 | }
30 |
31 | array = ms.ToArray();
32 | }
33 | }
34 | }
35 | return Convert.ToBase64String(array);
36 | }
37 |
38 | public static string Decrypt(string text)
39 | {
40 | byte[] iv = new byte[16];
41 | byte[] buffer = Convert.FromBase64String(text);
42 | using (Aes aes = Aes.Create())
43 | {
44 | aes.Key = Encoding.UTF8.GetBytes(key);
45 | aes.IV = iv;
46 | ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
47 | using (MemoryStream ms = new MemoryStream(buffer))
48 | {
49 | using (CryptoStream cryptoStream = new CryptoStream((Stream)ms, decryptor, CryptoStreamMode.Read))
50 | {
51 | using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
52 | {
53 | return streamReader.ReadToEnd();
54 | }
55 | }
56 | }
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Ac.Net.Authentication/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Ac.Net.Authentication/Html.cs:
--------------------------------------------------------------------------------
1 | namespace Ac.Net.Authentication
2 | {
3 | internal class Html
4 | {
5 | internal static string successHtml = @"
6 |
7 |
Online HTML Editor
8 |
29 |
30 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bulk-file-manager",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "start": "vite",
8 | "dev": "vite",
9 | "build": "tsc && vite build",
10 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
11 | "preview": "vite preview"
12 | },
13 | "dependencies": {
14 | "@emotion/react": "^11.11.1",
15 | "@emotion/styled": "^11.11.0",
16 | "@mui/icons-material": "^5.14.3",
17 | "@mui/lab": "^5.0.0-alpha.139",
18 | "@mui/material": "^5.14.3",
19 | "@mui/x-data-grid": "^6.11.0",
20 | "@tanstack/react-query": "^4.32.1",
21 | "@tanstack/react-query-devtools": "^4.32.6",
22 | "@types/react-router": "^5.1.20",
23 | "@types/react-router-dom": "^5.3.3",
24 | "@uidotdev/usehooks": "^2.4.1",
25 | "axios": "^1.4.0",
26 | "mobx-react": "^9.0.0",
27 | "react": "^18.2.0",
28 | "react-dom": "^18.2.0",
29 | "react-router": "^6.14.2",
30 | "react-router-dom": "^6.14.2",
31 | "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.0/xlsx-0.20.0.tgz"
32 | },
33 | "devDependencies": {
34 | "@types/react": "^18.2.15",
35 | "@types/react-dom": "^18.2.7",
36 | "@typescript-eslint/eslint-plugin": "^6.0.0",
37 | "@typescript-eslint/parser": "^6.0.0",
38 | "@vitejs/plugin-react": "^4.0.3",
39 | "eslint": "^8.45.0",
40 | "eslint-plugin-react-hooks": "^4.6.0",
41 | "eslint-plugin-react-refresh": "^0.4.3",
42 | "typescript": "^5.0.2",
43 | "vite": "^4.4.5",
44 | "vite-plugin-mkcert": "^1.15.0"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/public/autodesk-logo-blk.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | width: 100vw;
3 | height: 100vh;
4 | }
5 |
6 | main {
7 | padding: 1em;
8 | }
9 |
10 | code{
11 | color: #e22bca;
12 | white-space: pre-wrap;
13 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/App.tsx:
--------------------------------------------------------------------------------
1 | import './App.css'
2 | import {useSettings} from "./Utilities/Hooks/useSettings.ts";
3 | import {Navigate, Route, Routes, useLocation} from "react-router";
4 | import {BulkUploadCreate} from "./Pages/BulkUploads/BulkUploadCreate.tsx";
5 | import { SettingsPage } from "./Pages/SettingsPage.tsx";
6 | import { BulkUploadList } from "./Pages/BulkUploads/BulkUploadList.tsx";
7 | import {LoadingPage} from "./Pages/LoadingPage.tsx";
8 | import {Layout} from "./Components/Layout.tsx";
9 | import {BulkUploadSingle} from "./Pages/BulkUploads/BulkUploadSingle.tsx";
10 | import {DownloadSingle} from "./Pages/BulkDownloads/DownloadSingle";
11 | import {BulkUploadSingleStatus} from "./Pages/BulkUploads/BulkUploadSingleStatus.tsx";
12 | import { UtilitiesPage } from './Pages/UtilitiesPage.tsx';
13 | import { SelectionsPage } from './Pages/SelectionsPage.tsx';
14 | import {InitialSetupPage} from "./Pages/InitialSetupPage";
15 | import { BatchList } from './Pages/Batches/BatchLists.tsx';
16 | import {DownloadsCreate} from "./Pages/BulkDownloads/DownloadsCreate";
17 | import {DownloadsList} from "./Pages/BulkDownloads/DownloadsList";
18 | import {DownloadSingleStatus} from "./Pages/BulkDownloads/DownloadSingleStatus";
19 | import {useIsAuthenticated, useThreeLegged} from "./Utilities/Hooks/useThreeLegged.ts";
20 |
21 | export default function App() {
22 | const {dataHash: settingsHash, isLoading} = useSettings();
23 | const {isLoading: threeLeggedLoading} = useThreeLegged();
24 | const {isAuthenticated, isLoading: isAuthenticatedLoading} = useIsAuthenticated();
25 |
26 | if (isLoading || threeLeggedLoading) return
27 | if (!settingsHash['ClientId']?.value || !settingsHash['ClientSecret']?.value) return
28 |
29 | return (
30 |
31 | }>
32 | }/>
33 | }/>
34 | }/>
35 | }/>
36 | }/>
37 | }/>
38 | }/>
39 | }/>
40 | }/>
41 | }/>
42 | }/>
43 | }/>
44 |
45 | }/>
46 |
47 | )
48 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Components/AuthenticationButton.tsx:
--------------------------------------------------------------------------------
1 | import {Button, ButtonGroup} from "@mui/material";
2 | import {useIsAuthenticated, useThreeLegged} from "../Utilities/Hooks/useThreeLegged.ts";
3 | import IconButton from "@mui/material/IconButton";
4 | import {Sync} from "@mui/icons-material";
5 | import {useNavigate} from "react-router";
6 | import React from "react";
7 | import Box from "@mui/material/Box";
8 |
9 |
10 |
11 | export function AuthenticationButton() {
12 | const {isAuthenticated} = useIsAuthenticated();
13 | const {authUrl, logout} = useThreeLegged();
14 | const navigate = useNavigate();
15 |
16 | function login(e: React.SyntheticEvent) {
17 | e.preventDefault();
18 |
19 | // http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
20 | // Fixes dual-screen position Most browsers Firefox
21 | const w = 600;
22 | const h = 800;
23 |
24 | const dualScreenLeft = window.screenLeft != undefined ? window.screenLeft : 0;
25 | const dualScreenTop = window.screenTop != undefined ? window.screenTop : 0;
26 |
27 | const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
28 | const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;
29 |
30 | const left = ((width / 2) - (w / 2)) + dualScreenLeft;
31 | const top = ((height / 2) - (h / 2)) + dualScreenTop;
32 | const newWindow = window.open(authUrl, '_blank', 'scrollbars=yes, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left);
33 |
34 | newWindow?.focus();
35 | }
36 |
37 | return (
38 |
39 |
44 |
45 |
50 |
51 |
52 | window.location.reload()}/>
53 |
54 | )
55 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Components/BatchStatus.tsx:
--------------------------------------------------------------------------------
1 | import Button from "@mui/material/Button";
2 | import { BatchJob } from "../Models/BatchJob";
3 | import { BatchProcessPayload } from "../Models/BatchProcessPayload";
4 | import axios, { Axios } from "axios";
5 |
6 |
7 | export function BatchStatus({ batchJob }: {
8 | batchJob: BatchJob;
9 |
10 | }) {
11 | const payload = new BatchProcessPayload(batchJob.id, batchJob.type, "queued", "Queue Jobs");
12 | let btncolor: "primary" | "info" | "secondary" = "primary";
13 | let complete = false;
14 | if (batchJob.queued == null){
15 | //
16 | } else if (batchJob.started === null) {
17 | payload.buttonLabel= "Start";
18 | payload.action = "start";
19 | btncolor = "secondary";
20 | } else if (batchJob.completed !== null) {
21 | payload.buttonLabel = "";
22 | payload.action = "";
23 | btncolor = "info";
24 | }
25 | return (
26 |
27 | {(!complete) ?
28 | : null}
37 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Components/BrowseCloudLocation.tsx:
--------------------------------------------------------------------------------
1 | import Button from "@mui/material/Button";
2 | import React, {useState} from "react";
3 | import {Dialog} from "@mui/material";
4 | import {ICloudFolderProps, ProjectTree} from "./projectTree.tsx";
5 |
6 | export function BrowseCloudLocation({onFolderChange, type}: {
7 | type: 'upload' | 'download';
8 | onFolderChange: ({hubId, projectId, folderId, folderPath}: ICloudFolderProps) => void}) {
9 | const [openDiag, setOpenDiag] = useState(false);
10 |
11 | return (
12 | <>
13 |
18 |
19 |
30 | >
31 | )
32 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Components/ColoredStatusButton.tsx:
--------------------------------------------------------------------------------
1 | import Button from "@mui/material/Button";
2 | import { NavLink } from "react-router-dom";
3 |
4 |
5 | export function ColoredStatusButton({ color, count, path, title }: {
6 | color: "inherit" | "primary" | "secondary" | "success" | "error" | "info" | "warning",
7 | count?: number,
8 | path: string,
9 | title?: string
10 | }) {
11 | return (
12 |
22 | )
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Components/Layout.tsx:
--------------------------------------------------------------------------------
1 | import {Outlet} from "react-router";
2 | import ResponsiveAppBar from "./ResponsiveAppBar.tsx";
3 |
4 | export const Layout = ()=>{
5 | return <>
6 |
7 |
8 |
9 |
10 |
11 | >
12 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Components/Loading.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import CircularProgress from "@mui/material/CircularProgress";
3 | import "../App.css";
4 | import { observer } from "mobx-react";
5 |
6 | export interface LoadingProps {
7 | size?: number;
8 | }
9 | const Loading = observer((props: LoadingProps) => {
10 | return (
11 | <>
12 |
13 | >
14 | );
15 | });
16 |
17 | export default Loading;
18 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Components/SimpleDate.tsx:
--------------------------------------------------------------------------------
1 |
2 |
3 | export const SimpleDate = ({date}: {date: string})=>{
4 | if(!date) return <>--->
5 | const dateString = new Date(date)?.toLocaleString();
6 | return (
7 | {dateString}
8 |
)
9 | }
10 |
11 | export const SimpleDate2 = ({date}: {date: string})=>{
12 | if(!date) return <>--->
13 | const d = new Date(date);
14 | const dstr = [
15 |
16 | ('0' + (d.getMonth() + 1)).slice(-2),
17 | ('0' + d.getDate()).slice(-2),
18 | d.getFullYear().toString().slice(-2)
19 | ].join('.') + " " + [
20 | d.getHours().toString().slice(-2),
21 | ('0' + d.getMinutes()).slice(-2)
22 | ].join(':')
23 |
24 |
25 | return (
26 | {dstr}
27 |
)
28 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Images/AutodeskIcon.tsx:
--------------------------------------------------------------------------------
1 | import {SvgIcon, SvgIconProps} from "@mui/material";
2 |
3 | export const AutodeskIcon = (params: SvgIconProps) => {
4 |
5 | return (
6 |
7 |
23 |
24 | )
25 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Models/BatchEntry.ts:
--------------------------------------------------------------------------------
1 | export class BatchEntry
2 | {
3 | title: string = "";
4 | key: string = "";
5 | description: string = "";
6 | batchLoadUrl: string = "";
7 | batchTemplateUrl: string = "";
8 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Models/BatchJob.ts:
--------------------------------------------------------------------------------
1 | export interface BatchJob {
2 | id: number;
3 | name: string;
4 | type: string;
5 | errors: string | null;
6 | jobCount: number;
7 | data: string | null;
8 | created: string | null;
9 | queued: string | null;
10 | started: string | null;
11 | completed: string | null;
12 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Models/BatchProcessPayload.ts:
--------------------------------------------------------------------------------
1 | export class BatchProcessPayload {
2 |
3 |
4 | constructor (dbId: number = 0, batchType: string= "", action: string = "", buttonLabel: string = "")
5 | {
6 | this.dbId = dbId;
7 | this.batchType = batchType;
8 | this.buttonLabel = buttonLabel;
9 | this.action = action;
10 | }
11 |
12 | dbId: number;
13 | batchType: string ;
14 | action: string;
15 | buttonLabel: string ;
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Models/ProjectDownload.ts:
--------------------------------------------------------------------------------
1 | export class ProjectDownload {
2 | constructor (sourceHub: string, sourceProject: string, downloadFolder: string, includeProject: boolean, ignoreExtensions: string, ignoreFolders: string)
3 | {
4 | this.sourceHub = sourceHub;
5 | this.sourceProject = sourceProject;
6 | this.downloadFolder = downloadFolder;
7 | this.includeProject = includeProject;
8 | this.ignoreExtensions = ignoreExtensions;
9 | this.ignoreFolders = ignoreFolders;
10 | }
11 |
12 | sourceHub: string;
13 | sourceProject: string;
14 | includeProject: boolean;
15 | downloadFolder: string;
16 | ignoreExtensions: string | null;
17 | ignoreFolders: string | null;
18 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Pages/Batches/BatchLists.tsx:
--------------------------------------------------------------------------------
1 |
2 | import {DataGrid, GridColDef} from "@mui/x-data-grid";
3 | import {useNavigate} from "react-router";
4 | import IconButton from "@mui/material/IconButton";
5 | import SyncIcon from "@mui/icons-material/Sync";
6 | import {SimpleDate2} from "../../Components/SimpleDate.tsx";
7 | import {useState} from "react";
8 | import {Button, Container} from "@mui/material";
9 | import { useBatches } from "../../Utilities/Hooks/useBatches.ts";
10 | import { BatchJob } from "../../Models/BatchJob.ts";
11 | import { BatchStatus } from "../../Components/BatchStatus.tsx";
12 |
13 | export const BatchList = () => {
14 | const navigate = useNavigate()
15 | const {data: batches, isLoading, refresh} = useBatches();
16 | const [selected, setSelected] = useState([])
17 |
18 |
19 |
20 | const columns: GridColDef[] = [
21 | {field: 'name', headerName: 'Name', flex: 2},
22 | {field: 'type', headerName: 'Type', flex: 1},
23 | {field: 'jobCount ', headerName: 'Task Count', flex: 1},
24 | {field: 'created', headerName: 'Created On', flex: 1, renderCell: ({value})=>()},
25 | {field: 'queued', headerName: 'Queued On', flex: 1, renderCell: ({value})=>()},
26 | {field: 'started', headerName: 'Started On', flex: 1, renderCell: ({value})=>()},
27 | {field: 'completed', headerName: 'Completed On', flex: 1, renderCell: ({value})=>()},
28 |
29 | {
30 | field: 'id', headerName: 'Action', width: 200, align: "right",
31 | renderCell: ({row}) => (
32 |
33 | )
34 | },
35 |
36 | ]
37 |
38 | return <>
39 |
40 |
Active Batches
41 |
42 | {!!selected.length &&
43 | }
44 |
45 |
46 |
47 |
48 |
49 |
50 | setSelected(rowSelectionModel as number[])}
59 | />
60 | >
61 | }
62 |
63 |
64 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Pages/BulkDownloads/BulkDownloadSingleHeader.tsx:
--------------------------------------------------------------------------------
1 | import {NavLink} from "react-router-dom";
2 | import {ColoredStatusButton} from "../../Components/ColoredStatusButton.tsx";
3 | import IconButton from "@mui/material/IconButton";
4 | import SyncIcon from "@mui/icons-material/Sync";
5 | import {LinearProgress} from "@mui/material";
6 | import {IBulkDownload} from "../../Utilities/Hooks/useBulkDownloads.ts";
7 |
8 | export const BulkDownloadSingleHeader = ({bulkDownload, refresh}: { bulkDownload?: IBulkDownload, refresh?: () => void }) => {
9 |
10 | console.log(bulkDownload);
11 |
12 | return (
13 | <>
14 | {bulkDownload ?
15 |
16 | Bulk Downloads
17 | :
18 | {bulkDownload?.name}
19 |
20 |
{bulkDownload?.status}
21 |
22 |
23 |
24 |
30 |
35 |
41 |
47 | {refresh &&
48 |
49 | }
50 |
51 |
: }
52 | >
53 | )
54 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Pages/BulkDownloads/DownloadJobFileList.tsx:
--------------------------------------------------------------------------------
1 | import {DataGrid, GridColDef} from "@mui/x-data-grid";
2 | import {IBulkDownload, IBulkDownloadFile} from "../../Utilities/Hooks/useBulkDownloads";
3 | import {bytesToSize} from "./DownloadSingleStatus";
4 |
5 | const columns: GridColDef[] = [
6 | {field: 'fileName', headerName: 'File Name', flex:1},
7 | {field: 'sourceFilePath', headerName: 'Source File Path', flex:1},
8 | {field: 'destinationFilePath', headerName: 'Download Path', flex:1},
9 | {field: 'fileSize', headerName: 'File Size', sortable: false, width:100, type: 'number',
10 | renderCell: ({row})=>bytesToSize(row.fileSize)},
11 | {field: 'status', headerName: 'Status', width:80},
12 | ]
13 |
14 | export const DownloadJobFileList = ({download}: {download: IBulkDownload}) => {
15 | return (
16 |
17 | )
18 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Pages/BulkUploads/BulkUploadSingleHeader.tsx:
--------------------------------------------------------------------------------
1 | import {IBulkUpload} from "../../Utilities/Hooks/useBulkUploads.ts";
2 | import {NavLink} from "react-router-dom";
3 | import {ColoredStatusButton} from "../../Components/ColoredStatusButton.tsx";
4 | import IconButton from "@mui/material/IconButton";
5 | import SyncIcon from "@mui/icons-material/Sync";
6 | import {LinearProgress} from "@mui/material";
7 |
8 | export const BulkUploadSingleHeader = ({bulkUpload, refresh}: { bulkUpload?: IBulkUpload, refresh?: () => void }) => {
9 |
10 | return (
11 | <>
12 | {bulkUpload ?
13 |
14 | Bulk Uploads
15 | :
16 | {bulkUpload?.name}
18 |
19 |
{bulkUpload?.status}
20 |
21 |
22 |
23 |
29 |
35 |
41 |
47 |
53 | {refresh &&
54 |
55 | }
56 |
57 |
: }
58 | >
59 | )
60 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Pages/LoadingPage.tsx:
--------------------------------------------------------------------------------
1 | import {CircularProgress} from "@mui/material";
2 |
3 | export const LoadingPage = ()=>{
4 | return (
5 |
6 |
7 |
8 | )
9 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Pages/SelectionsPage.tsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import { BatchUiEntry } from "../Components/BatchUiEntry";
3 | import { BatchEntry } from "../Models/BatchEntry";
4 | import { SimpleGet } from "../Utilities/SimpleREST";
5 |
6 |
7 |
8 |
9 | export const SelectionsPage = () => {
10 |
11 |
12 | const { data, isLoading, isFetching } = useQuery({
13 | queryKey: ['batch'],
14 | queryFn: () => SimpleGet(`/api/batch`)
15 | })
16 |
17 | if (isLoading) {
18 | return Loading...
19 | }
20 |
21 | if (isFetching) {
22 | return Loading...
23 | }
24 |
25 | // const items: ReadonlyArray [...data]
26 | return (
27 | (
28 |
29 |
Operation Selections
30 |
31 |
39 |
40 | {data?.map(p => ())}
41 |
42 |
43 |
44 | )
45 |
46 | );
47 | };
48 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useBatches.ts:
--------------------------------------------------------------------------------
1 | import { useQuery, useQueryClient } from "@tanstack/react-query";
2 | import { SimpleGet } from "../SimpleREST.ts";
3 |
4 | import { BatchJob } from "../../Models/BatchJob.ts";
5 |
6 |
7 | export function useBatches() {
8 | const queryClient = useQueryClient();
9 |
10 | const { data, isLoading, isFetching } = useQuery({
11 | queryKey: ['batches'],
12 | queryFn: () => SimpleGet(`/api/batch/active`)
13 | });
14 |
15 | const refresh = () => {
16 | queryClient.invalidateQueries({ queryKey: ['batches'] });
17 | };
18 |
19 |
20 |
21 | return { data, isLoading: isLoading || isFetching, refresh };
22 | }
23 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useBulkDownload.ts:
--------------------------------------------------------------------------------
1 | import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
2 | import {SimpleGet, SimplePost} from "../SimpleREST";
3 | import {IBulkDownload, IBulkDownloadFile} from "./useBulkDownloads";
4 |
5 |
6 | export function useBulkDownload(bulkDownloadId: number | string){
7 | const queryClient = useQueryClient();
8 |
9 | const {data, isLoading, isFetching} = useQuery({
10 | queryKey: ['bulkDownload', {bulkDownloadId}] as [string, {bulkDownloadId: string | number}],
11 | queryFn: ({queryKey: [, {bulkDownloadId}]}) => {
12 | return SimpleGet(`/api/downloads/${bulkDownloadId}`)
13 | },
14 | refetchInterval: 15000
15 | })
16 |
17 | const refresh = ()=>{
18 | queryClient.invalidateQueries({queryKey: ['bulkDownload', {bulkDownloadId}]})
19 | queryClient.invalidateQueries({queryKey: ['bulkDownloads']})
20 | }
21 |
22 | const mutation = useMutation({
23 | mutationFn: ({command, bulkDownloadId, files}: {command: 'repeatFiles', bulkDownloadId: number, files?: Partial[]})=>{
24 | switch(command){
25 | case 'repeatFiles': return SimplePost(`/api/bulkDownloads/${bulkDownloadId}/bulkDownloadFiles`, files);
26 | }
27 | },
28 | onSuccess: refresh
29 | })
30 |
31 |
32 | return {data, isLoading: isLoading || isFetching, refresh, mutation}
33 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useBulkDownloadStatus.ts:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import { SimpleGet } from "../SimpleREST.ts";
3 | import {IBulkDownloadFile} from "./useBulkDownloads.ts";
4 | export function useBulkDownloadStatus({bulkDownloadId, status, count, skip}: {count?: number, skip?: number, bulkDownloadId: number | string, status: string}){
5 |
6 | const {data, isLoading, isFetching, refetch} = useQuery({
7 | queryKey: [`downloads/singleStatus`, {bulkDownloadId, status, count, skip}] as [string, {count?: number, skip?: number, bulkDownloadId: string | number, status: string}],
8 | queryFn: () => SimpleGet(`/api/downloads/${bulkDownloadId}/status/${status}`, {params: {count, skip}} ),
9 | cacheTime: 15000,
10 | initialData: [],
11 | refetchOnMount: true
12 | })
13 |
14 | return {data, isLoading: isLoading || isFetching, refetch}
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useBulkDownloads.ts:
--------------------------------------------------------------------------------
1 | import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
2 | import {SimpleDelete, SimpleGet, SimplePatch, SimplePost} from "../SimpleREST";
3 | import {IBulkUpload} from "./useBulkUploads";
4 |
5 | export interface IBulkDownload {
6 | id: number,
7 | name: string,
8 | cloudPath: string,
9 | localPath: string,
10 | hubId: string,
11 | projectId: string,
12 | apsFolderId: string,
13 | logs?:string,
14 | status?:string,
15 | pendingDownloadCount?:number,
16 | inProgressDownloadCount?:number,
17 | successDownloadCount?:number,
18 | failedDownloadCount?:number,
19 | files?:IBulkDownloadFile[],
20 | createdAt: Date,
21 | }
22 |
23 | export interface IBulkDownloadFile {
24 | id: number,
25 | bulkDownloadId: number,
26 | fileName: string,
27 | sourceFilePath: string,
28 | destinationFilePath: string,
29 | itemId: string,
30 | fileSize: number,
31 | downloadFileStatus: string,
32 | status?:string,
33 | logs: string,
34 | createdOn: Date,
35 | lastModified: Date,
36 | }
37 |
38 | export function useBulkDownloads() {
39 | const queryClient = useQueryClient();
40 |
41 | const {data, isLoading, isFetching} = useQuery({
42 | queryKey: ['bulkDownloads'],
43 | queryFn: () => SimpleGet(`/api/bulkDownloads`),
44 | refetchInterval: 15000
45 | })
46 |
47 | const refresh = ()=>{
48 | queryClient.invalidateQueries({queryKey: ['bulkDownloads']})
49 | }
50 |
51 | const mutation = useMutation({
52 | mutationFn: ({command, bulkDownloads}: {command: 'patch' | 'post' | 'delete', bulkDownloads?: Partial[]})=>{
53 | switch(command){
54 | case 'post': return SimplePost(`/api/download`, bulkDownloads);
55 | // case 'patch': return SimplePatch(`/api/bulkUploads`, bulkUploads);
56 | case 'delete': return Promise.all(bulkDownloads?.map(bulkDownload=>SimpleDelete(`/api/download?id=${bulkDownload.id}`)) ?? []);
57 | default: return Promise.resolve();
58 | }
59 | },
60 | onSuccess: refresh
61 | })
62 |
63 | return {data, isLoading: isLoading || isFetching, refresh, mutation}
64 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useBulkUpload.ts:
--------------------------------------------------------------------------------
1 | import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
2 | import {SimpleGet, SimplePost} from "../SimpleREST.ts";
3 | import {IBulkUpload, IBulkUploadFile} from "./useBulkUploads.ts";
4 |
5 | export function useBulkUpload(bulkUploadId: number | string){
6 | const queryClient = useQueryClient();
7 |
8 | const {data, isLoading, isFetching} = useQuery({
9 | queryKey: ['bulkUpload', {bulkUploadId}] as [string, {bulkUploadId: string | number}],
10 | queryFn: ({queryKey: [, {bulkUploadId}]}) => {
11 | return SimpleGet(`/api/bulkUploads/${bulkUploadId}` )
12 | },
13 | refetchInterval: 15000
14 | })
15 |
16 | const refresh = ()=>{
17 | queryClient.invalidateQueries({queryKey: ['bulkUpload', {bulkUploadId}]})
18 | queryClient.invalidateQueries({queryKey: ['bulkUploads']})
19 | }
20 |
21 | const filesMutation = useMutation({
22 | mutationFn: ({command, bulkUploadId, files}: {command: 'repeatFiles', bulkUploadId: number, files?: Partial[]})=>{
23 | switch(command){
24 | case 'repeatFiles': return SimplePost(`/api/bulkUploads/${bulkUploadId}/bulkUploadFiles`, files);
25 | }
26 | },
27 | onSuccess: refresh
28 | })
29 |
30 | return {data, isLoading: isLoading || isFetching, refresh, filesMutation}
31 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useBulkUploadPresets.ts:
--------------------------------------------------------------------------------
1 | import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
2 | import {SimpleDelete, SimpleGet, SimplePatch, SimplePost} from "../SimpleREST.ts";
3 |
4 | export interface IBulkUploadPreset{
5 | id: number;
6 | name: string;
7 | excludedFolderNames: string;
8 | excludedFileTypes: string;
9 | modifyPathScript: string;
10 | useModifyPathScript: boolean;
11 | }
12 | export function useBulkUploadPresets(options: any){
13 | const queryClient = useQueryClient();
14 |
15 | const {data, isLoading, isFetching} = useQuery({
16 | ...options,
17 | queryKey: ['bulkUploadPresets'],
18 | queryFn: () => SimpleGet(`/api/settings/bulkUploadPresets` )
19 | })
20 |
21 | const refresh = ()=>{
22 | queryClient.invalidateQueries({queryKey: ['bulkUploadPresets']})
23 | }
24 |
25 | const mutation = useMutation({
26 | mutationFn: ({command, presets}: {command: 'patch' | 'post' | 'delete', presets?: Partial[]})=>{
27 | switch(command){
28 | case 'post': return SimplePost(`/api/settings/bulkUploadPresets`, presets);
29 | case 'patch': return SimplePatch(`/api/settings/bulkUploadPresets`, presets);
30 | case 'delete':
31 | return Promise.all(presets?.map(preset=>SimpleDelete(`/api/settings/bulkUploadPresets/${preset.id}`)) ?? [])
32 | }
33 | },
34 | onSuccess: refresh
35 | })
36 |
37 | return {data, isLoading: isLoading || isFetching, refresh, presetMutation: mutation}
38 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useBulkUploadStatus.ts:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import { SimpleGet } from "../SimpleREST.ts";
3 | import {IBulkUploadFile} from "./useBulkUploads.ts";
4 | export function useBulkUploadStatus({bulkUploadId, status, count, skip}: {count?: number, skip?: number, bulkUploadId: number | string, status: string}){
5 |
6 | const {data, isLoading, isFetching, refetch} = useQuery({
7 | queryKey: [`bulkUploads/singleStatus`, {bulkUploadId, status, count, skip}] as [string, {count?: number, skip?: number, bulkUploadId: string | number, status: string}],
8 | queryFn: () => SimpleGet(`/api/bulkUploads/${bulkUploadId}/status/${status}`, {params: {count, skip}} ),
9 | cacheTime: 15000,
10 | initialData: [],
11 | refetchOnMount: true
12 | })
13 |
14 | return {data, isLoading: isLoading || isFetching, refetch}
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useBulkUploads.ts:
--------------------------------------------------------------------------------
1 | import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
2 | import {SimpleDelete, SimpleGet, SimplePatch, SimplePost} from "../SimpleREST.ts";
3 |
4 | export interface IBulkUpload {
5 | id: number;
6 | name: string;
7 | localPath: string;
8 | projectId: string;
9 | folderId: string;
10 | excludedFolderNames: string;
11 | excludedFileTypes: string;
12 | modifyPathScript: string;
13 | useModifyPathScript: boolean;
14 | startTime?: Date;
15 | endTime?: Date;
16 | status?: string;
17 | logs?: string;
18 | proposedFileCount?: number;
19 | doNotUploadFileCount?: number;
20 | pendingFileCount?: number;
21 | successFileCount?: number;
22 | failedFileCount?: number;
23 | files?: IBulkUploadFile[]
24 | autodeskMirrors?: IBulkUploadAutodeskMirror[]
25 | }
26 |
27 | export interface IBulkUploadAutodeskMirror{
28 | folderName: string;
29 | folderUrl: string;
30 | folderUrn: string;
31 | relativeFolderPath: string;
32 | }
33 |
34 | export interface IBulkUploadFile{
35 | id: number;
36 | bulkUploadId: number;
37 | sourceFileName: string;
38 | sourceAbsolutePath: string;
39 | sourceRelativePath: string;
40 | targetFileName: string;
41 | targetRelativePath: string;
42 | folderUrn: string;
43 | folderUrl: string;
44 | webUrl: string;
45 | versionId: string;
46 | status: string;
47 | logs: string;
48 | createdOn: Date;
49 | lastModified: Date;
50 | }
51 |
52 | export function useBulkUploads(){
53 | const queryClient = useQueryClient();
54 |
55 | const {data, isLoading, isFetching} = useQuery({
56 | queryKey: ['bulkUploads'],
57 | queryFn: () => SimpleGet(`/api/bulkUploads` ),
58 | refetchInterval: 15000
59 | })
60 |
61 | const refresh = ()=>{
62 | queryClient.invalidateQueries({queryKey: ['bulkUploads']})
63 | }
64 |
65 | const mutation = useMutation({
66 | mutationFn: ({command, bulkUploads}: {command: 'patch' | 'post' | 'delete', bulkUploads?: Partial[]})=>{
67 | switch(command){
68 | case 'post': return SimplePost(`/api/bulkUploads`, bulkUploads);
69 | case 'patch': return SimplePatch(`/api/bulkUploads`, bulkUploads);
70 | case 'delete': return Promise.all(bulkUploads?.map(bulkUpload=>SimpleDelete(`/api/bulkUploads/${bulkUpload.id}`)) ?? []);
71 | }
72 | },
73 | onSuccess: refresh
74 | })
75 |
76 | return {data, isLoading: isLoading || isFetching, refresh, mutation}
77 | }
78 |
79 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useDataManagement.ts:
--------------------------------------------------------------------------------
1 | import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
2 | import {SimpleGet, SimplePatch} from "../SimpleREST.ts";
3 | import {useMemo} from "react";
4 | import {Hub} from "@mui/icons-material";
5 |
6 | export interface ISimpleDmResponse {
7 | id: string;
8 | name: string;
9 | }
10 |
11 | export function useHubs(options: any) {
12 | return useQuery({
13 | ...options,
14 | queryKey: ["hubs"],
15 | queryFn: () => SimpleGet(`/api/dm/hubs`)
16 | });
17 | }
18 | export function useProjects(hubId: string | null, options: any) {
19 | return useQuery({
20 | ...options,
21 | queryKey: ["projects", hubId],
22 | queryFn: () => SimpleGet(`/api/dm/projects?hubId=${hubId}`)
23 | })
24 | }
25 |
26 | export function useTopFolders(hubId: string, projectId: string, options: any) {
27 | return useQuery({
28 | ...options,
29 | queryKey: ["topFolders", hubId, projectId],
30 | queryFn: () => SimpleGet(`/api/dm/topFolders?hubId=${hubId}&projectId=${projectId}`)
31 | })
32 | }
33 |
34 | export function useFolderContents(projectId: string, folderId: string, options: any) {
35 | return useQuery({
36 | ...options,
37 | queryKey: ["folderContents", projectId, folderId],
38 | queryFn: () => SimpleGet(`/api/dm/folderContent?projectId=${projectId}&folderId=${folderId}`)
39 | })
40 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useMessageListener.tsx:
--------------------------------------------------------------------------------
1 | import {useEffect, useState} from "react";
2 |
3 | export function useMessageListener(type: string, callback: (key: string)=>void) {
4 | const [data, setData] = useState("")
5 |
6 | useEffect(() => {
7 | if (window) {
8 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment
9 | //@ts-ignore
10 | window.chrome.webview.addEventListener('message', (event) => {
11 | try {
12 | const parsedData =event.data as {type: string, data: string};
13 | if (parsedData.type === type) {
14 | setData(parsedData.data)
15 | if(callback) callback(parsedData.data);
16 | }
17 | } catch (e) {
18 | console.error(e);
19 | }
20 | })
21 | }
22 |
23 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment
24 | //@ts-ignore
25 | return window.chrome.webview.removeEventListener('message', ()=>{})
26 | }, [])
27 |
28 | function sendMessage(message: string) {
29 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment
30 | //@ts-ignore
31 | window.chrome.webview.postMessage(message);
32 | }
33 |
34 | return {data, sendMessage}
35 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useScripting.ts:
--------------------------------------------------------------------------------
1 | import {useMutation} from "@tanstack/react-query";
2 | import {SimplePost} from "../SimpleREST.ts";
3 | import {IBulkUpload, IBulkUploadFile} from "./useBulkUploads.ts";
4 |
5 | export function useScripting(){
6 |
7 | const mutation = useMutation({
8 | mutationFn: ({command, bulkUpload, bulkUploadFile}: {command: 'post', bulkUpload: Partial, bulkUploadFile: Partial})=>{
9 | switch(command){
10 | case 'post': return SimplePost(`/api/scripting/modifyPath`, {
11 | bulkUpload,
12 | bulkUploadFile
13 | });
14 | }
15 | }
16 | })
17 | return {mutation}
18 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useSettings.ts:
--------------------------------------------------------------------------------
1 | import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
2 | import {SimpleGet, SimplePatch} from "../SimpleREST.ts";
3 | import {useMemo} from "react";
4 |
5 | export interface ISetting{
6 | settingId: number;
7 | key: string;
8 | value: string;
9 | }
10 | export function useSettings(){
11 | const queryClient = useQueryClient();
12 |
13 | const {data, isLoading, isFetching} = useQuery({
14 | queryKey: ['settings'],
15 | queryFn: () => SimpleGet(`/api/settings` )
16 | })
17 |
18 | const refresh = ()=>{
19 | queryClient.invalidateQueries({queryKey: ['settings']})
20 | }
21 |
22 | const mutation = useMutation({
23 | mutationFn: ({command, settings}: {command: 'patch', settings?: ISetting[]})=>{
24 | switch(command){
25 | case 'patch': return SimplePatch(`/api/settings`, settings);
26 | }
27 | },
28 | onSuccess: refresh
29 | })
30 |
31 | const dataHash = useMemo(()=>{
32 | return data?.reduce((acc: {[key: string]: ISetting}, val)=>({...acc, [val.key]: val}), {}) ?? {}
33 | }, [data])
34 |
35 | return {data, dataHash, isLoading: isLoading || isFetching, refresh, mutation}
36 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/Hooks/useThreeLegged.ts:
--------------------------------------------------------------------------------
1 | import {SimpleGet, SimplePost, SimpleDelete} from "../SimpleREST.ts";
2 | import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
3 |
4 | //Test this in built application
5 | export function useIsAuthenticated() {
6 | const {data: isAuthenticated, isLoading: isAuthenticatedLoading, refetch} = useQuery({
7 | queryKey: ['threeLegged/isAuthenticated'],
8 | queryFn: () => SimpleGet(`/api/settings/threeLegged/check`),
9 | refetchOnWindowFocus: "always"
10 | });
11 |
12 | return {isAuthenticated, isLoading: isAuthenticatedLoading};
13 | }
14 | export function useThreeLegged() {
15 | const queryClient = useQueryClient();
16 |
17 | // const {data: isAuthenticated, isLoading: isAuthenticatedLoading} = useQuery({
18 | // queryKey: ['threeLegged/isAuthenticated'],
19 | // queryFn: () => SimpleGet(`/api/settings/threeLegged/check`),
20 | // // refetchOnWindowFocus: "always"
21 | // });
22 |
23 |
24 | const {data: authUrl, isLoading: isAuthUrlLoading} = useQuery({
25 | queryKey: ['threeLegged/authUrl'],
26 | queryFn: () => SimpleGet(`/api/settings/threeLegged/getAuthUrl`),
27 | });
28 |
29 | const refresh = () => {
30 | return Promise.all([
31 | queryClient.invalidateQueries({queryKey: ['threeLegged/isAuthenticated']}),
32 | queryClient.invalidateQueries({queryKey: ['threeLegged/authUrl']})
33 | ]);
34 | }
35 | const refreshAuthUrl = () => {
36 | return new Promise((resolve, reject) => {
37 | refresh().then(()=>{
38 | setTimeout(resolve, 1000);
39 | })
40 | })
41 | }
42 |
43 | // const processCode = useMutation({
44 | // mutationFn: ({code}: { code: string }) => {
45 | // debugger;
46 | // return SimplePost(`/api/settings/threeLegged`, {code});
47 | // },
48 | // onSuccess: refresh
49 | // })
50 |
51 | const logout = useMutation({
52 | mutationFn: () => {
53 | return SimpleDelete(`/api/settings/threeLegged`);
54 | },
55 | onSuccess: refresh
56 | })
57 |
58 | return {
59 | refresh: refreshAuthUrl,
60 | authUrl,
61 | // processCode,
62 | logout,
63 | isLoading: isAuthUrlLoading
64 | }
65 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/RenderTree.ts:
--------------------------------------------------------------------------------
1 | export interface IRenderTree {
2 | parentId: string | null;
3 | hubId: string | null;
4 | projectId: string | null;
5 | id: string;
6 | name: string;
7 | type: string | null;
8 | data: any;
9 | children: IRenderTree[];
10 | isLoaded: boolean;
11 | isLoading: boolean;
12 | }
13 |
14 | export interface NodeVisitorFunction {
15 | (visitingNode: IRenderTree): boolean;
16 | }
17 |
18 | export class RenderTree {
19 | constructor(private root: IRenderTree) {}
20 |
21 | find(id: string): IRenderTree | undefined {
22 | let first;
23 |
24 | const callback = (node: IRenderTree) => {
25 | if (node.id === id) {
26 | first = node;
27 | return false;
28 | }
29 |
30 | return true;
31 | };
32 |
33 | this.search(this.root, callback);
34 |
35 | return first;
36 | }
37 |
38 | search(node: IRenderTree, callback: NodeVisitorFunction): boolean {
39 | const len = node.children?.length;
40 | let keepGoing = callback(node);
41 |
42 | for (let i = 0; i < len; i++) {
43 | if (keepGoing === false) {
44 | return false;
45 | }
46 |
47 | keepGoing = this.search(node.children[i], callback);
48 | }
49 |
50 | return keepGoing;
51 | }
52 |
53 | searchPath(
54 | node: IRenderTree,
55 | stack: IRenderTree[],
56 | callback: NodeVisitorFunction
57 | ): boolean {
58 | stack.push(node);
59 |
60 | const len = node.children?.length;
61 | let found = callback(node);
62 |
63 | for (let i = 0; i < len; i++) {
64 | if (found) {
65 | return true;
66 | }
67 |
68 | found = this.searchPath(node.children[i], stack, callback);
69 | }
70 |
71 | if (!found) {
72 | stack.pop();
73 | }
74 |
75 | return found;
76 | }
77 |
78 | path(target: IRenderTree): IRenderTree[] {
79 | let stack: IRenderTree[] = [];
80 |
81 | const callback = (node: IRenderTree) => {
82 | return (node.id === target.id);
83 | };
84 |
85 | this.searchPath(this.root, stack, callback);
86 |
87 | return stack;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/Utilities/SimpleREST.ts:
--------------------------------------------------------------------------------
1 | import axios, {AxiosRequestConfig} from "axios";
2 |
3 | export function SimplePost(url: string, body?: any, options?: any): Promise {
4 | return new Promise((resolve, reject) => {
5 | axios.post(url, body, options)
6 | .then(result => {
7 | resolve(result?.data)
8 | })
9 | .catch(error => {
10 | if(error.response?.status !== 401){
11 |
12 | }
13 | reject(error);
14 | })
15 | })
16 | }
17 |
18 | export function SimplePut(url: string, body?: any, options?: any): Promise {
19 | return new Promise((resolve, reject) => {
20 | axios.put(url, body, options)
21 | .then(result => {
22 | resolve(result?.data)
23 | })
24 | .catch(error => {
25 | if(error.response?.status !== 401){
26 |
27 | }
28 | reject(error);
29 | })
30 | })
31 | }
32 |
33 | export function SimplePatch(url: string, body?: any, options?: any): Promise {
34 | return new Promise((resolve, reject) => {
35 | axios.patch(url, body, options)
36 | .then(result => {
37 | resolve(result?.data)
38 | })
39 | .catch(error => {
40 | console.error(error)
41 | if(error.response?.status !== 401){
42 |
43 | }
44 | reject(error);
45 | })
46 | })
47 | }
48 |
49 | export function SimpleDelete(url: string, options?: any): Promise {
50 | return new Promise((resolve, reject) => {
51 | axios.delete(url, options)
52 | .then(result => {
53 | resolve(result?.data)
54 | })
55 | .catch(error => {
56 |
57 | reject(error);
58 | })
59 | })
60 | }
61 |
62 | export function SimpleGet(url: string, options?: AxiosRequestConfig): Promise{
63 | return new Promise((resolve, reject) => {
64 | axios.get(url, options)
65 | .then(result => {
66 | resolve(result?.data as T)
67 | })
68 | .catch(error => {
69 | if(error.response?.status !== 401){
70 |
71 | }
72 | reject(error);
73 | })
74 | })
75 | }
76 |
77 | export function SimpleDownload(url: string, options?: any): Promise{
78 | return new Promise((resolve, reject) => {
79 | axios.get(url, {responseType: 'blob', ...options})
80 | .then(result => {
81 | resolve(result?.data)
82 | })
83 | .catch(error => {
84 | if(error.response?.status !== 401){
85 |
86 | }
87 | reject(error);
88 | })
89 | })
90 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3 | line-height: 1.5;
4 | font-weight: 400;
5 |
6 | color-scheme: light dark;
7 | color: rgba(255, 255, 255, 0.87);
8 | background-color: white /*#242424*/;
9 |
10 | font-synthesis: none;
11 | text-rendering: optimizeLegibility;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | -webkit-text-size-adjust: 100%;
15 | }
16 |
17 | /*a {*/
18 | /* font-weight: 500;*/
19 | /* color: #646cff;*/
20 | /* text-decoration: inherit;*/
21 | /*}*/
22 | /*a:hover {*/
23 | /* color: #535bf2;*/
24 | /*}*/
25 |
26 | body {
27 | margin: 0;
28 | display: flex;
29 | min-width: 100vw;
30 | min-height: 100vh;
31 | }
32 |
33 | /*h1 {*/
34 | /* font-size: 3.2em;*/
35 | /* line-height: 1.1;*/
36 | /*}*/
37 |
38 | /*button {*/
39 | /* border-radius: 8px;*/
40 | /* border: 1px solid transparent;*/
41 | /* padding: 0.6em 1.2em;*/
42 | /* font-size: 1em;*/
43 | /* font-weight: 500;*/
44 | /* font-family: inherit;*/
45 | /* background-color: #1a1a1a;*/
46 | /* cursor: pointer;*/
47 | /* transition: border-color 0.25s;*/
48 | /*}*/
49 | /*button:hover {*/
50 | /* border-color: #646cff;*/
51 | /*}*/
52 | /*button:focus,*/
53 | /*button:focus-visible {*/
54 | /* outline: 4px auto -webkit-focus-ring-color;*/
55 | /*}*/
56 |
57 | @media (prefers-color-scheme: light) {
58 | :root {
59 | color: #213547;
60 | background-color: #ffffff;
61 | }
62 | a:hover {
63 | color: #747bff;
64 | }
65 | button {
66 | background-color: #f9f9f9;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 | import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
6 | import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
7 | import {BrowserRouter} from "react-router-dom";
8 |
9 | const queryClient = new QueryClient({
10 | defaultOptions: {
11 | queries: {
12 | refetchOnWindowFocus: false,
13 | refetchOnMount: false,
14 | }
15 | }
16 | })
17 |
18 | ReactDOM.createRoot(document.getElementById('root')!).render(
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ,
27 | )
28 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": false,
20 | "noUnusedParameters": false,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/ClientApp/vite.config.ts:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vite'
2 | import react from '@vitejs/plugin-react'
3 | import mkcert from "vite-plugin-mkcert";
4 |
5 | // https://vitejs.dev/config/
6 |
7 | export default defineConfig({
8 | plugins: [react(), mkcert()]
9 | , server: {
10 | port: 3000,
11 | https: true,
12 | proxy: {
13 | '/api': {
14 | target: 'http://localhost:8083',
15 | changeOrigin: true,
16 | secure: false,
17 | rewrite: (path) => path.replace(/^\/api/, '/api')
18 | },
19 | '/hangfire': {
20 | target: 'http://localhost:8083',
21 | changeOrigin: true,
22 | secure: false,
23 | rewrite: (path) => path.replace(/^\/hangfire/, '/hangfire')
24 | },
25 | '/code': {
26 | target: 'http://localhost:8083',
27 | changeOrigin: true,
28 | secure: false,
29 | rewrite: (path) => path.replace(/^\/code/, '/code')
30 | }
31 | }
32 | }
33 | })
34 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Controllers/BulkUploadPresetController.cs:
--------------------------------------------------------------------------------
1 | using ApsSettings.Data;
2 | using ApsSettings.Data.Models;
3 | using Microsoft.AspNetCore.Mvc;
4 | using Microsoft.EntityFrameworkCore;
5 |
6 | namespace Bulk_Uploader_Electron.Controllers;
7 |
8 | public class BulkUploadPresetController: ControllerBase
9 | {
10 | private readonly DataContext _dataContext;
11 |
12 | public BulkUploadPresetController(DataContext dataContext)
13 | {
14 | _dataContext = dataContext;
15 | }
16 |
17 | [HttpGet]
18 | [Route("api/settings/bulkUploadPresets")]
19 | public async Task GetSettings()
20 | {
21 | var presets = await _dataContext.BulkUploadPresets.ToListAsync();
22 | return Ok(presets);
23 | }
24 |
25 | [HttpPatch]
26 | [Route("api/setting/bulkUploadPresets")]
27 | public async Task PatchSettings([FromBody] List presets )
28 | {
29 | _dataContext.BulkUploadPresets.UpdateRange(presets);
30 | await _dataContext.SaveChangesAsync();
31 | return Ok();
32 | }
33 |
34 | [HttpPost]
35 | [Route("api/settings/bulkUploadPresets")]
36 | public async Task PostSettings([FromBody] List presets )
37 | {
38 | await _dataContext.BulkUploadPresets.AddRangeAsync(presets);
39 | await _dataContext.SaveChangesAsync();
40 | return Created("api/settings/bulkUploadPresets", presets);
41 | }
42 |
43 | [HttpDelete]
44 | [Route("api/settings/bulkUploadPresets/{id}")]
45 | public async Task DeleteSettings(int id)
46 | {
47 | var preset = await _dataContext.BulkUploadPresets.FirstOrDefaultAsync(x => x.Id == id);
48 | if (preset == null) return NotFound();
49 |
50 | _dataContext.BulkUploadPresets.Remove(preset);
51 | await _dataContext.SaveChangesAsync();
52 | return NoContent();
53 | }
54 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Controllers/ScriptingController.cs:
--------------------------------------------------------------------------------
1 | using ApsSettings.Data.Models;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.ClearScript.V8;
4 |
5 | namespace Bulk_Uploader_Electron.Controllers;
6 |
7 | public class ModifyPathDTU
8 | {
9 | public BulkUpload BulkUpload { get; set; }
10 | public BulkUploadFile BulkUploadFile { get; set; }
11 | }
12 |
13 | public class ScriptingController: ControllerBase
14 | {
15 | [HttpPost]
16 | [Route("api/scripting/modifyPath")]
17 | public ActionResult ModifyPath([FromBody] ModifyPathDTU dtu)
18 | {
19 | try
20 | {
21 | var newFile = BulkUploadFile.CreateFile(
22 | dtu.BulkUpload,
23 | dtu.BulkUploadFile.SourceRelativePath,
24 | dtu.BulkUploadFile.SourceAbsolutePath
25 | );
26 |
27 | return Ok(newFile);
28 | }
29 | catch (Exception e)
30 | {
31 | return BadRequest(e.Message);
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Helpers/APSClientHelper.cs:
--------------------------------------------------------------------------------
1 | using Autodesk.Authentication;
2 | using Autodesk.DataManagement;
3 | using Autodesk.Oss.Http;
4 | using Autodesk.SDKManager;
5 |
6 | namespace Bulk_Uploader_Electron.Helpers
7 | {
8 | internal class APSClientHelper
9 | {
10 | internal static SDKManager SdkManager => SdkManagerBuilder.Create().Build();
11 | internal static AuthenticationClient AuthClient => new (SdkManager);
12 | internal static DataManagementClient DataManagement => new(SdkManager);
13 | internal static OSSApi OssApi => new(SdkManager);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Helpers/FileMetricsHelper.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 |
4 | namespace Bulk_Uploader_Electron.Helpers
5 | {
6 | public class FileCollectionMetrics
7 | {
8 | public string Path = "";
9 | public float TotalSizeMb = 0;
10 | public int TotalFileCount = 0;
11 | }
12 | public class FileMetricsHelper
13 | {
14 | ///
15 | /// Given a path, this will iterate all files recursively for a total file size (MB) and file count
16 | ///
17 | ///
18 | ///
19 | public FileCollectionMetrics GetFileSizeAndCount(string rootFolderPath)
20 | {
21 |
22 | var metrics = new FileCollectionMetrics();
23 |
24 | try
25 | {
26 | metrics.Path = rootFolderPath;
27 |
28 | // Get full exclusion list
29 | string[] customerExclusion = AppSettings.Instance.CustomerExcludedFileTypes.Split(",");
30 | string[] folderExclusion = AppSettings.Instance.CustomerExcludedFolderNames.Split(",");
31 | var accExclusion = AppSettings.Instance.IllegalFileTypes; //Known ACC
32 | accExclusion = accExclusion.Union(customerExclusion).ToArray();
33 |
34 | accExclusion = accExclusion.Select(s => s.ToUpperInvariant()).ToArray();
35 |
36 | var localFiles = Directory
37 | .GetFiles(rootFolderPath, "*", SearchOption.AllDirectories)
38 | .ToList();
39 |
40 | long filesInBytes = 0;
41 | int fileCount = 0;
42 |
43 | foreach (string filename in localFiles)
44 | {
45 | foreach (var fold in folderExclusion)
46 | {
47 | var folders = filename.Split(Path.DirectorySeparatorChar);
48 | if (!folders.Any(x => x.ToUpper() == fold.ToUpper()))
49 | {
50 | string extension = Path.GetExtension(filename)
51 | .ToUpper()
52 | .Replace(".", "");
53 |
54 | if (!accExclusion.Contains(extension))
55 | {
56 | fileCount++;
57 | filesInBytes += new FileInfo(filename).Length;
58 | }
59 | }
60 | }
61 | }
62 |
63 | //Convert bytes to Mega
64 | var filesInMb = (filesInBytes / 1024f) / 1024f;
65 | metrics.TotalSizeMb = filesInMb;
66 | metrics.TotalFileCount = fileCount;
67 | }
68 | catch (Exception ex)
69 | {
70 | Log.Error("Problem calculating file sizes and count for local files");
71 | }
72 |
73 | return metrics;
74 | }
75 |
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Helpers/FolderHelpers.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Bulk_Uploader_Electron.Helpers
4 | {
5 | public class FolderHelpers
6 | {
7 | public static bool PathHasFolder(string pathToFileName, string folderToCheck)
8 | {
9 | bool bHasFolder = false;
10 |
11 | try
12 | {
13 | var folders = pathToFileName.Split(Path.DirectorySeparatorChar);
14 | bHasFolder = folders.Any(x => x.ToUpper() == folderToCheck.ToUpper());
15 | }
16 | catch (Exception ex)
17 | {
18 | }
19 |
20 | return bHasFolder;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Helpers/JointTokenManager.cs:
--------------------------------------------------------------------------------
1 | using Ac.Net.Authentication;
2 | using Bulk_Uploader_Electron.Managers;
3 |
4 | namespace Bulk_Uploader_Electron.Helpers;
5 |
6 | public static class JointTokenManager
7 | {
8 | public static async Task GetToken()
9 | {
10 | try
11 | {
12 | return await ThreeLeggedTokenManager.Instance.GetToken();
13 | }
14 | catch (Exception)
15 | {
16 | return await TwoLeggedTokenManager.GetTwoLeggedToken();
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Helpers/TwoLeggedTokenManager.cs:
--------------------------------------------------------------------------------
1 | using Autodesk.Authentication.Model;
2 | using Bulk_Uploader_Electron.Helpers;
3 |
4 | namespace Bulk_Uploader_Electron.Managers
5 | {
6 | public static class TwoLeggedTokenManager
7 | {
8 | #region Properties
9 | private static string TwoLeggedToken { get; set; } = string.Empty;
10 | private static DateTime TwoLeggedTokenExpiration { get; set; }
11 | private static string? ClientId { get; set; }
12 | private static string? ClientSecret { get; set; }
13 | #endregion
14 |
15 |
16 | #region Methods
17 | public static async Task GetTwoLeggedToken()
18 | {
19 | if (ClientId != AppSettings.Instance.ClientId || ClientSecret != AppSettings.Instance.ClientSecret)
20 | {
21 | ClientId = AppSettings.Instance.ClientId;
22 | ClientSecret = AppSettings.Instance.ClientSecret;
23 | return await RequestTwoLeggedToken();
24 | }
25 |
26 | if (!string.IsNullOrEmpty(TwoLeggedToken) && TwoLeggedTokenExpiration > (DateTime.UtcNow.AddMinutes(15)))
27 | return TwoLeggedToken;
28 | else
29 | return await RequestTwoLeggedToken();
30 | }
31 | private static async Task RequestTwoLeggedToken()
32 | {
33 | var twoLeggedToken = await APSClientHelper.AuthClient.GetTwoLeggedTokenAsync(AppSettings.Instance.ClientId, AppSettings.Instance.ClientSecret, AppSettings.Instance.ForgeTwoLegScope);
34 |
35 | TwoLeggedToken = twoLeggedToken.AccessToken;
36 | TwoLeggedTokenExpiration = DateTime.UtcNow.AddSeconds(twoLeggedToken.ExpiresIn ?? 60);
37 |
38 | return TwoLeggedToken;
39 | }
40 | #endregion
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Models/Account.cs:
--------------------------------------------------------------------------------
1 |
2 | using Ganss.Excel;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Text.Json.Serialization;
5 |
6 | namespace Bulk_Uploader_Electron.Models
7 | {
8 | public class Account
9 | {
10 | [Key]
11 | [JsonPropertyName("id")] public int Id { get; set; }
12 |
13 | [Column("Account ID")]
14 | [JsonPropertyName("accountId")]
15 | public string AccountId { get; set; }
16 |
17 | [Column("Account Name")]
18 | [JsonPropertyName("name")]
19 | public string Name { get; set; }
20 |
21 |
22 | [Column("Account Enabled")]
23 | [JsonPropertyName("enabled")]
24 | public bool Enabled { get; set; }
25 |
26 | [Column("Account Region")]
27 | [JsonPropertyName("region")]
28 | public string Region { get; set; }
29 | }
30 |
31 |
32 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Models/BusinessUnit.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Bulk_Uploader_Electron.Models
5 | {
6 | public class BusinessUnit
7 | {
8 | [Key]
9 | [JsonPropertyName("id")] public int Id { get; set; }
10 |
11 | [JsonPropertyName("accountId")] public string AccountId { get; set; }
12 | [JsonPropertyName("businessUnitId")] public string BusinessUnitId { get; set; }
13 | [JsonPropertyName("name")] public string Name { get; set; }
14 | [JsonPropertyName("path")] public string Path { get; set; }
15 | }
16 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Models/ErrorMessage.cs:
--------------------------------------------------------------------------------
1 |
2 | using Ganss.Excel;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Bulk_Uploader_Electron.Models
10 | {
11 | public class ErrorMessage
12 | {
13 | public ErrorMessage(string heading, string data, string details)
14 | {
15 | Heading = heading;
16 | Data = data;
17 | Details = details;
18 | }
19 |
20 | public ErrorMessage(string heading, Exception ex)
21 | {
22 | Heading = heading;
23 | Data = ex.Message;
24 | Details = ex.StackTrace?? "No Stack";
25 | }
26 |
27 | [Column("Error")] public string Heading { get; }
28 | [Column("Message")] public string Data { get; }
29 | [Column("Details")] public string Details { get; }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Models/Project.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.ComponentModel.DataAnnotations.Schema;
5 | using System.Text.Json.Serialization;
6 | using System.Threading.Tasks;
7 | using Bulk_Uploader_Electron.Managers;
8 | using Newtonsoft.Json;
9 | using Newtonsoft.Json.Converters;
10 |
11 | namespace Bulk_Uploader_Electron.Models
12 | {
13 | public class Project
14 | {
15 | [Key]
16 | [JsonPropertyName("id")] public int Id { get; set; }
17 |
18 | [Column("BU ID")]
19 | [JsonPropertyName("businessUnitId")]
20 | public string BusinessUnitId { get; set; }
21 |
22 | [Column("Account ID")]
23 | [JsonPropertyName("accountId")] public string
24 | AccountId { get; set; }
25 |
26 | [Column("Project ID")]
27 | [JsonPropertyName("projectId")]
28 | public string ProjectId { get; set; }
29 |
30 | [Column("Project Name")]
31 | [JsonPropertyName("name")]
32 | public string Name { get; set; }
33 |
34 | [Column("Project Status")]
35 | [Newtonsoft.Json.JsonConverter(typeof(StringEnumConverter))]
36 | [JsonPropertyName("projectType")]
37 | public ProjectType ProjectType { get; set; }
38 | [JsonPropertyName("status")] public bool Status { get; set; }
39 |
40 | [Column("Project Wieght")]
41 | [JsonPropertyName("weight")]
42 | public int Weight { get; set; }
43 | }
44 |
45 | public enum ProjectType
46 | {
47 | ACC,
48 | BIM360
49 | }
50 |
51 |
52 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Models/SimpleFile.cs:
--------------------------------------------------------------------------------
1 | using Ganss.Excel;
2 |
3 | namespace Bulk_Uploader_Electron.Models
4 | {
5 | public interface ISimpleFile
6 | {
7 | string Name { get; set; }
8 | string Path { get; set; }
9 | }
10 |
11 | public class SimpleFile : ISimpleFile
12 | {
13 | [Column("Item ID")]
14 | public string ItemId { get; set; }
15 | [Column("Version ID")]
16 | public string VersionId { get; set; }
17 | [Column("Derivative ID")]
18 | public string DerivativeId { get; set; }
19 | [Column("Storage ID")]
20 | public string ObjectId { get; set; }
21 | [Column("Name")]
22 | public string Name { get; set; }
23 | [Column("File Type")]
24 | public string FileType { get; set; }
25 | [Column("Item URL")]
26 | public string Url { get; set; }
27 | [Column("Last Modified")]
28 | public DateTime LastModified { get; set; }
29 | [Column("Parent")]
30 | public string? ParentPath { get; set; }
31 | [Column("Path")]
32 | public string? Path { get; set; }
33 | [Column("Size")]
34 | public long Size { get; set; } = 0;
35 |
36 | [Ignore]
37 | public List CustomAttributes { get; set; } = new List();
38 | }
39 |
40 | public class CustomAttribute
41 | {
42 | public int id { get; set; }
43 | public string type { get; set; }
44 | public string name { get; set; }
45 | public string value { get; set; }
46 | }
47 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Models/SimpleFolder.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | using Ganss.Excel;
4 |
5 | namespace Bulk_Uploader_Electron.Models
6 | {
7 | public class SimpleFolder
8 | {
9 | [Column("Folder ID")]
10 | public string FolderId { get; set; }
11 | [Column("Folder Name")]
12 | public string Name { get; set; }
13 | [Column("Folder URL")]
14 | public string Url { get; set; }
15 | [Column("Parent")]
16 | public string? ParentPath { get; set; }
17 | [Column("Path")]
18 | public string? Path { get; set; }
19 | [Column("Is Root")]
20 | public bool IsRoot { get; set; }
21 | }
22 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Program.cs:
--------------------------------------------------------------------------------
1 | namespace Bulk_Uploader_Electron
2 | {
3 | public class Program
4 | {
5 | [STAThread]
6 | public static void Main(string[] args)
7 | {
8 | CreateHostBuilder(args).Build().RunAsync();
9 |
10 | Application.SetHighDpiMode(HighDpiMode.SystemAware);
11 | Application.EnableVisualStyles();
12 | Application.SetCompatibleTextRenderingDefault(false);
13 | Application.Run(new MainForm());
14 |
15 | //Handle random errors without crashing the application
16 | AppDomain.CurrentDomain.FirstChanceException += (sender, e) =>
17 | {
18 | if ((e == null) || (e.Exception == null))
19 | {
20 | return;
21 | }
22 |
23 | using (var sw = File.AppendText(@".\exceptions.txt"))
24 | {
25 | sw.WriteLine(e.Exception);
26 | }
27 | };
28 |
29 | AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
30 | {
31 | if ((e == null) || (e.ExceptionObject == null))
32 | {
33 | return;
34 | }
35 |
36 | using (var sw = File.AppendText(@".\exceptions.txt"))
37 | {
38 | sw.WriteLine(e.ExceptionObject);
39 | }
40 | };
41 | }
42 |
43 |
44 | public static IHostBuilder CreateHostBuilder(string[] args) =>
45 | Host.CreateDefaultBuilder(args)
46 | .ConfigureWebHostDefaults(webBuilder =>
47 | {
48 |
49 | // webBuilder.UseElectron(args);
50 | webBuilder.UseStartup();
51 |
52 | webBuilder.UseUrls("http://localhost:8083");
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:22876",
7 | "sslPort": 44338
8 | }
9 | },
10 | "profiles": {
11 | "Electron.NET App": {
12 | "commandName": "Executable",
13 | "executablePath": "electronize",
14 | "commandLineArgs": "start",
15 | "workingDirectory": "."
16 | },
17 |
18 | "Bulk_Uploader_Electron": {
19 | "commandName": "Project",
20 | "dotnetRunMessages": true,
21 | "launchBrowser": true,
22 | "environmentVariables": {
23 | "ASPNETCORE_ENVIRONMENT": "Development",
24 | "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
25 | }
26 | },
27 | "IIS Express": {
28 | "commandName": "IISExpress",
29 | "launchBrowser": true,
30 | "environmentVariables": {
31 | "ASPNETCORE_ENVIRONMENT": "Development"
32 | }
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Resources/autodesk_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Bulk-Uploader-FrontEnd/Resources/autodesk_icon.ico
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Utils/BatchJobController.cs:
--------------------------------------------------------------------------------
1 | namespace Bulk_Uploader_Electron.ClientApp.src.Utilities
2 | {
3 | public class BatchJobController
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Utils/BatchManager.cs:
--------------------------------------------------------------------------------
1 | using PluginBase.Models;
2 | using Serilog;
3 | using System.Collections.Concurrent;
4 | using System.Reflection;
5 |
6 | namespace Bulk_Uploader_Electron.ClientApp.src.Utilities
7 | {
8 | public class BatchManager
9 | {
10 | public static readonly BatchManager Instance = new BatchManager();
11 |
12 | private readonly ConcurrentDictionary batches = new System.Collections.Concurrent.ConcurrentDictionary();
13 |
14 | public void RegisterAssembly(Assembly assembly)
15 | {
16 | foreach (Type type in assembly.GetTypes())
17 | {
18 | if (typeof(IBatchRegistry).IsAssignableFrom(type))
19 | {
20 | IBatchRegistry result = (Activator.CreateInstance(type) as IBatchRegistry)!;
21 | if (result != null)
22 | {
23 | try
24 | {
25 | RegisterBatch(result);
26 | }
27 | catch (Exception ex)
28 | {
29 | Log.Error("BatchManager.RegisterAssembly", ex);
30 | }
31 | }
32 | }
33 | }
34 | }
35 |
36 | public IBatchRegistry? GetBatch(string key)
37 | {
38 | return batches.ContainsKey(key) ? batches[key] : null;
39 | }
40 |
41 | public void RegisterBatch(IBatchRegistry batch)
42 | {
43 | var name = batch.BatchDetails.Key;
44 | if (string.IsNullOrEmpty(name)) throw new NullReferenceException("Batch key cannot be null");
45 | if (batches.ContainsKey(name))
46 | {
47 | throw new Exception($"{name} Batch already registered");
48 | }
49 | if (!batches.TryAdd(name, batch))
50 | {
51 | throw new Exception($"{name} Batch could not be added to batch registry");
52 | }
53 | }
54 |
55 | public IBatchRegistry[] Batches
56 | {
57 | get => batches.Values.ToArray();
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Utils/BatchRegistry.cs:
--------------------------------------------------------------------------------
1 | namespace Bulk_Uploader_Electron.ClientApp.src.Utilities
2 | {
3 |
4 |
5 | //public class BatchRegistry : IBatchRegistry
6 | //{
7 | // //public HfBatchOperation BatchOperation { get; }
8 | // //public HfBatchTemplate GetTemplate { get; }
9 | // //public HfBatchLoad LoadBatch { get; }
10 | // //public HfBatchBuildStack BuildStack { get; }
11 |
12 | // //public BatchRegistry(HfBatchOperation batchOperation, HfBatchTemplate getTemplate, HfBatchLoad loadBatch, HfBatchBuildStack buildStack)
13 | // //{
14 | // // BatchOperation = batchOperation;
15 | // // GetTemplate = getTemplate;
16 | // // LoadBatch = loadBatch;
17 | // // BuildStack = buildStack;
18 | // //}
19 | //}
20 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Utils/MappingProfiles.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Bulk_Uploader_Electron.Models;
3 |
4 | namespace Bulk_Uploader_Electron.Utils
5 | {
6 | public static class ClassMappings
7 | {
8 | public static readonly IMapper Mapper;
9 |
10 |
11 | ///
12 | /// Configures a static instance of a mapper
13 | ///
14 | static ClassMappings()
15 | {
16 | MapperConfiguration config = new MapperConfiguration(cfg => cfg.AddProfile());
17 | Mapper = config.CreateMapper();
18 | }
19 | }
20 |
21 | ///
22 | /// Defines set of Automapper profiles
23 | ///
24 | public class MigrationProfiles : Profile
25 | {
26 | public MigrationProfiles()
27 | {
28 | // CreateMap().ReverseMap();
29 |
30 | // Use CreateMap... Etc.. here (Profile methods are the same as configuration methods)
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Utils/PluginManager.cs:
--------------------------------------------------------------------------------
1 | using PluginBase;
2 | using PluginBase.Models;
3 | using Serilog;
4 | using System.Collections.Concurrent;
5 | using System.Reflection;
6 |
7 | namespace Bulk_Uploader_Electron.Utils
8 | {
9 | public class PluginManager
10 | {
11 | public static readonly PluginManager Instance = new PluginManager();
12 |
13 | private ConcurrentDictionary plugins = new System.Collections.Concurrent.ConcurrentDictionary();
14 |
15 | private ConcurrentDictionary globalSettings = new System.Collections.Concurrent.ConcurrentDictionary();
16 |
17 | public void RegisterAssembly(Assembly assembly)
18 | {
19 | foreach (Type type in assembly.GetTypes())
20 | {
21 | if (typeof(IJobExecutionPlugin).IsAssignableFrom(type))
22 | {
23 | IJobExecutionPlugin result = (Activator.CreateInstance(type) as IJobExecutionPlugin)!;
24 | if (result != null)
25 | {
26 | try
27 | {
28 | RegisterPlugin(result);
29 | }
30 | catch (Exception ex)
31 | {
32 | Log.Error("PluginManager.RegisterAssembly", ex);
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
39 | public void RegisterPlugin(IJobExecutionPlugin plugin)
40 | {
41 | var name = plugin.GetType().Name;
42 | if (plugins.ContainsKey(name))
43 | {
44 | throw new Exception($"{name} Plugin already registered");
45 | }
46 | if (!plugins.TryAdd(plugin.GetType().Name, plugin))
47 | {
48 | throw new Exception($"{name} Plugin could not be added to plugin registry");
49 | }
50 | }
51 |
52 | public void RegisterGlobalSetting(string setting)
53 | {
54 | if (globalSettings.ContainsKey(setting))
55 | {
56 | throw new Exception($"Setting '{setting}' is already registered");
57 | }
58 | if (!globalSettings.TryAdd(setting, setting))
59 | {
60 | throw new Exception($"Setting {setting} could not be added to global settings registry");
61 | }
62 | }
63 |
64 | public IJobExecutionPlugin[] Plugins
65 | {
66 | get => plugins.Values.ToArray();
67 | }
68 |
69 | public string[] GlobalSettings
70 | {
71 | get => globalSettings.Values.ToArray();
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/Views/MainForm.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | //
5 | // Changes to this file may cause incorrect behavior and will be lost if
6 | // the code is regenerated.
7 | //
8 | //------------------------------------------------------------------------------
9 |
10 | namespace Bulk_Uploader_Electron {
11 | using System;
12 |
13 | partial class MainForm
14 | {
15 | ///
16 | /// Required designer variable.
17 | ///
18 | private System.ComponentModel.IContainer components = null;
19 | ///
20 | /// Clean up any resources being used.
21 | ///
22 | /// true if managed resources should be disposed; otherwise, false.
23 | protected override void Dispose(bool disposing)
24 | {
25 | if (disposing && (components != null))
26 | {
27 | components.Dispose();
28 | }
29 | base.Dispose(disposing);
30 | }
31 | #region Windows Form Designer generated code
32 | ///
33 | /// Required method for Designer support - do not modify
34 | /// the contents of this method with the code editor.
35 | ///
36 | private void InitializeComponent()
37 | {
38 | this.webView21 = new Microsoft.Web.WebView2.WinForms.WebView2();
39 | ((System.ComponentModel.ISupportInitialize)(this.webView21)).BeginInit();
40 | this.SuspendLayout();
41 | //
42 | // webView21
43 | //
44 | this.webView21.CreationProperties = null;
45 | this.webView21.DefaultBackgroundColor = System.Drawing.Color.White;
46 | this.webView21.Dock = System.Windows.Forms.DockStyle.Fill;
47 | this.webView21.Location = new System.Drawing.Point(0, 0);
48 | this.webView21.Name = "webView21";
49 | this.webView21.Size = new System.Drawing.Size(1280, 768);
50 | this.webView21.Source = new System.Uri("http://localhost:8083/", System.UriKind.Absolute);
51 | this.webView21.TabIndex = 0;
52 | this.webView21.ZoomFactor = 1D;
53 | //
54 | // Form1
55 | //
56 | this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
57 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
58 | this.ClientSize = new System.Drawing.Size(1280, 768);
59 | this.Controls.Add(this.webView21);
60 | this.Name = "MainForm";
61 | this.Text = "Autodesk Bulk File Manager";
62 | this.ShowIcon = false;
63 | ((System.ComponentModel.ISupportInitialize)(this.webView21)).EndInit();
64 | this.ResumeLayout(false);
65 | }
66 | #endregion
67 | public Microsoft.Web.WebView2.WinForms.WebView2 webView21;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "DetailedErrors": true,
3 | "Logging": {
4 | "LogLevel": {
5 | "Default": "Information",
6 | "Microsoft.AspNetCore": "Warning"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*"
9 | }
10 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/electron.manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "executable": "BulkUploaderElectron",
3 | "splashscreen": {
4 | "imageFile": ""
5 | },
6 | "name": "BulkUploaderElectron",
7 | "author": "",
8 | "singleInstance": false,
9 | "environment": "Production",
10 | "build": {
11 | "appId": "com.BulkUploaderElectron.app",
12 | "productName": "BulkUploaderElectron",
13 | "copyright": "Copyright © 2020",
14 | "buildVersion": "1.0.0",
15 | "compression": "maximum",
16 | "directories": {
17 | "output": "../../../bin/Desktop"
18 | },
19 | "extraResources": [
20 | {
21 | "from": "./bin",
22 | "to": "bin",
23 | "filter": [ "**/*" ]
24 | }
25 | ],
26 | "files": [
27 | {
28 | "from": "./ElectronHostHook/node_modules",
29 | "to": "ElectronHostHook/node_modules",
30 | "filter": [ "**/*" ]
31 | },
32 | "**/*"
33 | ]
34 | }
35 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/wwwroot/css/site.css:
--------------------------------------------------------------------------------
1 | /* html {
2 | font-size: 36px;
3 | }
4 |
5 | @media (min-width: 768px) {
6 | html {
7 | font-size: 16px;
8 | }
9 | }
10 |
11 | html {
12 | position: relative;
13 | min-height: 100%;
14 | }
15 |
16 | body {
17 | margin-bottom: 60px;
18 | } */
19 | body {
20 | padding: 2em;
21 | }
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Bulk-Uploader-FrontEnd/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/wwwroot/js/site.js:
--------------------------------------------------------------------------------
1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
2 | // for details on configuring this project to bundle and minify static web assets.
3 |
4 | // Write your JavaScript code.
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/wwwroot/lib/bootstrap/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2011-2021 Twitter, Inc.
4 | Copyright (c) 2011-2021 The Bootstrap Authors
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) .NET Foundation. All rights reserved.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use
4 | these files except in compliance with the License. You may obtain a copy of the
5 | License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software distributed
10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the
12 | specific language governing permissions and limitations under the License.
13 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/wwwroot/lib/jquery-validation/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | =====================
3 |
4 | Copyright Jörn Zaefferer
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/Bulk-Uploader-FrontEnd/wwwroot/lib/jquery/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright JS Foundation and other contributors, https://js.foundation/
2 |
3 | This software consists of voluntary contributions made by many
4 | individuals. For exact contribution history, see the revision history
5 | available at https://github.com/jquery/jquery
6 |
7 | The following license applies to all parts of this software except as
8 | documented below:
9 |
10 | ====
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining
13 | a copy of this software and associated documentation files (the
14 | "Software"), to deal in the Software without restriction, including
15 | without limitation the rights to use, copy, modify, merge, publish,
16 | distribute, sublicense, and/or sell copies of the Software, and to
17 | permit persons to whom the Software is furnished to do so, subject to
18 | the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be
21 | included in all copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 |
31 | ====
32 |
33 | All files located in the node_modules and external directories are
34 | externally maintained libraries used by this software which have their
35 | own licenses; we recommend you read them, as their terms may differ from
36 | the terms above.
37 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/BulkUploaderUtils.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Helpers/FileMetricsHelper.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 |
4 | namespace BulkUploaderUtils.Helpers
5 | {
6 | public class FileCollectionMetrics
7 | {
8 | public string Path = "";
9 | public float TotalSizeMb = 0;
10 | public int TotalFileCount = 0;
11 | }
12 | public class FileMetricsHelper
13 | {
14 | ///
15 | /// Given a path, this will iterate all files recursively for a total file size (MB) and file count
16 | ///
17 | ///
18 | ///
19 | public FileCollectionMetrics GetFileSizeAndCount(string rootFolderPath)
20 | {
21 |
22 | var metrics = new FileCollectionMetrics();
23 |
24 | try
25 | {
26 | metrics.Path = rootFolderPath;
27 |
28 | // Get full exclusion list
29 | string[] customerExclusion = AppSettings.Instance.CustomerExcludedFileTypes.Split(",");
30 | string[] folderExclusion = AppSettings.Instance.CustomerExcludedFolderNames.Split(",");
31 | var accExclusion = AppSettings.Instance.IllegalFileTypes; //Known ACC
32 | accExclusion = accExclusion.Union(customerExclusion).ToArray();
33 |
34 | accExclusion = accExclusion.Select(s => s.ToUpperInvariant()).ToArray();
35 |
36 | var localFiles = Directory
37 | .GetFiles(rootFolderPath, "*", SearchOption.AllDirectories)
38 | .ToList();
39 |
40 | long filesInBytes = 0;
41 | int fileCount = 0;
42 |
43 | foreach (string filename in localFiles)
44 | {
45 | foreach (var fold in folderExclusion)
46 | {
47 | var folders = filename.Split(Path.DirectorySeparatorChar);
48 | if (!folders.Any(x => x.ToUpper() == fold.ToUpper()))
49 | {
50 | string extension = Path.GetExtension(filename)
51 | .ToUpper()
52 | .Replace(".", "");
53 |
54 | if (!accExclusion.Contains(extension))
55 | {
56 | fileCount++;
57 | filesInBytes += new FileInfo(filename).Length;
58 | }
59 | }
60 | }
61 | }
62 |
63 | //Convert bytes to Mega
64 | var filesInMb = (filesInBytes / 1024f) / 1024f;
65 | metrics.TotalSizeMb = filesInMb;
66 | metrics.TotalFileCount = fileCount;
67 | }
68 | catch (Exception ex)
69 | {
70 | Log.Error("Problem calculating file sizes and count for local files");
71 | }
72 |
73 | return metrics;
74 | }
75 |
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Helpers/FolderHelpers.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace BulkUploaderUtils.Helpers
4 | {
5 | public class FolderHelpers
6 | {
7 | public static bool PathHasFolder(string pathToFileName, string folderToCheck)
8 | {
9 | bool bHasFolder = false;
10 |
11 | try
12 | {
13 | var folders = pathToFileName.Split(Path.DirectorySeparatorChar);
14 | bHasFolder = folders.Any(x => x.ToUpper() == folderToCheck.ToUpper());
15 | }
16 | catch (Exception ex)
17 | {
18 | }
19 |
20 | return bHasFolder;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Helpers/HubHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Http.Json;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace BulkUploaderUtils.Helpers
9 | {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Account.cs:
--------------------------------------------------------------------------------
1 | using EPPlus.Core.Extensions.Attributes;
2 | using System.ComponentModel.DataAnnotations;
3 | using System.Text.Json.Serialization;
4 |
5 | namespace Data.Models
6 | {
7 | public class Account
8 | {
9 | [Key]
10 | [JsonPropertyName("id")] public int Id { get; set; }
11 |
12 | [ExcelTableColumn("Account ID", true)]
13 | [JsonPropertyName("accountId")]
14 | public string AccountId { get; set; }
15 |
16 | [ExcelTableColumn("Account Name", true)]
17 | [JsonPropertyName("name")]
18 | public string Name { get; set; }
19 |
20 |
21 | [ExcelTableColumn("Account Enabled", true)]
22 | [JsonPropertyName("enabled")]
23 | public bool Enabled { get; set; }
24 |
25 | [ExcelTableColumn("Account Region", true)]
26 | [JsonPropertyName("region")]
27 | public string Region { get; set; }
28 | }
29 |
30 |
31 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/BusinessUnit.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Data.Models
5 | {
6 | public class BusinessUnit
7 | {
8 | [Key]
9 | [JsonPropertyName("id")] public int Id { get; set; }
10 |
11 | [JsonPropertyName("accountId")] public string AccountId { get; set; }
12 | [JsonPropertyName("businessUnitId")] public string BusinessUnitId { get; set; }
13 | [JsonPropertyName("name")] public string Name { get; set; }
14 | [JsonPropertyName("path")] public string Path { get; set; }
15 | }
16 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/ErrorMessage.cs:
--------------------------------------------------------------------------------
1 | using EPPlus.Core.Extensions.Attributes;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace BulkUploaderUtils.Models
9 | {
10 | public class ErrorMessage
11 | {
12 | public ErrorMessage(string heading, string data, string details)
13 | {
14 | Heading = heading;
15 | Data = data;
16 | Details = details;
17 | }
18 |
19 | public ErrorMessage(string heading, Exception ex)
20 | {
21 | Heading = heading;
22 | Data = ex.Message;
23 | Details = ex.StackTrace?? "No Stack";
24 | }
25 |
26 | [ExcelTableColumn("Error", true)] public string Heading { get; }
27 | [ExcelTableColumn("Message", true)] public string Data { get; }
28 | [ExcelTableColumn("Details", true)] public string Details { get; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/FolderPermission.cs:
--------------------------------------------------------------------------------
1 | namespace Data.Models
2 | {
3 | public class FolderPermission
4 | {
5 | public string ProjectId { get; set; }
6 | public string FolderId { get; set; }
7 | public bool Access { get; set; }
8 | }
9 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeAttributesBatchGet.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Data.Models.Forge
8 | {
9 | public class ForgeAttributesBatchGet
10 | {
11 | public List results { get; set; }
12 | public List errors { get; set; }
13 | }
14 |
15 | public class ForgeAttributesBatchGetResult
16 | {
17 | public string urn { get; set; }
18 | public string itemUrn { get; set; }
19 | public string name { get; set; }
20 | public string title { get; set; }
21 | public string number { get; set; }
22 | public string createTime { get; set; }
23 | public string createUserId { get; set; }
24 | public string createUserName { get; set; }
25 | public string lastModifiedTime { get; set; }
26 | public string lastModifiedUserId { get; set; }
27 | public string lastModifiedUserName { get; set; }
28 | public string storageUrn { get; set; }
29 | public int? storageSize { get; set; }
30 | public string entityType { get; set; }
31 | public int revisionNumber { get; set; }
32 | public string processState { get; set; }
33 | public Approvalstatus approvalStatus { get; set; }
34 | public List customAttributes { get; set; }
35 | }
36 |
37 | public class Approvalstatus
38 | {
39 | public string label { get; set; }
40 | public string value { get; set; }
41 | }
42 |
43 | public class CustomAttribute
44 | {
45 | public int id { get; set; }
46 | public string type { get; set; }
47 | public string name { get; set; }
48 | public string value { get; set; }
49 | }
50 |
51 | public class Error
52 | {
53 | public string urn { get; set; }
54 | public string code { get; set; }
55 | public string title { get; set; }
56 | public string detail { get; set; }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeBim360Project.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Data.Models.Forge.Bim360Project
8 | {
9 |
10 | public class ForgeBim360Project
11 | {
12 | public string id { get; set; }
13 | public string account_id { get; set; }
14 | public string name { get; set; }
15 | public string start_date { get; set; }
16 | public string end_date { get; set; }
17 | public string project_type { get; set; }
18 | public float? value { get; set; }
19 | public string currency { get; set; }
20 | public ForgeBim360ProjectStatus status { get; set; }
21 | public string job_number { get; set; }
22 | public string address_line_1 { get; set; }
23 | public string address_line_2 { get; set; }
24 | public string city { get; set; }
25 | public string state_or_province { get; set; }
26 | public string postal_code { get; set; }
27 | public string country { get; set; }
28 | public string business_unit_id { get; set; }
29 | public string timezone { get; set; }
30 | public string language { get; set; }
31 | public string construction_type { get; set; }
32 | public string contract_type { get; set; }
33 | public object? last_sign_in { get; set; }
34 | public DateTime? created_at { get; set; }
35 | public DateTime? updated_at { get; set; }
36 | }
37 |
38 | public enum ForgeBim360ProjectStatus
39 | {
40 | Active,
41 | Pending,
42 | Inactive,
43 | Archived
44 | }
45 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeBusinessUnits.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Data.Models.Forge
8 | {
9 |
10 | public class ForgeBusinessUnitResponse
11 | {
12 | public List business_units { get; set; }
13 | }
14 |
15 | public class ForgeBusinessUnit
16 | {
17 | public string id { get; set; }
18 | public string account_id { get; set; }
19 | public string parent_id { get; set; }
20 | public string name { get; set; }
21 | public string description { get; set; }
22 | public string path { get; set; }
23 | public DateTime created_at { get; set; }
24 | public DateTime updated_at { get; set; }
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeCheckPermissionResponse.cs:
--------------------------------------------------------------------------------
1 | namespace Data.Models.Forge
2 | {
3 |
4 | public class ForgeCheckPermissionResponse
5 | {
6 | public Data data { get; set; }
7 | public Jsonapi jsonapi { get; set; }
8 | }
9 |
10 | public class Data
11 | {
12 | public string type { get; set; }
13 | public string id { get; set; }
14 | public Attributes attributes { get; set; }
15 | public Relationships relationships { get; set; }
16 | }
17 |
18 | public class Attributes
19 | {
20 | public Extension extension { get; set; }
21 | }
22 |
23 | public class Extension
24 | {
25 | public Data1 data { get; set; }
26 | public string version { get; set; }
27 | public string type { get; set; }
28 | public Schema schema { get; set; }
29 | }
30 |
31 | public class Data1
32 | {
33 | public Permission[] permissions { get; set; }
34 | public string[] requiredActions { get; set; }
35 | }
36 |
37 | public class Permission
38 | {
39 | public string type { get; set; }
40 | public string id { get; set; }
41 | public Details details { get; set; }
42 | public bool permission { get; set; }
43 | }
44 |
45 | public class Details
46 | {
47 | public bool create { get; set; }
48 | public bool download { get; set; }
49 | public bool view { get; set; }
50 | }
51 |
52 | public class Schema
53 | {
54 | public string href { get; set; }
55 | }
56 |
57 | public class Relationships
58 | {
59 | public Resources resources { get; set; }
60 | }
61 |
62 | public class Resources
63 | {
64 | public Datum[] data { get; set; }
65 | }
66 |
67 | public class Datum
68 | {
69 | public string type { get; set; }
70 | public string id { get; set; }
71 | }
72 |
73 | public class Jsonapi
74 | {
75 | public string version { get; set; }
76 | }
77 |
78 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeFirstVersionResponse.cs:
--------------------------------------------------------------------------------
1 | namespace mass_upload_via_s3_csharp.Models.Forge;
2 |
3 | public class Attributes
4 | {
5 | public string displayName { get; set; }
6 | public DateTime createTime { get; set; }
7 | public string createUserId { get; set; }
8 | public string createUserName { get; set; }
9 | public DateTime lastModifiedTime { get; set; }
10 | public string lastModifiedUserId { get; set; }
11 | public string lastModifiedUserName { get; set; }
12 | public bool hidden { get; set; }
13 | public bool reserved { get; set; }
14 | public Extension extension { get; set; }
15 | public string name { get; set; }
16 | public int versionNumber { get; set; }
17 | }
18 |
19 | public class Data
20 | {
21 | public string type { get; set; }
22 | public string id { get; set; }
23 | public Attributes attributes { get; set; }
24 | public Relationships relationships { get; set; }
25 | public Links links { get; set; }
26 | public string conformingStatus { get; set; }
27 | }
28 |
29 | public class Extension
30 | {
31 | public string type { get; set; }
32 | public string version { get; set; }
33 | public Data data { get; set; }
34 | }
35 |
36 | public class Included
37 | {
38 | public string type { get; set; }
39 | public string id { get; set; }
40 | public Attributes attributes { get; set; }
41 | public Relationships relationships { get; set; }
42 | public Links links { get; set; }
43 | }
44 |
45 | public class Jsonapi
46 | {
47 | public string version { get; set; }
48 | }
49 |
50 | public class Links
51 | {
52 | public Self self { get; set; }
53 | public Related related { get; set; }
54 | public WebView webView { get; set; }
55 | }
56 |
57 | public class Related
58 | {
59 | public string href { get; set; }
60 | }
61 |
62 | public class Relationships
63 | {
64 | public Tip tip { get; set; }
65 | }
66 |
67 | public class ForgeFirstVersionResponse
68 | {
69 | public Jsonapi jsonapi { get; set; }
70 | public Links links { get; set; }
71 | public Data data { get; set; }
72 | public List included { get; set; }
73 | }
74 |
75 | public class Self
76 | {
77 | public string href { get; set; }
78 | }
79 |
80 | public class Tip
81 | {
82 | public Links links { get; set; }
83 | public Data data { get; set; }
84 | }
85 |
86 | public class WebView
87 | {
88 | public string href { get; set; }
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeGetDataHooksResponse.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Data.Models.Forge.GetDataHooksResponse
9 | {
10 |
11 | public class ForgeGetDataHooksResponse
12 | {
13 | public Links links { get; set; }
14 | public Datum[] data { get; set; }
15 | }
16 |
17 | public class Links
18 | {
19 | public string next { get; set; }
20 | }
21 |
22 | public class Datum
23 | {
24 | public string hookId { get; set; }
25 | public string tenant { get; set; }
26 | public string callbackUrl { get; set; }
27 | public string createdBy { get; set; }
28 | [JsonProperty("event")]
29 | public string _event { get; set; }
30 | public DateTime createdDate { get; set; }
31 | public DateTime lastUpdatedDate { get; set; }
32 | public string system { get; set; }
33 | public string creatorType { get; set; }
34 | public string status { get; set; }
35 | public Scope scope { get; set; }
36 | public bool autoReactivateHook { get; set; }
37 | public string urn { get; set; }
38 | public bool callbackWithEventPayloadOnly { get; set; }
39 | public string __self__ { get; set; }
40 | }
41 |
42 | public class Scope
43 | {
44 | public string folder { get; set; }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeMetadata.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Data.Models.Forge.Metadata
8 | {
9 | public class ForgeMetadata
10 | {
11 | public Data data { get; set; }
12 | }
13 |
14 | public class Data
15 | {
16 | public string type { get; set; }
17 | public Metadata[] metadata { get; set; }
18 | }
19 |
20 | public class Metadata
21 | {
22 | public string name { get; set; }
23 | public string role { get; set; }
24 | public string guid { get; set; }
25 | public Nullable isMasterView { get; set; }
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeMetadataProperties.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Data.Models.Forge.MetadataProperties;
4 |
5 | public class ForgeMetadataProperties
6 | {
7 | public Data data { get; set; }
8 | }
9 |
10 | public class Data
11 | {
12 | public string type { get; set; }
13 | public Property[] collection { get; set; }
14 | }
15 |
16 | public class Property
17 | {
18 | public int objectid { get; set; }
19 | public string externalId { get; set; }
20 | public string name { get; set; }
21 | public Dictionary> properties { get; set; }
22 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeProjectUsers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Data.Models.Forge
8 | {
9 | public class ForgeProjectUsers
10 | {
11 | public Pagination pagination { get; set; }
12 | public Result[] results { get; set; }
13 | }
14 |
15 | public class Pagination
16 | {
17 | public int limit { get; set; }
18 | public int offset { get; set; }
19 | public int totalResults { get; set; }
20 | public string nextUrl { get; set; }
21 | public string previousUrl { get; set; }
22 | }
23 |
24 | public class Result
25 | {
26 | public string id { get; set; }
27 | public string email { get; set; }
28 | public string name { get; set; }
29 | // public string firstName { get; set; }
30 | // public string lastName { get; set; }
31 | // public string autodeskId { get; set; }
32 | // public string anaylticsId { get; set; }
33 | // public string addressLine1 { get; set; }
34 | // public string addressLine2 { get; set; }
35 | // public string city { get; set; }
36 | // public string stateOrProvince { get; set; }
37 | // public int postalCode { get; set; }
38 | // public string country { get; set; }
39 | // public string imageUrl { get; set; }
40 | // public Phone phone { get; set; }
41 | // public string jobTitle { get; set; }
42 | // public string industry { get; set; }
43 | // public string aboutMe { get; set; }
44 | // public Accesslevels accessLevels { get; set; }
45 | // public string companyId { get; set; }
46 | // public string[] roleIds { get; set; }
47 | // public Service[] services { get; set; }
48 | }
49 |
50 | public class Phone
51 | {
52 | public string number { get; set; }
53 | public string phoneType { get; set; }
54 | public string extension { get; set; }
55 | }
56 |
57 | public class Accesslevels
58 | {
59 | public bool accountAdmin { get; set; }
60 | public bool projectAdmin { get; set; }
61 | public bool executive { get; set; }
62 | }
63 |
64 | public class Service
65 | {
66 | public string serviceName { get; set; }
67 | public string access { get; set; }
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeS3UploadCompletion.cs:
--------------------------------------------------------------------------------
1 | namespace mass_upload_via_s3_csharp.Models.Forge;
2 |
3 | public class ForgeS3UploadCompletion
4 | {
5 | public string bucketKey { get; set; }
6 | public string objectId { get; set; }
7 | public string objectKey { get; set; }
8 | public int size { get; set; }
9 | public string contentType { get; set; }
10 | public string location { get; set; }
11 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeSignedS3Upload.cs:
--------------------------------------------------------------------------------
1 | namespace mass_upload_via_s3_csharp.Models.Forge.ForgeSignedS3Upload;
2 |
3 | public class ForgeSignedS3Upload
4 | {
5 | public string uploadKey { get; set; }
6 | public DateTime uploadExpiration { get; set; }
7 | public DateTime urlExpiration { get; set; }
8 | public List urls { get; set; }
9 |
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeSignedS3Url.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Data.Models.Forge
8 | {
9 | public class ForgeSignedS3Url
10 | {
11 | public string status { get; set; }
12 | public string url { get; set; }
13 | public Params _params { get; set; }
14 | public int size { get; set; }
15 | public string sha1 { get; set; }
16 | }
17 |
18 | public class Params
19 | {
20 | public string contenttype { get; set; }
21 | public string contentdisposition { get; set; }
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeStorageCreation.cs:
--------------------------------------------------------------------------------
1 | namespace mass_upload_via_s3_csharp.Models.Forge.ForgeStorageCreation;
2 |
3 | // Root myDeserializedClass = JsonConvert.DeserializeObject(myJsonResponse);
4 | public class Data
5 | {
6 | public string type { get; set; }
7 | public string id { get; set; }
8 | public Relationships relationships { get; set; }
9 | }
10 |
11 | public class Jsonapi
12 | {
13 | public string version { get; set; }
14 | }
15 |
16 | public class Links
17 | {
18 | public Self self { get; set; }
19 | public Related related { get; set; }
20 | }
21 |
22 | public class Related
23 | {
24 | public string href { get; set; }
25 | }
26 |
27 | public class Relationships
28 | {
29 | public Target target { get; set; }
30 | }
31 |
32 | public class ForgeStorageCreation
33 | {
34 | public Jsonapi jsonapi { get; set; }
35 | public Links links { get; set; }
36 | public Data data { get; set; }
37 | }
38 |
39 | public class Self
40 | {
41 | public string href { get; set; }
42 | }
43 |
44 | public class Target
45 | {
46 | public Links links { get; set; }
47 | public Data data { get; set; }
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeSupportedFormats.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Data.Models.Forge;
4 |
5 | public class ForgeSupportedFormats
6 | {
7 | public Formats formats { get; set; }
8 | }
9 |
10 | public class Formats
11 | {
12 | public List dwg { get; set; }
13 | public List fbx { get; set; }
14 | public List ifc { get; set; }
15 | public List iges { get; set; }
16 | public List obj { get; set; }
17 | public List step { get; set; }
18 | public List stl { get; set; }
19 | public List svf { get; set; }
20 | public List svf2 { get; set; }
21 | public List thumbnail { get; set; }
22 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeUserInformation.cs:
--------------------------------------------------------------------------------
1 | namespace Data.Models.Forge
2 | {
3 |
4 | public class ForgeUserInformation
5 | {
6 | public string userId { get; set; }
7 | public string userName { get; set; }
8 | public string emailId { get; set; }
9 | public string firstName { get; set; }
10 | public string lastName { get; set; }
11 | public bool emailVerified { get; set; }
12 | public bool _2FaEnabled { get; set; }
13 | public string countryCode { get; set; }
14 | public string language { get; set; }
15 | public bool optin { get; set; }
16 | public string lastModified { get; set; }
17 | public Profileimages profileImages { get; set; }
18 | public Ldapinfo ldapInfo { get; set; }
19 | public object[] socialUserInfoList { get; set; }
20 | public string createdDate { get; set; }
21 | public Userprofessionalinfo userProfessionalInfo { get; set; }
22 | public string lastLoginDate { get; set; }
23 | public string eidmGuid { get; set; }
24 | }
25 |
26 | public class Profileimages
27 | {
28 | public string sizeX20 { get; set; }
29 | public string sizeX40 { get; set; }
30 | public string sizeX50 { get; set; }
31 | public string sizeX58 { get; set; }
32 | public string sizeX80 { get; set; }
33 | public string sizeX120 { get; set; }
34 | public string sizeX160 { get; set; }
35 | public string sizeX176 { get; set; }
36 | public string sizeX240 { get; set; }
37 | public string sizeX360 { get; set; }
38 | }
39 |
40 | public class Ldapinfo
41 | {
42 | public bool ldapEnabled { get; set; }
43 | public string domainName { get; set; }
44 | }
45 |
46 | public class Userprofessionalinfo
47 | {
48 | public string industry { get; set; }
49 | public string industryCode { get; set; }
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Forge/ForgeWebhookResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Data.Models.Forge.WebhookResponse
8 | {
9 |
10 | public class ForgeWebhookResponse
11 | {
12 | public Hook[] hooks { get; set; }
13 | }
14 |
15 | public class Hook
16 | {
17 | public string hookId { get; set; }
18 | public string tenant { get; set; }
19 | public string callbackUrl { get; set; }
20 | public string createdBy { get; set; }
21 | public string _event { get; set; }
22 | public DateTime createdDate { get; set; }
23 | public string system { get; set; }
24 | public string creatorType { get; set; }
25 | public Scope scope { get; set; }
26 | public string status { get; set; }
27 | public bool autoReactivateHook { get; set; }
28 | public string urn { get; set; }
29 | public string __self__ { get; set; }
30 | }
31 |
32 | public class Scope
33 | {
34 | public string folder { get; set; }
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/ForgeBatchS3Download.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Data.Models.Forge
5 | {
6 | public class ForgeBatchS3Download
7 | {
8 | public Dictionary results { get; set; }
9 | }
10 |
11 | public class ForgeBatchS3DownloadItem
12 | {
13 | public string status { get; set; }
14 | public string reason { get; set; }
15 | public string url { get; set; }
16 | // public Nullable> urls { get; set; }
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/Project.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.ComponentModel.DataAnnotations.Schema;
5 | using System.Text.Json.Serialization;
6 | using System.Threading.Tasks;
7 | using Data.Managers;
8 | using Data.Models.Forge.Bim360Project;
9 | using EPPlus.Core.Extensions.Attributes;
10 | using Newtonsoft.Json;
11 | using Newtonsoft.Json.Converters;
12 |
13 | namespace Data.Models
14 | {
15 | public class Project
16 | {
17 | [Key]
18 | [JsonPropertyName("id")] public int Id { get; set; }
19 |
20 | [ExcelTableColumn("BU ID", true)]
21 | [JsonPropertyName("businessUnitId")]
22 | public string BusinessUnitId { get; set; }
23 |
24 | [ExcelTableColumn("Account ID", true)]
25 | [JsonPropertyName("accountId")] public string
26 | AccountId { get; set; }
27 |
28 | [ExcelTableColumn("Project ID", true)]
29 | [JsonPropertyName("projectId")]
30 | public string ProjectId { get; set; }
31 |
32 | [ExcelTableColumn("Project Name", true)]
33 | [JsonPropertyName("name")]
34 | public string Name { get; set; }
35 |
36 | [ExcelTableColumn("Project Status", true)]
37 | [Newtonsoft.Json.JsonConverter(typeof(StringEnumConverter))]
38 | [JsonPropertyName("projectType")]
39 | public ProjectType ProjectType { get; set; }
40 | [JsonPropertyName("status")] public bool Status { get; set; }
41 |
42 | [ExcelTableColumn("Project Wieght", true)]
43 | [JsonPropertyName("weight")]
44 | public int Weight { get; set; }
45 | }
46 |
47 | public enum ProjectType
48 | {
49 | ACC,
50 | BIM360
51 | }
52 |
53 |
54 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/SimpleFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Data.Models.Forge;
4 | using EPPlus.Core.Extensions.Attributes;
5 |
6 | namespace Data.Models
7 | {
8 | public class SimpleFile
9 | {
10 | [ExcelTableColumn("Item ID", true)]
11 | public string ItemId { get; set; }
12 | [ExcelTableColumn("Version ID", true)]
13 | public string VersionId { get; set; }
14 | [ExcelTableColumn("Derivative ID", true)]
15 | public string DerivativeId { get; set; }
16 | [ExcelTableColumn("Storage ID", true)]
17 | public string ObjectId { get; set; }
18 | [ExcelTableColumn("Name", true)]
19 | public string Name { get; set; }
20 | [ExcelTableColumn("File Type", true)]
21 | public string FileType { get; set; }
22 | [ExcelTableColumn("Item URL", true)]
23 | public string Url { get; set; }
24 | [ExcelTableColumn("Last Modified", true)]
25 | public DateTime LastModified { get; set; }
26 | [ExcelTableColumn("Parent", true)]
27 | public string? ParentPath { get; set; }
28 | [ExcelTableColumn("Path", true)]
29 | public string? Path { get; set; }
30 | [ExcelTableColumn("Size", true)]
31 | public long Size { get; set; }
32 | [ExcelTableColumn("Status", true)]
33 | public string Status { get; set; }
34 |
35 |
36 | public List CustomAttributes { get; set; } = new List();
37 | }
38 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/SimpleFolder.cs:
--------------------------------------------------------------------------------
1 | using EPPlus.Core.Extensions.Attributes;
2 |
3 | namespace Data.Models
4 | {
5 | public class SimpleFolder
6 | {
7 | [ExcelTableColumn("Folder ID", true)]
8 | public string FolderId { get; set; }
9 | [ExcelTableColumn("Folder Name", true)]
10 | public string Name { get; set; }
11 | [ExcelTableColumn("Folder URL", true)]
12 | public string Url { get; set; }
13 | [ExcelTableColumn("Parent", true)]
14 | public string? ParentPath { get; set; }
15 | [ExcelTableColumn("Path", true)]
16 | public string? Path { get; set; }
17 | [ExcelTableColumn("Is Root", true)]
18 | public bool IsRoot { get; set; }
19 | }
20 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/SimpleHook.cs:
--------------------------------------------------------------------------------
1 | namespace Data.Models
2 | {
3 | public class SimpleHook
4 | {
5 | public string HookId { get; set; }
6 | public string Event { get; set; }
7 | public string System { get; set; }
8 | public string Callback { get; set; }
9 | public string FolderId { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Models/ThreeLeggedToken.cs:
--------------------------------------------------------------------------------
1 | namespace Data.Models
2 | {
3 | public class ThreeLeggedToken
4 | {
5 | public string AccessToken { get; set; }
6 | public string RefreshToken { get; set; }
7 | public string TokenType { get; set; }
8 | public int ExpiresIn { get; set; }
9 | }
10 | }
--------------------------------------------------------------------------------
/BulkUploaderUtils/Utils/MappingProfiles.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Data.Models;
3 |
4 | namespace BulkUploaderUtils.Utils
5 | {
6 | public static class ClassMappings
7 | {
8 | public static readonly IMapper Mapper;
9 |
10 |
11 | ///
12 | /// Configures a static instance of a mapper
13 | ///
14 | static ClassMappings()
15 | {
16 | MapperConfiguration config = new MapperConfiguration(cfg => cfg.AddProfile());
17 | Mapper = config.CreateMapper();
18 | }
19 | }
20 |
21 | ///
22 | /// Defines set of Automapper profiles
23 | ///
24 | public class MigrationProfiles : Profile
25 | {
26 | public MigrationProfiles()
27 | {
28 | // CreateMap().ReverseMap();
29 |
30 | // Use CreateMap... Etc.. here (Profile methods are the same as configuration methods)
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/Documentation/20230817-Upload-Download-Coordination.drawio:
--------------------------------------------------------------------------------
1 | 7Vxbj5s4FP418zgVYGzI49xHVautNN1L+7JygkPoEBw5TjPpr187kAu2k0kVsEmyqdQJBxPgO+cz52auwN347YnhyegzTUh+FXjJ2xW4vwqCII5i8UdKFqUkCkApSFmWlCJ/I3jJfpFK6FXSWZaQaW0gpzTn2aQuHNCiIANek2HG6Lw+bEjz+lknOCWa4GWAc136d5bwUSWNPG+z45lk6Wh1an+1Z4xXoyvBdIQTOt8SgYcrcMco5eW38dsdySV6K2DK4x537F1fGSMFP+QA8P3fnHy/vkXR0/Nfv2bTP54RvkaVfn7ifFbdcnW1fLHCgNFZkRD5K94VuJ2PMk5eJngg986F1oVsxMe52PLF12GW53c0p2x5LEggiZNQyKec0VeytScO+gAhsae6AMI4edt5a/4aMGFqhI4JZwsxZGVnsMK4sjJ/tT3f6AysZKMtdQUrbeHKTtL1b2+QFF8qMH8HWNQusEMo/0k5LfiWvPyYAEfLTzOAX/tIQVwHPAgNgIdBW4CvznaegMO4jncIDgQ8bgtvEJ0z3kixbxgeiHeAWgMcnjPgfqAYOHIOuB9p+JJEeAfVJmV8RFNa4PxhI72ta2Az5hOlkwr3H4TzReXq4Bmnda0IvNjin+r45cY3ufEBrjbv37Z33i+qrfJa5QXu14C4HzpjA7LvxstxHLOU8H3jQrNKGckxz37WL8SknuWhN4zhxdaACc0KPt365S9SsPWwV6kZQUXV5S9uFL++tCNsQePeR9r/iqevN2nKSIq54ESjfhMm8XBgJN0gJv1hQ34TUEgX66RbE3ObdK1xDrml3IZl32oka51ywaGUO5JxRynH8XzYeeVAl8oJTDPULeaDUbNewnAYDIwTU4L6CDbkDQCvPjEBgzdgd2LqXajtgwNt34kroHnpQEmBKONhb+/4llwH73/T2Ws68SmYTgQcmA7Q5vTbWf765ySnODnBWR11blYP9wD8mAlkT8+nh6qhGnKhdkHW8xbnEDuBqGuxk57KP3ljRlHXjNnXk26X8TT3D80KBS4e59BXHi1qMUwZD9De8S15gsCF7biwAXAKNqC6gHZsQM8kfs5SJmChhXgsNjpHD4cE7fDqol7f85qZo0O1+Bo5n6N1t+6Fk8lUA/cdOOvYNwBVoFTxoMEBjgxIgdaQit0+zdoqcbyfR+01MUMdUJtQNFdOsdVRG+UdWwNBvo0aSO9EaLWOM13Rav1AvTRaBY04fweYuyVaQQu0CoJToZWhB8IureCl0grZoRW0RKueDVrpDXgdpZUhcWSXVmfa5/I+rRqpPOi5hV7vQ2/rE9fUve4ka5hkoae6njZIpuccu0GydZ9pVyKtwHEB2RnJgGdW1JEk083dDq2sRForK+w8rZxHWuBSIy3QTqSlm7slWtmItEBXIy2NVq4jLeCkUtEFWjXSx3SAuVuilY1IC3Q13a7RynWkBfSY9BMt0mtO2FjeWY4LDTRx+7yOU72aU9CCKKWfSoTzLC3E5kDARYT8VoKZDXB+U+0YZ0myZK5JMY1r45A1Y+vVk3bUsTN68cRdif/7syyX7BIco/KicJEOM6Y3UpyNjiK1QRJB1zrSiw5fGJlT9iqEH2lfn2V+q2LaAGZqV1toeHhb7mrTa8yXbtfq6jLo2q6hrqOb6XQ2JlJLfISlevDyZwS6ZKO614LOC3nvLJNtc95SgQNGhHdSpOLrj11N/2ejS/UxAkyNd8CqLvW4Qnbe3QtNnWinrrr+InDe3Aj1Xuhm1rc04tmoySjnaIUaKJcRv8Fj12CZ46prpWbjt5QW0c4TWojfoN6g3Y347VoNGAwvPbAav0HHy1Pd0SqyQ6uWamPaeWIbtNJfkNFRWhkaNu3S6lK7EGE7XYi6uduhVWDlaaUnBJ5IQRjmMjx6LIOljjHMlOoyMUytYTb3WgXvQhmGjn1hwg7LBzvio6YZppwH2Kg+o53pI9e0Uhd4uK4+o+BSadXIAp8DzN0SrWxUn5GewegorVxXn9GlZi9QS9kLzdwt0cpG9Rl1Nnuh0sp19RnpAekDlulTTwImj16WGSRqWZHK9eJblSFZWNAgPZ+aQljXFTStYbRaH0KGxVYjMasJ0deyW+DLWXcLBIpGTK/0barKIzY3r2Eu56XN26zBw38=
--------------------------------------------------------------------------------
/Documentation/BalsamiqWireframes.bmpr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/BalsamiqWireframes.bmpr
--------------------------------------------------------------------------------
/Documentation/Readme_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/Readme_img.png
--------------------------------------------------------------------------------
/Documentation/Thumnail1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/Thumnail1.gif
--------------------------------------------------------------------------------
/Documentation/Thumnail_bulk.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/Thumnail_bulk.gif
--------------------------------------------------------------------------------
/Documentation/User_guide_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/User_guide_0.png
--------------------------------------------------------------------------------
/Documentation/User_guide_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/User_guide_1.png
--------------------------------------------------------------------------------
/Documentation/User_guide_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/User_guide_2.png
--------------------------------------------------------------------------------
/Documentation/User_guide_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/User_guide_3.png
--------------------------------------------------------------------------------
/Documentation/User_guide_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/User_guide_4.png
--------------------------------------------------------------------------------
/Documentation/User_guide_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/User_guide_5.png
--------------------------------------------------------------------------------
/Documentation/User_guide_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/User_guide_6.png
--------------------------------------------------------------------------------
/Documentation/Wireframes.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autodesk-platform-services/aps-hubs-bulk-files-manager/6ea61fa3b64182e4c564c19e8630350e2e273e38/Documentation/Wireframes.pdf
--------------------------------------------------------------------------------
/Documentation/developer-guide.md:
--------------------------------------------------------------------------------
1 | # Developer Guide
2 |
3 | This is a help document with notes when you want to debug or extend the ability of this application.
4 |
5 | ## Layout
6 |
7 | This repository comprises the following top level folders.
8 |
9 | - [Ac.Net.Authentication](../Ac.Net.Authentication): help project that manages the workflow of user context authentication (3-legged token) of APS.
10 | - [ApsSettings.Data](../ApsSettings.Data):data models
11 | - [Bulk-Uploader-FrontEnd](../Bulk-Uploader-FrontEnd): main project, including user interfaces
12 | - [BulkUploaderUtils](../BulkUploaderUtils): help functions for upload/download
13 | - [Documentation](../Documentation): documentations and screenshots
14 | - [MigrationPlugin](../MigrationPlugin): This is project will have batch jobs controller to initiate the background jobs
15 | - [PluginBase](../PluginBase): This Plugins have models based out of Hangfire Btach jobs.
16 | - [TestPlugin](../TestPlugin): This is a Test project will help developer to test the scenarios and user/developers can add their own test cases if needed.
17 |
18 |
19 | ## Communication between UI and Backend
20 |
21 | There are two ways to communicate between the host and the UI:
22 | * REST calls
23 | * Standard ASP.net controllers may be used to send messages from the UI to the backend.
24 | * PostMessage
25 | * The UI is able to send messages to the backend using `window.chrome.webview.postMessage(YOUR_MESSAGE_HERE)`.
26 | It is then processed by the `WebMessageReceived()` method in `Bulk-Uploader-FrontEnd/Views/MainForm.cs`.
27 | * The backend is able to respond using `this.webView21.CoreWebView2.PostWebMessageAsString(YOUR_MESSAGE_HERE)`. It
28 | is then processed by the UI using `window.chrome.webview.addEventListener('message', event=>{ alert(event.data) })`
29 | in `Bulk-Uploader-FrontEnd/ClientApp/src/App.tsx`.
30 | * This has been simplified on the front end with the `useMessageListener(messagename)` hook.
31 |
32 | ## React Notes
33 |
34 | When using `` to go between the SPA and other pages (such as the hangfire dashboard), you must use the `reloadDocument` parameter to force React Router to treat it as a normal `` element.
35 |
36 | ## Database
37 |
38 | This application uses Entity Framework. If you make any changes to the data structures, you'll need to run the following command from the `Bulk-Uploader-FrontEnd` folder:
39 |
40 | `dotnet ef migrations add NameOfYourMigrationHere --project "..\ApsSettings.Data\ApsSettings.Data.csproj"`
41 |
42 | ## Additional Notes
43 | - In debugging mode, the `flower icon` on the bottom right corner will show up. It is the console that tracks the logs of each HTTP traffic.
44 |
45 | ## Building
46 | To build the application for a windows machine, use dotnet publish -r win-x64 while in the root solution folder.
47 |
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Autodesk Platform Services
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/License.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Autodesk Inc. http://www.autodesk.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/MigrationPlugin/Batches/ApsDownloadBatch.cs:
--------------------------------------------------------------------------------
1 | using ApsSettings.Data;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using MigrationPlugin.Controllers;
4 | using MigrationPlugin.Plugin;
5 | using PluginBase.Models;
6 |
7 | namespace MigrationPlugin.Batches
8 | {
9 | internal class ApsDownloadBatch : IBatchRegistry
10 | {
11 | public string Name => "APS Download";
12 |
13 | public HfBatchOperation BatchExecute => throw new NotImplementedException();
14 |
15 | public HfBatchOperation BuildStack => throw new NotImplementedException();
16 |
17 | public HfBatchOperation GetTemplate => throw new NotImplementedException();
18 |
19 | public HfBatchLoad LoadBatch => throw new NotImplementedException();
20 |
21 | public BatchDetails BatchDetails => new BatchDetails
22 | {
23 | Title = "Download",
24 | Key = "download_v1",
25 | Description = "Basic Download of Files from the Autodesk Cloud",
26 | BatchLoadUrl = BatchDownloadController.PATH_LOAD,
27 | BatchTemplateUrl = BatchDownloadController.PATH_TEMPLATE
28 | };
29 |
30 | public HfBatchOperation BatchEnqueue => EnqueueStart;
31 |
32 | private BatchProcessResults EnqueueStart(BatchJob batchJob)
33 | {
34 | try
35 | {
36 | var context = MigrationPluginObj.Instance.ServiceProvider.GetService()!;
37 | }
38 | catch (Exception ex)
39 | {
40 | throw;
41 | }
42 |
43 | return null;
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/MigrationPlugin/Batches/ApsMigrateBatch.cs:
--------------------------------------------------------------------------------
1 | using MigrationPlugin.Controllers;
2 | using PluginBase.Models;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MigrationPlugin.Batches
10 | {
11 | public class ApsMigrateBatch : IBatchRegistry
12 | {
13 | public string Name => "APS Simple Migrate";
14 |
15 |
16 | public HfBatchOperation BatchExecute => throw new NotImplementedException();
17 |
18 | public HfBatchOperation BuildStack => throw new NotImplementedException();
19 |
20 | public HfBatchOperation GetTemplate => throw new NotImplementedException();
21 |
22 | public HfBatchLoad LoadBatch => throw new NotImplementedException();
23 |
24 | public BatchDetails BatchDetails => new BatchDetails
25 | {
26 | Title = "Migration",
27 | Key = "migrate_v1",
28 | Description = "Basic Migration of Files to the Autodesk Cloud",
29 | BatchLoadUrl = BatchBasicMigrationController.PATH_LOAD,
30 | BatchTemplateUrl = BatchBasicMigrationController.PATH_TEMPLATE
31 | };
32 |
33 | public HfBatchOperation BatchEnqueue => throw new NotImplementedException();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/MigrationPlugin/Batches/ApsUploadBatch.cs:
--------------------------------------------------------------------------------
1 | using MigrationPlugin.Controllers;
2 | using PluginBase.Models;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MigrationPlugin.Batches
10 | {
11 | internal class ApsUploadBatch : IBatchRegistry
12 | {
13 | public string Name => "APS Upload";
14 |
15 | public HfBatchOperation BatchExecute => throw new NotImplementedException();
16 |
17 | public HfBatchOperation BuildStack => throw new NotImplementedException();
18 |
19 | public HfBatchOperation GetTemplate => throw new NotImplementedException();
20 |
21 | public HfBatchLoad LoadBatch => throw new NotImplementedException();
22 |
23 | public BatchDetails BatchDetails => new BatchDetails
24 | {
25 | Title = "Upload",
26 | Key="upload_v1",
27 | Description = "Basic Upload of Files to the Autodesk Cloud",
28 | BatchLoadUrl = BatchUploadController.PATH_LOAD,
29 | BatchTemplateUrl = BatchUploadController.PATH_TEMPLATE
30 | };
31 |
32 | public HfBatchOperation BatchEnqueue => throw new NotImplementedException();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/MigrationPlugin/Controllers/BatchBasicMigrationController.cs:
--------------------------------------------------------------------------------
1 | using Ac.Net.Authentication;
2 | using Ac.Net.Authentication.Models;
3 |
4 | using Flurl.Http;
5 | using Ganss.Excel;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.EntityFrameworkCore;
9 | using MigrationPlugin.Excel;
10 | using MigrationPlugin.Models;
11 | using NPOI.SS.Formula.Functions;
12 | using System.IO;
13 | using System.Text;
14 |
15 | namespace MigrationPlugin.Controllers
16 | {
17 | public class BatchBasicMigrationController : Controller
18 | {
19 | const string operation = "migrate";
20 | public const string PATH_TEMPLATE = "api/batch/" + operation + "/template";
21 | public const string PATH_LOAD = "api/batch/" + operation + "/load";
22 | [HttpGet]
23 | [Route(PATH_TEMPLATE)]
24 | public async Task GetTemplate()
25 | {
26 | try
27 | {
28 | var fileName = $"Batch.Download.Template.{DateTime.Now.ToString("yy.MM.dd.mm.ss")}.xlsx";
29 | var path = Path.Combine(Path.GetTempPath(), fileName);
30 | var bytes = ExcelUtils.MockBatchDownloadXlsx();
31 | return File(bytes, "application/vnd.ms-excel");
32 | }
33 | catch (Exception ex)
34 | {
35 | return BadRequest();
36 | }
37 | }
38 |
39 | [HttpPost]
40 | [Route(PATH_LOAD)]
41 | public async Task UploadData([FromForm] IFormFile FileData)
42 | {
43 |
44 | try
45 | {
46 | string name = FileData.FileName;
47 | string extension = Path.GetExtension(FileData.FileName);
48 |
49 | using (var memoryStream = new MemoryStream())
50 | {
51 | FileData.CopyTo(memoryStream);
52 | var items = new ExcelMapper(memoryStream).Fetch();
53 | // var items = ExcelUtils.LoadBatchDownloadXlsx(memoryStream);
54 |
55 | //await BatchFlows.ProcessDownloadBatch(ThreeLeggedMananger.Instance, DataContext context, items);
56 | }
57 |
58 | return Ok();
59 |
60 | }
61 | catch (Exception ex)
62 | {
63 | return BadRequest();
64 | }
65 | }
66 |
67 |
68 |
69 | }
70 | }
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/MigrationPlugin/Controllers/BatchDownloadController.cs:
--------------------------------------------------------------------------------
1 | using Ac.Net.Authentication;
2 | using Ac.Net.Authentication.Models;
3 | using ApsSettings.Data;
4 | using Ganss.Excel;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using MigrationPlugin.Excel;
9 | using MigrationPlugin.Models;
10 | using MigrationPlugin.Plugin;
11 | using Newtonsoft.Json;
12 | using PluginBase.Models;
13 |
14 | namespace MigrationPlugin.Controllers
15 | {
16 | public class BatchDownloadController : Controller
17 | {
18 | private const string operation = "download";
19 | public const string PATH_TEMPLATE = "api/batch/" + operation + "/template";
20 | public const string PATH_LOAD = "api/batch/" + operation + "/load";
21 |
22 | [HttpGet]
23 | [Route(PATH_TEMPLATE)]
24 | public async Task GetTemplate()
25 | {
26 | try
27 | {
28 | var fileName = $"Batch.Download.Template.{DateTime.Now.ToString("yy.MM.dd.mm.ss")}.xlsx";
29 | var path = Path.Combine(Path.GetTempPath(), fileName);
30 | var bytes = ExcelUtils.MockBatchDownloadXlsx();
31 | return File(bytes, "application/vnd.ms-excel");
32 | }
33 | catch (Exception ex)
34 | {
35 | return BadRequest();
36 | }
37 | }
38 |
39 | [HttpPost]
40 | [Route(PATH_LOAD)]
41 | public async Task UploadData([FromForm] IFormFile FileData)
42 | {
43 | try
44 | {
45 | string name = FileData.FileName;
46 | string extension = Path.GetExtension(FileData.FileName);
47 |
48 | using (var memoryStream = new MemoryStream())
49 | {
50 | FileData.CopyTo(memoryStream);
51 | var items = new ExcelMapper(memoryStream).Fetch().ToList();
52 | var context = MigrationPluginObj.Instance.ServiceProvider.GetService()!;
53 | foreach (var item in items)
54 | {
55 | var job = new BatchJob()
56 | {
57 | Name = item.Name,
58 | Type = "download",
59 | Data = JsonConvert.SerializeObject(item),
60 | };
61 | context.Batches.Add(job);
62 | }
63 | context.SaveChanges();
64 | }
65 |
66 | return Ok();
67 | }
68 | catch (Exception ex)
69 | {
70 | return BadRequest();
71 | }
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/MigrationPlugin/Controllers/BatchUploadController.cs:
--------------------------------------------------------------------------------
1 | using Ac.Net.Authentication;
2 | using Ac.Net.Authentication.Models;
3 |
4 | using Flurl.Http;
5 | using Ganss.Excel;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.EntityFrameworkCore;
9 | using MigrationPlugin.Excel;
10 | using MigrationPlugin.Models;
11 | using NPOI.SS.Formula.Functions;
12 | using System.IO;
13 | using System.Text;
14 |
15 | namespace MigrationPlugin.Controllers
16 | {
17 | public class BatchUploadController : Controller
18 | {
19 | const string operation = "upload";
20 | public const string PATH_TEMPLATE = "api/batch/" + operation + "/template";
21 | public const string PATH_LOAD = "api/batch/" + operation + "/load";
22 | [HttpGet]
23 | [Route(PATH_TEMPLATE)]
24 | public async Task GetTemplate()
25 | {
26 | try
27 | {
28 | var fileName = $"Batch.Download.Template.{DateTime.Now.ToString("yy.MM.dd.mm.ss")}.xlsx";
29 | var path = Path.Combine(Path.GetTempPath(), fileName);
30 | var bytes = ExcelUtils.MockBatchDownloadXlsx();
31 | return File(bytes, "application/vnd.ms-excel");
32 |
33 | }
34 | catch (Exception ex)
35 | {
36 | return BadRequest();
37 | }
38 | }
39 |
40 | [HttpPost]
41 | [Route(PATH_LOAD)]
42 | public async Task UploadData([FromForm] IFormFile FileData)
43 | {
44 |
45 | try
46 | {
47 | string name = FileData.FileName;
48 | string extension = Path.GetExtension(FileData.FileName);
49 |
50 | using (var memoryStream = new MemoryStream())
51 | {
52 | FileData.CopyTo(memoryStream);
53 | var items = new ExcelMapper(memoryStream).Fetch();
54 | // var items = ExcelUtils.LoadBatchDownloadXlsx(memoryStream);
55 |
56 | //await BatchFlows.ProcessDownloadBatch(ThreeLeggedMananger.Instance, DataContext context, items);
57 | }
58 |
59 | return Ok();
60 |
61 | }
62 | catch (Exception ex)
63 | {
64 | return BadRequest();
65 | }
66 | }
67 |
68 |
69 |
70 | }
71 | }
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/MigrationPlugin/MigrationPlugin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | all
25 | runtime; build; native; contentfiles; analyzers; buildtransitive
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/MigrationPlugin/Models/IBatchRegistry.cs:
--------------------------------------------------------------------------------
1 | namespace MigrationPlugin.Models
2 | {
3 |
4 | }
--------------------------------------------------------------------------------
/MigrationPlugin/Plugin/MigrationPluginObj.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.Hosting;
4 | using NPOI.SS.Formula.Functions;
5 | using PluginBase;
6 | using PluginBase.Models;
7 |
8 | namespace MigrationPlugin.Plugin
9 | {
10 | public class MigrationPluginObj : IJobExecutionPlugin
11 | {
12 | public MigrationPluginObj()
13 | {
14 | if (_instance != null)
15 | throw new NotSupportedException($"Only one instance of the MigrationPlugin plugin is supported");
16 | _instance = this;
17 | }
18 | public static MigrationPluginObj Instance => _instance;
19 | private static MigrationPluginObj _instance;
20 |
21 | public IServiceProvider ServiceProvider { get; private set; }
22 |
23 |
24 | public void Configure(IApplicationBuilder app, IHostEnvironment env, IServiceProvider serviceProvider)
25 | {
26 | ServiceProvider = serviceProvider;
27 | ISettingsProvider sp = ServiceProvider.GetService()!;
28 | sp.RegisterGlobalSetting("OutputFolder", null);
29 |
30 | }
31 |
32 | public void ConfigureServices(IServiceCollection services)
33 | {
34 | }
35 |
36 |
37 | }
38 | }
--------------------------------------------------------------------------------
/PackageApplication.ps1:
--------------------------------------------------------------------------------
1 |
2 | $now = Get-Date -Format "yyyyMMdd"
3 | $zipFileName = "$now - Bulk File Manager - APPLICATION BUNDLE.zip"
4 |
5 | Compress-Archive `
6 | -Path `
7 | "./BUILD/ClientApp", `
8 | "./BUILD/Bulk-File-Manager.exe", `
9 | "./BUILD/WebView2Loader.dll", `
10 | "./BUILD/e_sqlite3.dll", `
11 | "./BUILD/v8-base-ia32.dll", `
12 | "./BUILD/v8-base-x64.dll", `
13 | "./BUILD/v8-ia32.dll", `
14 | "./BUILD/v8-x64.dll", `
15 | "./BUILD/v8-zlib-ia32.dll", `
16 | "./BUILD/v8-zlib-x64.dll" `
17 | -DestinationPath "$zipFileName" `
18 | -Force
19 |
--------------------------------------------------------------------------------
/PluginBase/ISettingsProvider.cs:
--------------------------------------------------------------------------------
1 | namespace PluginBase
2 | {
3 | public interface ISimpleSetting
4 | {
5 | int SettingId { get; set; }
6 | string Key { get; set; }
7 | string Value { get; set; }
8 | }
9 |
10 | public enum GlobalKeyResult
11 | {
12 | Added,
13 | Exists
14 | }
15 |
16 | public interface ISettingsProvider
17 | {
18 | ISimpleSetting GetSetting(string key, bool force = true);
19 |
20 | //ISimpleSetting[] GetSettings(string[] keys, bool force);
21 |
22 | ISimpleSetting SetSetting(string key, string value, bool force = true);
23 |
24 | //ISimpleSetting[] SetSettings(ISimpleSetting[] settings, bool force);
25 |
26 | public GlobalKeyResult RegisterGlobalSetting(string key, string? defaultValue);
27 | }
28 | }
--------------------------------------------------------------------------------
/PluginBase/Models/BatchDetails.cs:
--------------------------------------------------------------------------------
1 | namespace PluginBase.Models
2 | {
3 | public class BatchDetails
4 | {
5 | public string Title { get; set; }
6 | public string Key { get; set; }
7 | public string Description { get; set; }
8 | public string BatchTemplateUrl { get; set; }
9 | public string BatchLoadUrl { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/PluginBase/Models/BatchJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace PluginBase.Models
5 | {
6 | public class BatchJob
7 | {
8 | public int Id { get; set; }
9 |
10 | public string Name { get; set; }
11 |
12 | public string Type { get; set; }
13 |
14 | public List Jobs { get; set; } = new List();
15 |
16 | public string? Errors { get; set; }
17 |
18 | public int JobCount { get; }
19 |
20 | public string? Data { get; set; }
21 |
22 | public DateTime? Created { get; set; } = DateTime.Now;
23 |
24 | public DateTime? Queued { get; set; }
25 |
26 | public DateTime? Started { get; set; }
27 |
28 | public DateTime? Completed { get; set; }
29 | }
30 | }
--------------------------------------------------------------------------------
/PluginBase/Models/BatchProcessPayload.cs:
--------------------------------------------------------------------------------
1 | namespace PluginBase.Models
2 | {
3 | public class BatchProcessPayload
4 | {
5 | public int DbId { get; set; }
6 |
7 | public string BatchId { get; set; }
8 |
9 | public string Action { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/PluginBase/Models/BatchProcessResults.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace PluginBase.Models
4 | {
5 | public class BatchProcessResults
6 | {
7 | public int DbId { get; set; }
8 |
9 | public string BatchId { get; set; }
10 |
11 | public string Status { get; set; }
12 |
13 | public List Errors { get; set; } = new List();
14 |
15 | }
16 | }
--------------------------------------------------------------------------------
/PluginBase/Models/IBatchRegistry.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 |
3 | namespace PluginBase.Models
4 | {
5 | public delegate BatchProcessResults HfBatchOperation(BatchJob batchJob);
6 | public delegate void HfBatchLoad(IFormFile fileData);
7 |
8 |
9 | public interface IBatchRegistry
10 | {
11 | HfBatchOperation BatchEnqueue { get; }
12 | HfBatchOperation BatchExecute { get; }
13 | HfBatchLoad LoadBatch { get; }
14 | BatchDetails BatchDetails { get; }
15 | }
16 | }
--------------------------------------------------------------------------------
/PluginBase/Models/IJobExecutionPlugin.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.Hosting;
4 | using System;
5 |
6 | namespace PluginBase.Models
7 | {
8 | public interface IJobExecutionPlugin
9 | {
10 |
11 | void ConfigureServices(IServiceCollection services);
12 |
13 | void Configure(IApplicationBuilder app, IHostEnvironment env, IServiceProvider serviceProvider);
14 |
15 | }
16 | }
--------------------------------------------------------------------------------
/PluginBase/Models/JobTaskAggregator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace PluginBase.Models
5 | {
6 | public class JobTaskAggregator
7 | {
8 | public int Id { get; set; }
9 |
10 | public string Description { get; set; }
11 |
12 | public DateTime CreatedOn { get; set; } = DateTime.Now;
13 |
14 | public DateTime? StartedOn { get; set; }
15 |
16 | public DateTime? LastUpdateOn { get; set; }
17 |
18 | public DateTime? CompletedOn { get; set; }
19 |
20 | public Dictionary Properties { get; set; } = new Dictionary();
21 |
22 | public List Steps { get; set; } = new List();
23 |
24 | public string? Errors { get; set; }
25 |
26 | public List History { get; set; }
27 |
28 | public BatchJob? JobOwner { get; set; }
29 |
30 | public int? JobOwnerId { get; set; }
31 |
32 | public int TotalSteps { get; set; } = 0;
33 |
34 | public int StepsCompleted { get; set; } = 0;
35 |
36 | public string? ActiveStep { get; set; }
37 |
38 | public void Update()
39 | {
40 | }
41 |
42 | // TODO
43 | // public void SetProperty(DbContext context, string key, T value)
44 | //where T : struct
45 | // {
46 | // {
47 | // Properties[key] = value!.ToString();
48 | // }
49 | // context.Entry(this).Property(d => d.Properties).IsModified = true;
50 | // }
51 |
52 | // public void SetProperty(DbContext context, string key, string value)
53 |
54 | // {
55 | // {
56 | // Properties[key] = value!.ToString();
57 | // }
58 | // context.Entry(this).Property(d => d.Properties).IsModified = true;
59 | // }
60 |
61 | public string? GetProperty(string key)
62 | {
63 | if (Properties.ContainsKey(key))
64 | {
65 | var st = Properties[key];
66 | return st.ToString();
67 | }
68 | return null;
69 | }
70 |
71 | public T? GetProperty(string key)
72 | where T : struct
73 | {
74 | if (Properties.ContainsKey(key))
75 | {
76 | var st = Properties[key];
77 | if (string.IsNullOrEmpty(st))
78 | {
79 | return default(T);
80 | }
81 | try
82 | {
83 | return (T)Convert.ChangeType(st, typeof(T));
84 | }
85 | catch
86 | {
87 | return default(T);
88 | }
89 | }
90 | return default(T); ;
91 | }
92 | }
93 | }
--------------------------------------------------------------------------------
/PluginBase/Models/StepHistory.cs:
--------------------------------------------------------------------------------
1 | namespace PluginBase.Models
2 | {
3 | public class StepHistory
4 | {
5 | public int Id { get; set; }
6 | public int StepNumber { get; set; }
7 | public string? StepResult { get; set; }
8 | public string? Error { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/PluginBase/Models/TaskStep.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace PluginBase.Models
4 | {
5 | public class TaskStep
6 | {
7 | public int Id { get; set; }
8 | public int AggregatorId { get; set; }
9 |
10 | public string StepType { get; set; }
11 |
12 | public int StepOrder { get; set; }
13 |
14 | public string Description { get; set; }
15 |
16 | public string? Data { get; set; }
17 |
18 | public DateTime? StartedOn { get; set; }
19 |
20 | public DateTime? CompletedOn { get; set; }
21 |
22 | public string? ErrorMessage { get; set; }
23 |
24 | public TimeSpan? ExecutionTime { get; set; }
25 |
26 | public double? ExecutionRate { get; set; }
27 |
28 | public int Retries { get; set; } = 1;
29 |
30 | public int RetryCount { get; set; } = 0;
31 |
32 | public string? Notes { get; set; }
33 | }
34 | }
--------------------------------------------------------------------------------
/PluginBase/PluginBase.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/TestPlugin/Batches/TestDownloadBatch.cs:
--------------------------------------------------------------------------------
1 | using ApsSettings.Data;
2 | using Ganss.Excel;
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Newtonsoft.Json;
6 | using PluginBase.Models;
7 | using TestPlugin.Controllers;
8 | using TestPlugin.Models;
9 | using TestPlugin.Plugin;
10 |
11 | namespace MigrationPlugin.Batches
12 | {
13 | internal class TestDownloadBatch : IBatchRegistry
14 | {
15 | public string Name => "Test Download";
16 |
17 | public HfBatchOperation BatchExecute => throw new NotImplementedException();
18 |
19 | public HfBatchOperation BuildStack => throw new NotImplementedException();
20 |
21 | public HfBatchLoad LoadBatch { get => ProcessBatch; }
22 |
23 | public HfBatchOperation BatchEnqueue => EnqueueStart;
24 |
25 | public BatchDetails BatchDetails => new BatchDetails
26 | {
27 | Title = "Test 1",
28 | Key = "test_1_v1",
29 | Description = "Test of a plugin",
30 | BatchLoadUrl = TestBatchController.PATH_LOAD,
31 | BatchTemplateUrl = TestBatchController.PATH_TEMPLATE
32 | };
33 |
34 | protected void ProcessBatch(IFormFile fileData)
35 | {
36 | string name = fileData.FileName;
37 | string extension = Path.GetExtension(fileData.FileName);
38 |
39 | using (var memoryStream = new MemoryStream())
40 | {
41 | fileData.CopyTo(memoryStream);
42 | var items = new ExcelMapper(memoryStream).Fetch();
43 | var context = TestPluginObj.Instance.ServiceProvider.GetService()!;
44 |
45 | foreach (var batchData in items)
46 | {
47 | var jb = new BatchJob
48 | {
49 | Name = batchData.Name,
50 | Data = JsonConvert.SerializeObject(batchData),
51 | Type = this.BatchDetails.Key
52 | };
53 |
54 | context.Batches.Add(jb);
55 | }
56 |
57 | context.SaveChanges();
58 | }
59 | }
60 |
61 | private BatchProcessResults EnqueueStart(BatchJob batchJob)
62 | {
63 | try
64 | {
65 | var context = TestPluginObj.Instance.ServiceProvider.GetService()!;
66 | }
67 | catch (Exception ex)
68 | {
69 | throw;
70 | }
71 |
72 | return null;
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/TestPlugin/Controllers/TestBatchController.cs:
--------------------------------------------------------------------------------
1 | using Ac.Net.Authentication;
2 | using Ac.Net.Authentication.Models;
3 |
4 | using Flurl.Http;
5 | using Ganss.Excel;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.EntityFrameworkCore;
9 |
10 | using NPOI.SS.Formula.Functions;
11 | using System.IO;
12 | using System.Text;
13 | using TestPlugin.Excel;
14 | using TestPlugin.Models;
15 |
16 | namespace TestPlugin.Controllers
17 | {
18 | public class TestBatchController : Controller
19 | {
20 | const string operation = "test";
21 | public const string PATH_TEMPLATE = "api/batch/" + operation + "/template";
22 | public const string PATH_LOAD = "api/batch/" + operation + "/load";
23 | [HttpGet]
24 | [Route(PATH_TEMPLATE)]
25 | public async Task GetTemplate()
26 | {
27 | try
28 | {
29 | var fileName = $"{ExcelUtils.DownloadWorksheet}.Template.{DateTime.Now.ToString("yy.MM.dd.mm.ss")}.xlsx";
30 | var path = Path.Combine(Path.GetTempPath(), fileName);
31 | var bytes = ExcelUtils.MockTestDownloadXlsx();
32 | return File(bytes, "application/vnd.ms-excel");
33 |
34 | }
35 | catch (Exception ex)
36 | {
37 | return BadRequest();
38 | }
39 | }
40 |
41 | [HttpPost]
42 | [Route(PATH_LOAD)]
43 | public async Task UploadData([FromForm] IFormFile FileData)
44 | {
45 |
46 | try
47 | {
48 | string name = FileData.FileName;
49 | string extension = Path.GetExtension(FileData.FileName);
50 |
51 | using (var memoryStream = new MemoryStream())
52 | {
53 | FileData.CopyTo(memoryStream);
54 | var items = new ExcelMapper(memoryStream).Fetch();
55 |
56 |
57 |
58 | //await BatchFlows.ProcessDownloadBatch(ThreeLeggedMananger.Instance, DataContext context, items);
59 | }
60 |
61 | return Ok();
62 |
63 | }
64 | catch (Exception ex)
65 | {
66 | return BadRequest();
67 | }
68 | }
69 |
70 |
71 |
72 | }
73 | }
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/TestPlugin/ExelUtils/ExcelUtils.cs:
--------------------------------------------------------------------------------
1 | using Bogus;
2 | using Ganss.Excel;
3 | using TestPlugin.Models;
4 |
5 | namespace TestPlugin.Excel
6 | {
7 | public class ExcelUtils
8 | {
9 | public const string UploadFile = "Test.Upload";
10 |
11 | public const string UploadWorksheet = "TestUploads";
12 |
13 | public const string DownloadFile = "Test.Download";
14 |
15 | public const string DownloadWorksheet = "TestDownloads";
16 |
17 | public static TestDownload MockTestDownload(int index)
18 | {
19 | var mock = new Faker()
20 | .StrictMode(true)
21 | .RuleFor(o => o.SourceProject, f => f.Internet.Url())
22 | .RuleFor(o => o.SourceFolderId, f => f.Internet.Url())
23 | .RuleFor(o => o.DownloadFolder, f => $"c:\\temp\\{index.ToString("000")}")
24 | .RuleFor(o => o.IgnoreExtensions, f => f.System.FileExt())
25 | .RuleFor(o => o.IgnoreFolders, f => f.Lorem.Word())
26 | .RuleFor(o => o.SourceHub, f => f.Internet.Url())
27 | .RuleFor(o => o.UseProjectForRoot, f => true)
28 | .RuleFor(o => o.Name, "TestBatch_" + index.ToString());
29 | return mock;
30 | }
31 |
32 |
33 |
34 | public static byte[] MockTestDownloadXlsx()
35 | {
36 | try
37 | {
38 | var list = new List();
39 | for (int i = 1; i < 10; i++)
40 | {
41 | list.Add(MockTestDownload(i));
42 | }
43 |
44 |
45 | using (var stream = new MemoryStream())
46 | {
47 | new ExcelMapper().Save(stream, list, "TestDownload");
48 | return stream.ToArray();
49 | }
50 | }
51 | catch (Exception ex)
52 | {
53 | Serilog.Log.Warning("Template download", ex);
54 | throw;
55 | }
56 | }
57 |
58 | }
59 | }
--------------------------------------------------------------------------------
/TestPlugin/Models/TestDownload.cs:
--------------------------------------------------------------------------------
1 | using Ganss.Excel;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace TestPlugin.Models
9 | {
10 | public class TestDownload
11 | {
12 | [Column("Name")]
13 | public string Name { get; set; }
14 |
15 | [Column("ID of Hub")]
16 | public string SourceHub { get; set; }
17 |
18 | [Column("ID of Project")]
19 | public string SourceProject { get; set; }
20 |
21 | [Column("Id to Target Folder (optional)")]
22 | public string SourceFolderId { get; set; }
23 |
24 | [Column("Root Folder for Download")]
25 | public string DownloadFolder { get; set; }
26 |
27 | [Column("Use Project For Root")]
28 | public bool UseProjectForRoot { get; set; } = false;
29 |
30 | [Column("File Extensions to Ignore (comma seperated)")]
31 | public string? IgnoreExtensions { get; set; }
32 |
33 | [Column("Folders to Ignore (comma seperated)")]
34 | public string? IgnoreFolders { get; set; }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/TestPlugin/Plugin/TestPluginObj.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.Hosting;
4 | using NPOI.SS.Formula.Functions;
5 | using PluginBase;
6 | using PluginBase.Models;
7 |
8 | namespace TestPlugin.Plugin
9 | {
10 | public class TestPluginObj : IJobExecutionPlugin
11 | {
12 | public TestPluginObj()
13 | {
14 | if (_instance != null)
15 | throw new NotSupportedException($"Only one instance of the TestPluginObj plugin is supported");
16 | _instance = this;
17 | }
18 | public static TestPluginObj Instance => _instance;
19 | private static TestPluginObj _instance;
20 |
21 | public IServiceProvider ServiceProvider { get; private set; }
22 |
23 |
24 | public void Configure(IApplicationBuilder app, IHostEnvironment env, IServiceProvider serviceProvider)
25 | {
26 | ServiceProvider = serviceProvider;
27 | ISettingsProvider sp = ServiceProvider.GetService()!;
28 |
29 |
30 | }
31 |
32 | public void ConfigureServices(IServiceCollection services)
33 | {
34 | }
35 |
36 |
37 | }
38 | }
--------------------------------------------------------------------------------
/TestPlugin/TestPlugin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | all
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------