13 Commits

Author SHA1 Message Date
Jonathon Broughton 24137fd6c0 Update README.md (#67) 2025-11-04 00:24:12 +01:00
Claire Kuang 3a027dcd6d Update README.md to align with main github page 2024-11-01 18:36:55 +00:00
Alan Rynne 6ad15f6a38 fix: Use v1 for tag pages (#66) 2024-10-11 15:47:09 +02:00
Oğuzhan Koral 3dad1a0849 Chore (FE2): CNX-9074 use new fe2 terminology persistently (#62)
* Make account servel url visible on snack bar

* Make FE2 terminology persistent, no switch anymore
2024-03-01 14:03:37 -06:00
Oğuzhan Koral e6d667c951 Feat(Notification): CNX-9017 raise warning with multi model urls (#61)
* Add global notification toast mechanism

* Warn user if url is multi model
2024-03-01 12:32:59 +03:00
connorivy 226693f82d Fix (Excel): CNX-9045 deserialize chunked data (#60)
* wip

* working

* remove weird change

---------

Co-authored-by: Connor Ivy <connor@speckle.systems>
2024-02-26 17:30:14 +03:00
Oğuzhan Koral 8caf0e5a77 Chore (UI): Increase branch limit to 100 2024-02-15 21:43:32 +03:00
Oğuzhan Koral 050aab39d0 Feat (FE2): CNX-8706 support fe2 urls in excel (#58)
* Fix create project

* Fetch 'x-speckle-frontend-2' header from server

* Change new dev default to app.speckle.systems

* Adjust stream wrapper according to frontend version

* Switch terminology according to server frontend

* Use state instead getter

* Try catch CORS issue and fallback to FE1

* Minor css
2024-02-06 12:36:47 -06:00
connorivy c7d7806a30 fix setting selectedHeaders variable for small commit objects (#57)
Co-authored-by: Connor Ivy <connor@speckle.systems>
2024-02-06 10:50:35 -06:00
connorivy a9cc5ed161 fix: CNX-7547 serialize get properties (#56)
* serialize get properties

* remove unwanted code segment

---------

Co-authored-by: Connor Ivy <connor@speckle.systems>
2024-01-29 14:53:10 +01:00
connorivy eeba4f860e Fix CNX-7547: size limit on data sending (#55)
* avoid giant post request

* add typescript

* add data retrieve to batch retrieve data from sheet

* upgrade node

* working for node 12

* creating closure table

* add key cleaning similar to c# sdk

---------

Co-authored-by: Connor Ivy <connor@speckle.systems>
2024-01-23 22:56:31 +01:00
connorivy 7aa91c2d86 fix partial receive (#54)
Co-authored-by: Connor Ivy <connor@speckle.systems>
2023-12-12 16:59:56 +01:00
Matteo Cominetti e9edf0fb17 Merge pull request #53 from specklesystems/dev
Various fixes
2023-08-01 14:05:54 +02:00
29 changed files with 1556 additions and 249 deletions
+9 -2
View File
@@ -1,4 +1,5 @@
{
"root": true,
"env": {
"browser": true,
"es2021": true,
@@ -9,12 +10,15 @@
"prettier",
"prettier/vue",
"plugin:prettier/recommended",
"eslint:recommended"
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "vue-eslint-parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module",
"parser": "@babel/eslint-parser"
"parser": "@typescript-eslint/parser"
},
"plugins": ["vue", "prettier"],
"rules": {
@@ -25,6 +29,9 @@
"after": true
}
],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-array-constructor": "off",
//"array-bracket-spacing": [2, "always"],
"block-spacing": [2, "always"],
"camelcase": [
+3
View File
@@ -0,0 +1,3 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
+36 -47
View File
@@ -1,63 +1,52 @@
# Speckle Excel Connector
<h1 align="center">
<img src="https://user-images.githubusercontent.com/2679513/131189167-18ea5fe1-c578-47f6-9785-3748178e4312.png" width="150px"/><br/>
Speckle | Excel
</h1>
[![Twitter Follow](https://img.shields.io/twitter/follow/SpeckleSystems?style=social)](https://twitter.com/SpeckleSystems) [![Community forum users](https://img.shields.io/discourse/users?server=https%3A%2F%2Fdiscourse.speckle.works&style=flat-square&logo=discourse&logoColor=white)](https://discourse.speckle.works) [![website](https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square)](https://speckle.systems) [![docs](https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&logo=read-the-docs&logoColor=white)](https://speckle.guide/dev/)
<p align="center">
<a href="https://speckle.community"><img src="https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&style=flat-square&logo=discourse&logoColor=white" alt="Community forum users"></a>
<a href="https://speckle.systems"><img src="https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square" alt="website"></a>
<a href="https://docs.speckle.systems"><img src="https://img.shields.io/badge/docs-docs.speckle.systems-orange?style=flat-square&logo=read-the-docs&logoColor=white" alt="docs"></a>
</p>
> Status: deprecated; not compatible with Speckle v3.
> Retained as open source for reference and community use.
> No active maintenance or feature updates are planned.
<h3 align="center">
Speckle Connector for Excel
</h3>
## Introduction
This repo holds Speckle's Excel Connector, it's currently released as early alpha.
This repository contains the Speckle Excel Connector, originally released as an early alpha. It enabled sending and receiving data between Microsoft Excel and the Speckle platform. The project is no longer maintained and is not compatible with Speckle v3. For modern workflows, use current SDKs and APIs documented at docs.speckle.systems
## Documentation
## Developing and debugging
Comprehensive developer and user documentation can be found in our:
Note: the instructions below are preserved for archival purposes.
#### 📚 [Speckle Docs website](https://speckle.guide/dev/)
### App setup
## Developing & Debugging
You need a Speckle App. The server must be on https. Do not use a local server on http://localhost:3000.
### App Set up
Use: https://app.speckle.systems/
For developing and debugging this connector you'll need to set up a Speckle App.
The server on which the app runs must be on `https`, so **do not use a local Speckle server** on `http://localhost:3000/` as it will not work.
In the server frontend, register a new app.
You can use `https://latest.speckle.dev/` or `https://speckle.xyz/`.
Example values for an app while the Excel add-in runs on https://localhost:3000:
Now open up its frontend, and under your profile register a new app.
- Name: ExcelConnector
- Redirect URL: `https://localhost:3000`
- Permissions: `streams:read, streams:write, profile:read, profile:email, users:read`
I've used the following values since my excel-connector app is running at `https://localhost:3000`:
Then in your local `speckle-excel` repo:
- name: ExcelConnector
- redirect url: `https://localhost:3000`
- permissions: `streams:read, streams:write, profile:read, profile:email, users:read`
- Duplicate `.env sample` to `.env.local`
- Add your `app id` and `secret`
- Set `BASE_URL=https://localhost:3000`
Take note of the `app id` and `secret`, then in your speckle-excel repo:
### Running locally
- duplicate `.env sample` to `.env.local`
- then fill it in with your new `app id` and `secret`
- note that the `BASE_URL=https://localhost:3000`
### Running the connector locally
Run it locally:
- `npm install` (first time only)
- `npm run serve`
- _You might be prompted to install some certificates, go ahead and accept_
- _Wait for the the process to start the Vue app, then in a separate terminal either_
- `npm run excel` will run excel desktop and sideload the plugin
- `npm run excel:web` will run excel web, open the document defined in `packages.json` and sideload the plugin
If this worked out well, you should see the connector sideloaded correctly:
![image](https://user-images.githubusercontent.com/2679513/119171684-cdf3da00-ba5c-11eb-87a5-bee798f96f90.png)
## Contributing
Please make sure you read the [contribution guidelines](.github/CONTRIBUTING.md) for an overview of the best practices we try to follow.
## Community
The Speckle Community hangs out on [the forum](https://discourse.speckle.works), do join and introduce yourself & feel free to ask us questions!
## License
Unless otherwise described, the code in this repository is licensed under the Apache-2.0 License. Please note that some modules, extensions or code herein might be otherwise licensed. This is indicated either in the root of the containing folder under a different license file, or in the respective file's header. If you have any questions, don't hesitate to get in touch with us via [email](mailto:hello@speckle.systems).
```bash
npm install
npm run serve
+403 -81
View File
@@ -4607,6 +4607,12 @@
"integrity": "sha1-YcyEaYSeW83QxwRBIiZcOc7BDPQ=",
"dev": true
},
"@types/crypto-js": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.1.tgz",
"integrity": "sha512-FSPGd9+OcSok3RsM0UZ/9fcvMOXJ1ENE/ZbLfOPlBWj7BgXtEAM8VYfTtT760GiLbQIMoVozwVuisjvsVwqYWw==",
"dev": true
},
"@types/express": {
"version": "4.17.11",
"resolved": "https://registry.npm.taobao.org/@types/express/download/@types/express-4.17.11.tgz",
@@ -4771,6 +4777,18 @@
"integrity": "sha1-5IbQ2XOW15vu3QpuM/RTT/a0lz4=",
"dev": true
},
"@types/office-js": {
"version": "1.0.364",
"resolved": "https://registry.npmjs.org/@types/office-js/-/office-js-1.0.364.tgz",
"integrity": "sha512-sEoI3Rhtyz83WjMo3GamshNU7/j8CNY/UVrSVr3n6yOTWo6eG94pZvT4f00g0bvOYlY7Wi+OTZWEqY+MtQ81MQ==",
"dev": true
},
"@types/office-runtime": {
"version": "1.0.35",
"resolved": "https://registry.npmjs.org/@types/office-runtime/-/office-runtime-1.0.35.tgz",
"integrity": "sha512-qrP3bkDNoPY6WZMTOoutFdkZHdK91OUC1/Ohzw94bE8OD8pVoPjHKSoYaS+NlTFyXH3SNKYmQU+E6o966zARIA==",
"dev": true
},
"@types/q": {
"version": "1.5.4",
"resolved": "https://registry.npm.taobao.org/@types/q/download/@types/q-1.5.4.tgz",
@@ -4906,6 +4924,297 @@
"integrity": "sha1-gIyfp+RRcnTtVV+hWPLeS09GjnE=",
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz",
"integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "4.33.0",
"@typescript-eslint/scope-manager": "4.33.0",
"debug": "^4.3.1",
"functional-red-black-tree": "^1.0.1",
"ignore": "^5.1.8",
"regexpp": "^3.1.0",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
},
"dependencies": {
"ignore": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
"integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
"dev": true
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
}
}
},
"@typescript-eslint/experimental-utils": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz",
"integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.7",
"@typescript-eslint/scope-manager": "4.33.0",
"@typescript-eslint/types": "4.33.0",
"@typescript-eslint/typescript-estree": "4.33.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
"dependencies": {
"eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
"dev": true,
"requires": {
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
}
},
"eslint-utils": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
"integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^2.0.0"
}
},
"eslint-visitor-keys": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
"dev": true
}
}
},
"@typescript-eslint/parser": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz",
"integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "4.33.0",
"@typescript-eslint/types": "4.33.0",
"@typescript-eslint/typescript-estree": "4.33.0",
"debug": "^4.3.1"
}
},
"@typescript-eslint/scope-manager": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz",
"integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.33.0",
"@typescript-eslint/visitor-keys": "4.33.0"
}
},
"@typescript-eslint/types": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz",
"integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz",
"integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.33.0",
"@typescript-eslint/visitor-keys": "4.33.0",
"debug": "^4.3.1",
"globby": "^11.0.3",
"is-glob": "^4.0.1",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
},
"dependencies": {
"@nodelib/fs.stat": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true
},
"array-union": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"requires": {
"fill-range": "^7.0.1"
}
},
"dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
"dev": true,
"requires": {
"path-type": "^4.0.0"
}
},
"fast-glob": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
"dev": true,
"requires": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.4"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"dev": true,
"requires": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.2.9",
"ignore": "^5.2.0",
"merge2": "^1.4.1",
"slash": "^3.0.0"
}
},
"ignore": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
"integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
"dev": true
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
}
},
"micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"requires": {
"braces": "^3.0.2",
"picomatch": "^2.3.1"
}
},
"path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true
},
"picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true
},
"semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"requires": {
"is-number": "^7.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
}
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz",
"integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.33.0",
"eslint-visitor-keys": "^2.0.0"
},
"dependencies": {
"eslint-visitor-keys": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
"dev": true
}
}
},
"@vue/babel-helper-vue-jsx-merge-props": {
"version": "1.2.1",
"resolved": "https://registry.npm.taobao.org/@vue/babel-helper-vue-jsx-merge-props/download/@vue/babel-helper-vue-jsx-merge-props-1.2.1.tgz",
@@ -5215,6 +5524,63 @@
"integrity": "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=",
"dev": true
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"ssri": {
"version": "8.0.1",
"resolved": "https://registry.npm.taobao.org/ssri/download/ssri-8.0.1.tgz?cache=0&sync_timestamp=1617826339976&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fssri%2Fdownload%2Fssri-8.0.1.tgz",
@@ -5223,6 +5589,28 @@
"requires": {
"minipass": "^3.1.1"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.8.3",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
"integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
}
}
},
@@ -18672,6 +19060,15 @@
"integrity": "sha1-zy04vcNKE0vK8QkcQfZhni9nLQA=",
"dev": true
},
"tsutils": {
"version": "3.21.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
}
},
"tty": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/tty/download/tty-1.0.1.tgz",
@@ -18739,6 +19136,12 @@
"is-typedarray": "^1.0.0"
}
},
"typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true
},
"ua-parser-js": {
"version": "0.7.28",
"resolved": "https://registry.npm.taobao.org/ua-parser-js/download/ua-parser-js-0.7.28.tgz?cache=0&sync_timestamp=1618065908450&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fua-parser-js%2Fdownload%2Fua-parser-js-0.7.28.tgz",
@@ -19669,87 +20072,6 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.8.3",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
"integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-mixpanel": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/vue-mixpanel/-/vue-mixpanel-1.0.7.tgz",
+12 -2
View File
@@ -4,9 +4,10 @@
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"compile": "tsc && prettier ./src/**/*.js --write",
"lint": "vue-cli-service lint",
"excel": "office-addin-debugging start public/manifest.xml",
"excel:web": "office-addin-debugging start public/manifest.xml web --document https://skfn3-my.sharepoint.com/:x:/r/personal/connorisadmin_skfn3_onmicrosoft_com/_layouts/15/Doc.aspx?sourcedoc=%7B46999CFC-9B5A-4716-9F71-FECAEA59A6E8%7D",
"excel:web": "office-addin-debugging start public/manifest.xml web --document https://v2k0p-my.sharepoint.com/:x:/r/personal/connor_v2k0p_onmicrosoft_com/_layouts/15/Doc.aspx?sourcedoc=%7B101920A4-0AEA-4E82-B0E5-36E3162F7E86%7D&file=signalisation-permanente.xlsx&action=default&mobileredirect=true&DefaultItemOpen=1&ct=1705007910553&wdOrigin=OFFICECOM-WEB.MAIN.REC&cid=634f97aa-c5a2-411a-a705-412dc57eaaf7&wdPreviousSessionSrc=HarmonyWeb&wdPreviousSession=96a3ad12-6cd0-44ee-bf8b-c523c706e44e",
"excel:web2": "office-addin-debugging start public/manifest.xml web --document https://teocomi-my.sharepoint.com/:x:/g/personal/teocomi_teocomi_onmicrosoft_com/EdxKPPFhnMdDoGclr4J-xJABLf-Ao6CJ902W95kcFPL2fA?e=mD0B8a",
"excel:prod": "office-addin-debugging start public/manifest-prod.xml",
"excel:web-prod": "office-addin-debugging start public/manifest-prod.xml web --document https://teocomi-my.sharepoint.com/:x:/g/personal/teocomi_teocomi_onmicrosoft_com/EdxKPPFhnMdDoGclr4J-xJAB7H6-TRJ5s5ZnXzVdrdFyUg?e=VAFmBN",
@@ -34,6 +35,11 @@
},
"devDependencies": {
"@babel/eslint-parser": "^7.14.3",
"@types/crypto-js": "^4.2.1",
"@types/office-js": "^1.0.364",
"@types/office-runtime": "^1.0.35",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"@vue/cli-plugin-babel": "~4.5.13",
"@vue/cli-plugin-eslint": "~4.5.13",
"@vue/cli-plugin-router": "^4.5.13",
@@ -52,6 +58,7 @@
"prettier": "^2.2.1",
"sass": "^1.32.0",
"sass-loader": "^10.0.0",
"typescript": "^4.9.5",
"vue-cli-plugin-apollo": "~0.22.2",
"vue-cli-plugin-vuetify": "~2.4.0",
"vue-template-compiler": "^2.6.11",
@@ -61,5 +68,8 @@
"> 1%",
"last 2 versions",
"not dead"
]
],
"engines": {
"node": ">=14.0.0 <15.0.0"
}
}
+11 -6
View File
@@ -18,6 +18,7 @@
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title>{{ user.name }}</v-list-item-title>
<v-list-item-subtitle class="smaller-text">{{ serverUrl }}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
@@ -56,7 +57,7 @@
</v-list-item-content>
</v-list-item>
<v-list-item href="https://speckle.systems/tag/excel/" target="_blank">
<v-list-item href="https://v1.speckle.systems/tag/excel/" target="_blank">
<v-list-item-icon>📺</v-list-item-icon>
<v-list-item-content>
@@ -94,6 +95,7 @@
<span class="caption">CONNECTOR</span>
</v-btn>
</v-app-bar>
<global-toast />
<v-main :style="background">
<router-view />
@@ -105,13 +107,15 @@
const crypto = require('crypto')
export default {
name: 'App',
components: {},
components: {
GlobalToast: () => import('@/components/GlobalToast')
},
data: () => ({
drawer: null,
showSnackbar: false,
items: [
{
name: 'Streams',
name: 'Projects',
icon: '📃',
to: '/'
},
@@ -186,9 +190,6 @@ export default {
'@' +
crypto.createHash('md5').update(this.user.email.toLowerCase()).digest('hex').toUpperCase()
console.log(server_id)
console.log(distinct_id)
this.$mixpanel.register({ server_id: server_id, hostApp: 'excel', type: 'action' })
this.$mixpanel.identify(distinct_id)
@@ -311,4 +312,8 @@ export default {
opacity: 1;
transition: opacity 0.15s;
}
.smaller-text {
font-size: 0.7rem; /* Example: Adjust the size as needed */
}
</style>
+66
View File
@@ -0,0 +1,66 @@
<template>
<v-snackbar v-if="text" v-model="snack" app bottom :color="color">
<div v-for="(line, index) in text.split('\n')" :key="index">
{{ line }}
</div>
<template #action="{}">
<v-btn v-if="actionName" small outlined @click="openUrl(url)" @click:append="snack = false">
{{ actionName }}
</v-btn>
<v-btn small icon @click="snack = false">
<v-icon small>mdi-close</v-icon>
</v-btn>
</template>
</v-snackbar>
</template>
<script>
export default {
data() {
return {
snack: false,
color: 'primary',
text: null,
actionName: null,
url: null
}
},
watch: {
snack(newVal) {
if (!newVal) {
this.text = null
this.actionName = null
this.url = null
}
}
},
mounted() {
this.$eventHub.$on('success', (args) => {
this.snack = true
this.color = 'green'
this.text = args.text
this.actionName = args.action ? args.action.name : null
this.url = args.action ? args.action.url : null
})
this.$eventHub.$on('notification', (args) => {
this.snack = true
this.color = 'primary'
this.text = args.text
this.actionName = args.action ? args.action.name : null
this.url = args.action ? args.action.url : null
})
this.$eventHub.$on('error', (args) => {
this.snack = true
this.color = '#CC3300'
this.text = args.text
this.actionName = args.action ? args.action.name : null
this.url = args.action ? args.action.url : null
})
},
methods: {
openUrl(link) {
this.$mixpanel.track('Connector Action', { name: 'Open In Web' })
window.open(link)
}
}
}
</script>
+5 -11
View File
@@ -1,8 +1,8 @@
<template>
<v-card class="pa-5 mb-3" style="transition: all 0.2s" @click="openStream">
<v-card class="px-5 py-2 mb-3" style="transition: all 0.2s" @click="openStream">
<v-row>
<v-col cols="12" sm="8" class="align-self-center">
<div class="subtitle-1">
<div class="text-h6">
{{ stream.name }}
</div>
<div class="caption mb-2 mt-1 text-truncate">
@@ -19,11 +19,7 @@
{{ role }}
</v-chip>
<v-chip
v-tooltip="
stream.branches.totalCount +
' branch' +
(stream.branches.totalCount === 1 ? '' : 'es')
"
v-tooltip="`${stream.branches.totalCount === 1 ? '1 model' : 'models'}`"
outlined
class="ml-2"
small
@@ -33,9 +29,7 @@
</v-chip>
<v-chip
v-tooltip="
stream.commits.totalCount + ' commit' + (stream.commits.totalCount === 1 ? '' : 's')
"
v-tooltip="`${stream.commits.totalCount === 1 ? '1 version' : 'versions'}`"
outlined
class="ml-2"
small
@@ -45,7 +39,7 @@
</v-chip>
</div>
</v-col>
<v-col cols="12" sm="4" class="text-sm-center text-md-right align-self-center">
<v-col cols="12" sm="4" class="text-sm-center text-md-right align-self-center pt-0">
<div>
<span v-for="user in collaboratorsSlice" :key="user.id">
<user-avatar
+228
View File
@@ -0,0 +1,228 @@
<template>
<v-card :class="`my-1 mb-0 pa-0 pb-2 ${localExpand ? 'elevation-3' : 'elevation-0'} my-0`">
<v-card-title>
<v-chip @click="toggleLoadExpand">
<v-icon small class="mr-2">mdi-code-array</v-icon>
{{ keyName }}
<span class="caption ml-2">List ( >{{ (value.length - 1) * 5000 }} elements)</span>
<v-icon class="ml-2" small>
{{ localExpand ? 'mdi-minus' : 'mdi-plus' }}
</v-icon>
</v-chip>
<v-dialog v-model="progress" persistent>
<v-card class="pt-3">
<v-card-text class="caption">
Receiving data from the Speckleverse...
<v-progress-linear
class="mt-2"
:value="`${(numChunksReceived / value.length) * 100}`"
color="primary"
></v-progress-linear>
<v-btn class="mt-3" outlined x-small color="primary" @click="cancel">Cancel</v-btn>
</v-card-text>
</v-card>
</v-dialog>
<v-btn icon small @click="bake">
<v-icon small>mdi-download</v-icon>
</v-btn>
</v-card-title>
<v-card-text v-if="localExpand" class="pb-0 pr-0 pl-3">
<component
:is="entry.type"
v-for="(entry, index) in rangeEntries"
:key="index"
:key-name="entry.key"
:full-key-name="fullKeyName ? `${fullKeyName}.${entry.key}` : entry.key"
:value="entry.value"
:stream-id="streamId"
:commit-id="commitId"
:commit-msg="commitMsg"
:nearest-object-id="nearestObjectId"
:path-from-nearest-object="`${entry.pathFromNearestObject}`"
></component>
</v-card-text>
<v-card-text v-if="localExpand && currentLimit < value.length">
<v-btn small @click="loadMore">Show more</v-btn>
</v-card-text>
<filter-modal ref="modal" />
</v-card>
</template>
<script>
import { bake } from '../plugins/excel'
import objectQuery from '../graphql/object.gql'
import { createClient } from '../vue-apollo'
let ac = new AbortController()
export default {
name: 'ObjectChunkedListViewer',
components: {
FilterModal: () => import('./FilterModal'),
ObjectSpeckleViewer: () => import('./ObjectSpeckleViewer'),
ObjectSimpleViewer: () => import('./ObjectSimpleViewer'),
ObjectValueViewer: () => import('./ObjectValueViewer')
},
props: {
value: {
type: Array,
default: () => []
},
keyName: {
type: String,
default: null
},
fullKeyName: {
type: String,
default: null
},
streamId: {
type: String,
default: null
},
commitId: {
type: String,
default: null
},
commitMsg: {
type: String,
default: null
},
nearestObjectId: {
type: String,
default: null
},
pathFromNearestObject: {
type: String,
default: null
}
},
data() {
return {
localExpand: false,
itemsPerLoad: 3,
currentLimit: 3,
progress: false,
numChunksReceived: 0
}
},
computed: {
rangeEntries() {
let arr = []
let index = 0
const delimiter = ':::'
for (let val of this.range) {
index++
if (Array.isArray(val)) {
arr.push({
key: `${index}`,
value: val,
type: 'ObjectChunkedListViewer',
pathFromNearestObject: this.pathFromNearestObject
? this.pathFromNearestObject + (index - 1) + delimiter
: index - 1 + delimiter
})
} else if (typeof val === 'object' && val !== null) {
if (val.speckle_type && val.speckle_type === 'reference') {
arr.push({
key: `${index}`,
value: val,
type: 'ObjectSpeckleViewer'
})
} else {
arr.push({
key: `${index}`,
value: val,
type: 'ObjectSimpleViewer'
})
}
} else {
arr.push({
key: `${index}`,
value: val,
type: 'ObjectValueViewer'
})
}
}
arr.sort((a, b) => {
if (a.type === b.type) return 0
if (a.type === 'ObjectValueViewer') return -1
return 0
})
return arr
},
range() {
return this.value.slice(0, this.currentLimit)
}
},
methods: {
toggleLoadExpand() {
this.localExpand = !this.localExpand
},
loadMore() {
this.currentLimit += this.itemsPerLoad
},
cancel() {
ac.abort()
},
async bake() {
ac = new AbortController()
this.progress = true
this.$mixpanel.track('Receive')
let allData = []
for (let i = 0; i < this.value.length; i++) {
if (ac.signal.aborted) {
break
}
let speckleObj = await this.getObjectFromCurrentStreamWithId(this.value[i].referencedId)
let speckleType = speckleObj?.data?.stream?.object?.data?.speckle_type
if (speckleType !== 'Speckle.Core.Models.DataChunk') {
continue
}
allData.push(...speckleObj.data.stream.object.data.data)
this.numChunksReceived++
}
let receiverSelection = null
if (!ac.signal.aborted) {
receiverSelection = await bake(
allData,
this.streamId,
this.commitId,
this.commitMsg,
this.$refs.modal,
ac.signal,
this.nearestObjectId,
this.pathFromNearestObject
)
}
if (receiverSelection) {
receiverSelection.fullKeyName = this.fullKeyName
this.$store.dispatch('setReceiverSelection', {
id: this.streamId,
receiverSelection: receiverSelection
})
}
this.progress = false
this.numChunksReceived = 0
},
async getObjectFromCurrentStreamWithId(id) {
let client = createClient()
return await client.query({
query: objectQuery,
variables: {
streamId: this.streamId,
id: id
}
})
}
}
}
</script>
+1 -2
View File
@@ -55,8 +55,7 @@ export default {
},
props: {
value: {
type: Object,
default: () => {}
type: Object
},
streamId: {
type: String,
+37 -12
View File
@@ -60,6 +60,7 @@ export default {
components: {
FilterModal: () => import('./FilterModal'),
ObjectListViewer: () => import('./ObjectListViewer'),
ObjectChunkedListViewer: () => import('./ObjectChunkedListViewer'),
ObjectSimpleViewer: () => import('./ObjectSimpleViewer'),
ObjectValueViewer: () => import('./ObjectValueViewer')
},
@@ -123,8 +124,8 @@ export default {
id: this.value.referencedId
}
},
result() {
this.objectEntries = this.getObjectEntries()
async result() {
this.objectEntries = await this.getObjectEntries()
},
skip() {
return !this.localExpand
@@ -145,14 +146,12 @@ export default {
cancel() {
ac.abort()
},
getObjectEntries() {
async getObjectEntries() {
if (!this.object) return []
let entries = Object.entries(this.object.data)
let arr = []
this.updatedObjectId = this.object.data.id ?? this.nearestObjectId
const delimiter = ':::'
console.log(this.updatedObjectId, delimiter)
console.log(this.nearestObjectId)
for (let [key, val] of entries) {
let name = key
if (key.startsWith('__')) continue
@@ -161,13 +160,29 @@ export default {
if (key === 'speckle_type') name = 'speckle type'
if (Array.isArray(val)) {
arr.push({
key,
name,
value: val,
type: 'ObjectListViewer',
pathFromNearestObject: key + delimiter
})
let speckleTypeOfFirstObj = null
if (val.length > 0 && val[0].referencedId) {
let firstObj = await this.getObjectFromCurrentStreamWithId(val[0].referencedId)
speckleTypeOfFirstObj = firstObj?.data?.stream?.object?.data?.speckle_type
}
if (speckleTypeOfFirstObj === 'Speckle.Core.Models.DataChunk') {
arr.push({
key,
name,
value: val,
type: 'ObjectChunkedListViewer',
pathFromNearestObject: key + delimiter
})
} else {
arr.push({
key,
name,
value: val,
type: 'ObjectListViewer',
pathFromNearestObject: key + delimiter
})
}
} else if (typeof val === 'object' && val !== null) {
if (val.speckle_type && val.speckle_type === 'reference') {
arr.push({
@@ -236,6 +251,16 @@ export default {
}
this.progress = false
},
async getObjectFromCurrentStreamWithId(id) {
let client = createClient()
return await client.query({
query: objectQuery,
variables: {
streamId: this.streamId,
id: id
}
})
}
}
}
+12 -1
View File
@@ -12,7 +12,7 @@
small
icon
color="primary"
:href="`${serverUrl}/streams/${stream.id}/branches/${selectedBranch.name}`"
:href="commitViewUrl()"
target="_blank"
>
<v-icon small>mdi-open-in-new</v-icon>
@@ -313,6 +313,17 @@ export default {
})
},
methods: {
commitViewUrl() {
if (this.$store.state.isFE2) {
if (this.selectedCommit && this.selectedCommit.id) {
return `${this.serverUrl}/projects/${this.stream.id}/models/${this.selectedBranch.id}@${this.selectedCommit.id}`
} else {
return `${this.serverUrl}/projects/${this.stream.id}/models/${this.selectedBranch.id}`
}
} else {
return `${this.serverUrl}/streams/${this.stream.id}/branches/${this.selectedBranch.name}`
}
},
swapReceiver() {
this.isReceiver = !this.isReceiver
this.$emit('loadByCommitId', this.selectedCommitId)
@@ -14,7 +14,9 @@
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5 mb-1">Create a New Branch</v-card-title>
<v-card-title class="text-h5 mb-1">
{{ `Create a New Model` }}
</v-card-title>
<v-card-subtitle class="py-0 my-0 font-italic">under {{ streamName }} stream</v-card-subtitle>
<v-container class="px-6" pb-0>
<v-text-field
@@ -48,6 +50,7 @@
<script>
import gql from 'graphql-tag'
import { createClient } from '../../vue-apollo'
// import { bus } from '@/main'
export default {
name: 'CreateBranchDialog',
@@ -66,7 +69,7 @@ export default {
showCreateBranch: false,
branchName: '',
description: '',
defaultDescription: 'Stream created from SketchUp',
defaultDescription: 'Stream created from Excel',
accountToCreateStream: null
}
},
@@ -76,6 +79,9 @@ export default {
return this.$store.getters.isAuthenticated
}
},
apollo: {
client: createClient()
},
methods: {
async createBranch() {
let res = await this.$apollo.mutate({
+33 -14
View File
@@ -1,18 +1,20 @@
<template>
<v-container fluid class="px-1 pb-0 pt-1">
<v-row>
<v-col class="center-content">
<v-row class="px-3 py-0">
<v-col class="d-flex justify-center pb-0">
<!-- DIALOG: Create New Stream -->
<v-dialog v-model="showCreateNewStream">
<template #activator="{ on, attrs }">
<v-btn class="ma-2 pa-3" x-small v-bind="attrs" v-on="on">
<v-btn block class="pa-3" small v-bind="attrs" v-on="on">
<v-icon dark left>mdi-plus-circle</v-icon>
Create New Stream
{{ `Create New Project` }}
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5">Create a New Stream</v-card-title>
<v-card-title class="text-h5">
{{ `Create New Project` }}
</v-card-title>
<v-container class="px-6" pb-0>
<v-text-field
v-model="streamName"
@@ -20,7 +22,7 @@
hide-details
dense
flat
placeholder="Stream Name (Optional)"
:placeholder="`Project Name (Optional)`"
/>
<v-text-field
v-model="description"
@@ -30,7 +32,7 @@
flat
placeholder="Description (Optional)"
/>
<v-switch v-model="privateStream" :label="'Private Stream'"></v-switch>
<v-switch v-model="privateStream" label="Private Project"></v-switch>
</v-container>
<v-card-actions>
@@ -40,19 +42,24 @@
</v-card-actions>
</v-card>
</v-dialog>
</v-col>
<v-col class="d-flex justify-center">
<!-- DIALOG: Add a Stream by ID or URL -->
<v-dialog v-model="showCreateStreamById">
<template #activator="{ on, attrs }">
<v-btn class="ma-2 pa-3" x-small min-width="163" v-bind="attrs" v-on="on">
<v-btn block class="pa-3" small min-width="163" v-bind="attrs" v-on="on">
<v-icon dark left>mdi-link-plus</v-icon>
Add By ID or URL
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5">Add a Stream by ID or URL</v-card-title>
<v-card-text>Stream IDs and Stream/Branch/Commit URLs are supported.</v-card-text>
<v-card-title class="text-h5">
{{ `Add a Project by ID or URL` }}
</v-card-title>
<v-card-text>
{{ 'Project IDs and Project/Model/Version URLs are supported.' }}
</v-card-text>
<v-container class="px-6">
<v-text-field
v-model="createStreamByIdText"
@@ -60,7 +67,7 @@
hide-details
dense
flat
placeholder="Stream URL"
:placeholder="'Project URL'"
/>
</v-container>
<v-card-actions>
@@ -85,6 +92,7 @@
<script>
import gql from 'graphql-tag'
import { StreamWrapper } from '@/utils/streamWrapper'
import { createClient } from '../../vue-apollo'
export default {
name: 'CreateStreamDialog',
@@ -117,14 +125,26 @@ export default {
return this.$store.getters.isAuthenticated
}
},
apollo: {
$client: createClient()
},
methods: {
async getStream() {
try {
const streamWrapper = new StreamWrapper(
this.createStreamByIdText,
this.accountId,
this.serverUrl
this.serverUrl,
this.$store.state.isFE2
)
const match = streamWrapper.matchUrl(this.createStreamByIdText)
if (match.groups.additionalModels !== undefined) {
this.$eventHub.$emit('error', {
text:
'Multi-model URLs are not supported!\nTry to select just one single model in the web app and paste that in.'
})
return
}
this.$router.push(`/streams/${streamWrapper.streamId}/${streamWrapper.commitId}`)
} catch (e) {
console.log(e)
@@ -150,7 +170,6 @@ export default {
this.streamName = ''
this.description = ''
this.$mixpanel.track('Connector Action', { name: 'Create Stream' })
this.refresh()
return res
}
}
+2 -2
View File
@@ -1,9 +1,9 @@
query Stream($id: String!) {
query Stream($id: String!, $limit: Int!) {
stream(id: $id) {
id
name
role
branches {
branches (limit: $limit) {
totalCount
items {
id
+2
View File
@@ -6,6 +6,8 @@ import { apolloProvider } from './vue-apollo'
import vuetify from './plugins/vuetify'
import '@mdi/font/css/materialdesignicons.css'
Vue.prototype.$eventHub = new Vue()
Vue.config.productionTip = false
import VueTimeago from 'vue-timeago'
+137
View File
@@ -0,0 +1,137 @@
'use strict'
Object.defineProperty(exports, '__esModule', { value: true })
exports.SerializedBase = exports.ObjectReference = exports.DataChunk = exports.BaseObjectSerializer = void 0
/* eslint-disable @typescript-eslint/ban-types */
const crypto_js_1 = require('crypto-js')
/**
* Serializer for Speckle objects written in Typescript
*/
class BaseObjectSerializer {
constructor(transports) {
this.transports = transports
}
async SerializeBase(object) {
return await this.SerializeBaseWithClosures(object, [])
}
async SerializeBaseWithClosures(object, closures) {
const thisClosure = new Map()
closures.push(thisClosure)
const converted = await this.PreserializeEachObjectProperty(object, closures)
let json = this.SerializeMap(converted)
const id = this.GetId(json)
converted.set('id', id)
this.AddSelfToParentClosures(id, closures)
if (thisClosure.size > 0) {
converted.set('__closure', Object.fromEntries(thisClosure))
}
converted.set('totalChildrenCount', thisClosure.size)
json = this.SerializeMap(converted)
await this.StoreObject(id, converted)
return new SerializedBase(id, json)
}
async PreserializeObject(object, closures) {
if (!(object instanceof Object) || object instanceof String) {
return object
}
if (object instanceof DataChunk) {
const serialized = await this.SerializeBaseWithClosures(object, [...closures])
return new ObjectReference(serialized.id)
}
if (object instanceof Array) {
// chunk array into 5000 by default
const chunkSize = 5000
if (object.length > chunkSize) {
let serializedCount = 0
const data = new Array()
while (serializedCount < object.length) {
const dataChunkCount = Math.min(chunkSize, object.length - serializedCount)
data.push(new DataChunk(object.slice(serializedCount, serializedCount + dataChunkCount)))
serializedCount += dataChunkCount
}
return await this.PreserializeObject(data, closures)
}
const convertedList = new Array()
for (const element of object) {
convertedList.push(await this.PreserializeObject(element, closures))
}
return convertedList
}
if (object instanceof Object) {
return Object.fromEntries(await this.PreserializeEachObjectProperty(object, closures))
}
throw new Error(`Cannot serialize object ${object}`)
}
async PreserializeEachObjectProperty(o, closures) {
const converted = new Map()
const getters = Object.entries(Object.getOwnPropertyDescriptors(Reflect.getPrototypeOf(o)))
.filter(([key, descriptor]) => typeof descriptor.get === 'function' && key !== '__proto__')
.map(([key]) => key)
const objectKeys = new Array()
objectKeys.push(...Object.keys(o))
objectKeys.push(...getters)
for (const key of objectKeys) {
const objKey = key
converted.set(
BaseObjectSerializer.CleanKey(key),
await this.PreserializeObject(o[objKey], closures)
)
}
return converted
}
static CleanKey(originalKey) {
const newStringChars = []
for (let i = 0; i < originalKey.length; i++) {
if (i == 1 && originalKey[i] == '@' && originalKey[0] == '@') {
continue
}
if (this.disallowedCharacters.includes(originalKey[i])) {
continue
}
newStringChars.push(originalKey[i])
}
return newStringChars.join('')
}
async StoreObject(objectId, object) {
for (const transport of this.transports) {
await transport.SaveObject(objectId, object)
}
}
SerializeMap(map) {
return JSON.stringify(Object.fromEntries(map))
}
GetId(json) {
return (0, crypto_js_1.MD5)(json).toString(crypto_js_1.enc.Hex)
}
AddSelfToParentClosures(objectId, closureTables) {
// only go to closureTable length - 1 because the last closure table belongs to the object with the
// provided id
const parentClosureTablesCount = closureTables.length - 1
for (let parentLevel = 0; parentLevel < parentClosureTablesCount; parentLevel++) {
const childDepth = parentClosureTablesCount - parentLevel
closureTables[parentLevel].set(objectId, childDepth)
}
}
}
exports.BaseObjectSerializer = BaseObjectSerializer
BaseObjectSerializer.disallowedCharacters = ['.', '/']
class DataChunk {
constructor(data) {
this.speckle_type = 'Speckle.Core.Models.DataChunk'
this.data = data ?? []
}
}
exports.DataChunk = DataChunk
class ObjectReference {
constructor(referencedId) {
this.referencedId = referencedId
this.speckle_type = 'reference'
}
}
exports.ObjectReference = ObjectReference
class SerializedBase {
constructor(id, json) {
this.id = id
this.json = json
}
}
exports.SerializedBase = SerializedBase
+166
View File
@@ -0,0 +1,166 @@
/* eslint-disable @typescript-eslint/ban-types */
import { MD5, enc } from 'crypto-js'
/**
* Serializer for Speckle objects written in Typescript
*/
export class BaseObjectSerializer {
constructor(public transports: ITransport[]) {}
public async SerializeBase(object: object): Promise<SerializedBase> {
return await this.SerializeBaseWithClosures(object, [])
}
private async SerializeBaseWithClosures(object: object, closures: Array<Map<string, number>>) {
const thisClosure = new Map<string, number>()
closures.push(thisClosure)
const converted = await this.PreserializeEachObjectProperty(object, closures)
let json = this.SerializeMap(converted)
const id = this.GetId(json)
converted.set('id', id)
this.AddSelfToParentClosures(id, closures)
if (thisClosure.size > 0) {
converted.set('__closure', Object.fromEntries(thisClosure))
}
converted.set('totalChildrenCount', thisClosure.size)
json = this.SerializeMap(converted)
await this.StoreObject(id, converted)
return new SerializedBase(id, json)
}
private async PreserializeObject(
object: any,
closures: Array<Map<string, number>>
): Promise<any> {
if (!(object instanceof Object) || object instanceof String) {
return object
}
if (object instanceof DataChunk) {
const serialized = await this.SerializeBaseWithClosures(object, [...closures])
return new ObjectReference(serialized.id)
}
if (object instanceof Array) {
// chunk array into 5000 by default
const chunkSize = 5000
if (object.length > chunkSize) {
let serializedCount = 0
const data = new Array<DataChunk>()
while (serializedCount < object.length) {
const dataChunkCount = Math.min(chunkSize, object.length - serializedCount)
data.push(new DataChunk(object.slice(serializedCount, serializedCount + dataChunkCount)))
serializedCount += dataChunkCount
}
return await this.PreserializeObject(data, closures)
}
const convertedList = new Array<any>()
for (const element of object) {
convertedList.push(await this.PreserializeObject(element, closures))
}
return convertedList
}
if (object instanceof Object) {
return Object.fromEntries(await this.PreserializeEachObjectProperty(object, closures))
}
throw new Error(`Cannot serialize object ${object}`)
}
private async PreserializeEachObjectProperty(
o: object,
closures: Array<Map<string, number>>
): Promise<Map<string, any>> {
const converted = new Map<string, any>()
const getters = Object.entries(Object.getOwnPropertyDescriptors(Reflect.getPrototypeOf(o)))
.filter(([key, descriptor]) => typeof descriptor.get === 'function' && key !== '__proto__')
.map(([key]) => key)
const objectKeys = new Array<string>()
objectKeys.push(...Object.keys(o))
objectKeys.push(...getters)
for (const key of objectKeys) {
const objKey = key as keyof object
converted.set(
BaseObjectSerializer.CleanKey(key),
await this.PreserializeObject(o[objKey], closures)
)
}
return converted
}
private static disallowedCharacters: string[] = ['.', '/']
private static CleanKey(originalKey: string): string {
const newStringChars = []
for (let i = 0; i < originalKey.length; i++) {
if (i == 1 && originalKey[i] == '@' && originalKey[0] == '@') {
continue
}
if (this.disallowedCharacters.includes(originalKey[i])) {
continue
}
newStringChars.push(originalKey[i])
}
return newStringChars.join('')
}
private async StoreObject(objectId: string, object: Map<string, any>) {
for (const transport of this.transports) {
await transport.SaveObject(objectId, object)
}
}
private SerializeMap(map: Map<string, any>): string {
return JSON.stringify(Object.fromEntries(map))
}
private GetId(json: string): string {
return MD5(json).toString(enc.Hex)
}
private AddSelfToParentClosures(objectId: string, closureTables: Array<Map<string, number>>) {
// only go to closureTable length - 1 because the last closure table belongs to the object with the
// provided id
const parentClosureTablesCount = closureTables.length - 1
for (let parentLevel = 0; parentLevel < parentClosureTablesCount; parentLevel++) {
const childDepth = parentClosureTablesCount - parentLevel
closureTables[parentLevel].set(objectId, childDepth)
}
}
}
export class DataChunk implements IBase {
public speckle_type = 'Speckle.Core.Models.DataChunk'
public data: any[]
constructor(data: any[] | null) {
this.data = data ?? []
}
}
export class ObjectReference implements IBase {
public speckle_type = 'reference'
constructor(public referencedId: string) {}
}
export interface IBase {
readonly speckle_type: string
}
export interface ITransport {
SaveObject(id: string, object: Map<string, any>): Promise<void>
}
export class SerializedBase {
constructor(public id: string, public json: string) {}
}
+33
View File
@@ -0,0 +1,33 @@
'use strict'
Object.defineProperty(exports, '__esModule', { value: true })
exports.ExcelSheetDataRetriever = void 0
class ExcelSheetDataRetriever {
// Takes address as a string formatted like this 'Sheet1!A1:B4' and returns a 2d array of values.
// This queries the document in batches in order to avoid api errors about requests that are too large
static async GetValuesAsNestedList(context, address) {
const sheetName = address.split('!')[0].replace(/'/g, '')
const rangeAddress = address.split('!')[1]
const sheet = context.workbook.worksheets.getItem(sheetName)
const totalRange = sheet.getRange(rangeAddress)
totalRange.load('columnCount, columnIndex, rowCount, rowIndex')
await context.sync()
let numRowsRetrieved = 0
const chunkSize = 5000
let values = []
while (numRowsRetrieved < totalRange.rowCount) {
const numRowsToRetrieve = Math.min(chunkSize, totalRange.rowCount - numRowsRetrieved)
const currentRange = sheet.getRangeByIndexes(
totalRange.rowIndex + numRowsRetrieved,
totalRange.columnIndex,
numRowsToRetrieve,
totalRange.columnCount
)
currentRange.load('values')
await context.sync()
values = values.concat(currentRange.values)
numRowsRetrieved += numRowsToRetrieve
}
return values
}
}
exports.ExcelSheetDataRetriever = ExcelSheetDataRetriever
+37
View File
@@ -0,0 +1,37 @@
export class ExcelSheetDataRetriever {
// Takes address as a string formatted like this 'Sheet1!A1:B4' and returns a 2d array of values.
// This queries the document in batches in order to avoid api errors about requests that are too large
static async GetValuesAsNestedList(
context: Excel.RequestContext,
address: string
): Promise<any[][]> {
const sheetName = address.split('!')[0].replace(/'/g, '')
const rangeAddress = address.split('!')[1]
const sheet = context.workbook.worksheets.getItem(sheetName)
const totalRange = sheet.getRange(rangeAddress)
totalRange.load('columnCount, columnIndex, rowCount, rowIndex')
await context.sync()
let numRowsRetrieved = 0
const chunkSize = 5000
let values: any[][] = []
while (numRowsRetrieved < totalRange.rowCount) {
const numRowsToRetrieve = Math.min(chunkSize, totalRange.rowCount - numRowsRetrieved)
const currentRange = sheet.getRangeByIndexes(
totalRange.rowIndex + numRowsRetrieved,
totalRange.columnIndex,
numRowsToRetrieve,
totalRange.columnCount
)
currentRange.load('values')
await context.sync()
values = values.concat(currentRange.values)
numRowsRetrieved += numRowsToRetrieve
}
return values
}
}
+57
View File
@@ -0,0 +1,57 @@
'use strict'
Object.defineProperty(exports, '__esModule', { value: true })
exports.ServerTransport = void 0
class ServerTransport {
/**
*
*/
constructor(serverUrl, token, streamId) {
this.serverUrl = serverUrl
this.token = token
this.streamId = streamId
}
async SaveObject(id, map) {
const query = `mutation objectCreate ($object: ObjectCreateInput!) {objectCreate(objectInput: $object)}`
await fetch(`${this.serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
object: {
streamId: this.streamId,
objects: [Object.fromEntries(map)]
}
}
})
})
// const data = (await response.json()) as any
}
async CreateCommit(branchName, objectId, message) {
const query = `mutation commitCreate($myCommit: CommitCreateInput!){ commitCreate(commit: $myCommit)}`
await fetch(`${this.serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
myCommit: {
streamId: this.streamId,
branchName: branchName,
objectId: objectId,
message: message ? message : 'Data from Excel',
sourceApplication: 'excel'
}
}
})
})
// const data = (await response.json()) as any
}
}
exports.ServerTransport = ServerTransport
+57
View File
@@ -0,0 +1,57 @@
import { ITransport } from './BaseObjectSerializer'
export class ServerTransport implements ITransport {
/**
*
*/
constructor(private serverUrl: string, private token: string, private streamId: string) {}
async SaveObject(id: string, map: Map<string, any>): Promise<void> {
const query = `mutation objectCreate ($object: ObjectCreateInput!) {objectCreate(objectInput: $object)}`
await fetch(`${this.serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
object: {
streamId: this.streamId,
objects: [Object.fromEntries(map)]
}
}
})
})
// const data = (await response.json()) as any
}
async CreateCommit(branchName: string, objectId: string, message: string | null) {
const query = `mutation commitCreate($myCommit: CommitCreateInput!){ commitCreate(commit: $myCommit)}`
await fetch(`${this.serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
myCommit: {
streamId: this.streamId,
branchName: branchName,
objectId: objectId,
message: message ? message : 'Data from Excel',
sourceApplication: 'excel'
}
}
})
})
// const data = (await response.json()) as any
}
}
+10 -9
View File
@@ -11,6 +11,7 @@ import {
onTableChanged,
onTableDeleted
} from './dataTable.js'
import { ExcelSheetDataRetriever } from './ExcelSheetDataRetreiver.js'
const unflatten = require('flat').unflatten
@@ -214,7 +215,7 @@ export function hideRowOrColumn(sheet, columnIndex = -1, rowIndex = -1) {
}
}
async function addIdDataToObjectData() {
async function addIdDataToObjectData(arrayData) {
if (
arrayData.length != arrayIdData.length ||
arrayData.length <= 1 ||
@@ -599,14 +600,13 @@ export async function bake(
} else if (previousHeaders) {
selectedHeaders = filterArrayData(previousHeaders, arrayData)
}
console.log(arrayData)
}
if (signal.aborted) return
await addIdDataToObjectData()
await bakeArray(arrayData, rowStart, colStart, context)
selectedHeaders ??= arrayData
await addIdDataToObjectData(selectedHeaders)
await bakeArray(selectedHeaders, rowStart, colStart, context)
}
await context.sync()
@@ -668,11 +668,12 @@ export async function send(savedStream, streamId, branchName, message) {
let sheetName = savedStream.selection.split('!')[0].replace(/'/g, '')
let rangeAddress = savedStream.selection.split('!')[1]
let sheet = context.workbook.worksheets.getItem(sheetName)
let range = sheet.getRange(rangeAddress)
range.load('values')
await context.sync()
let values = range.values
let values = await ExcelSheetDataRetriever.GetValuesAsNestedList(
context,
savedStream.selection
)
let data = []
// check for specific conversion
+58 -42
View File
@@ -6,6 +6,8 @@ import ObjectLoader from '@speckle/objectloader'
import streamsModule from './streams'
import userModule from './user'
import router from '../router'
import { BaseObjectSerializer } from '../plugins/BaseObjectSerializer'
import { ServerTransport } from '../plugins/ServerTransport'
const xml2js = require('xml2js')
@@ -100,7 +102,8 @@ const vuexExcel = new VuexPersistence({
export default new Vuex.Store({
state: {
snackbar: {}
snackbar: {},
isFE2: false
},
plugins: [vuexLocal.plugin, vuexExcel.plugin],
getters: {
@@ -109,9 +112,16 @@ export default new Vuex.Store({
mutations: {
SET_SNACKBAR(state, value) {
state.snackbar = value
},
UPDATE_IS_FE2(state, value) {
localStorage.setItem('frontend2', value)
state.isFE2 = value
}
},
actions: {
updateIsFE2({ commit }, value) {
commit('UPDATE_IS_FE2', value)
},
async redirect(_, data) {
//go to login and refresh token
window.location = `${data.serverUrl}/authn/verify/${process.env.VUE_APP_SPECKLE_ID}/${data.challenge}`
@@ -140,11 +150,13 @@ export default new Vuex.Store({
dialog.close()
await dispatch('exchangeAccessCode', args.message)
await dispatch('hasValidToken')
await dispatch('getServerInfo')
router.push('/')
})
}
)
},
async hasValidToken({ state, dispatch }) {
if (localStorage.getItem(TOKEN) === null) return false
await dispatch('getUser')
@@ -161,6 +173,7 @@ export default new Vuex.Store({
localStorage.removeItem('serverUrl')
localStorage.removeItem(REFRESH_TOKEN)
localStorage.removeItem('uuid')
localStorage.removeItem('frontend2')
window.location = window.location.origin
},
@@ -193,51 +206,14 @@ export default new Vuex.Store({
}
},
async createCommit(context, { streamId, branchName, message, object }) {
let query = `mutation objectCreate ($object: ObjectCreateInput!) {objectCreate(objectInput: $object)}`
let serverUrl = localStorage.getItem('serverUrl')
let token = localStorage.getItem(TOKEN)
let response = await fetch(`${serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
object: {
streamId: streamId,
objects: [object]
}
}
})
})
let data = await response.json()
let objectId = data.data.objectCreate[0]
const serverTransport = new ServerTransport(serverUrl, token, streamId)
const serializer = new BaseObjectSerializer([serverTransport])
const serialized = await serializer.SerializeBase(object)
query = `mutation commitCreate($myCommit: CommitCreateInput!){ commitCreate(commit: $myCommit)}`
response = await fetch(`${serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
myCommit: {
streamId: streamId,
branchName: branchName,
objectId: objectId,
message: message ? message : 'Data from Excel',
sourceApplication: 'excel'
}
}
})
})
await serverTransport.CreateCommit(branchName, serialized.id, message)
},
async receiveCommit(context, { sourceApplication, streamId, commitId, message }) {
let query = `mutation objectReceive ($myInput:CommitReceivedInput!) {commitReceive(input:$myInput)}`
@@ -264,6 +240,46 @@ export default new Vuex.Store({
})
})
},
async getServerInfo({ dispatch }) {
let serverUrl = localStorage.getItem('serverUrl')
// Now, to check for a specific header
const isFrontend2Server = (headers) => {
const HEADER = 'x-speckle-frontend-2'
const headerValue = headers.get(HEADER)
if (headerValue === null) {
return false
}
const value = headerValue.toLowerCase() === 'true'
if (headerValue !== 'true' && headerValue !== 'false') {
throw new Error(
`Headers contained ${HEADER} header, but value ${headerValue} could not be parsed to a bool`
)
}
return value
}
// Use the function to check the header
try {
let response = await fetch(serverUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
let isFE2Server = isFrontend2Server(response.headers)
await dispatch('updateIsFE2', isFE2Server)
console.log('Is Frontend2 Server:', isFE2Server)
} catch (error) {
console.warn(error.message)
// TODO: fallback is FE1, this should be changed later
await dispatch('updateIsFE2', false)
}
},
async getUser(context) {
try {
let query = `query {
+14 -5
View File
@@ -1,7 +1,11 @@
require('url')
export class StreamWrapper {
constructor(streamIdOrUrl, accountId, serverUrl) {
constructor(streamIdOrUrl, accountId, serverUrl, isFE2) {
this.isFE2 = isFE2
this.streamsKey = this.isFE2 ? 'projects/' : 'streams/'
this.branchesKey = this.isFE2 ? 'models/' : 'branches/'
this.commitsKey = this.isFE2 ? 'versions/' : 'commits/'
this.originalOutput = streamIdOrUrl
try {
this.streamWrapperFromUrl(streamIdOrUrl)
@@ -12,12 +16,17 @@ export class StreamWrapper {
}
}
matchUrl(streamUrl) {
const fe2UrlRegex = /\/projects\/(?<projectId>[\w\d]+)(?:\/models\/(?<model>[\w\d]+(?:@[\w\d]+)?)(?:,(?<additionalModels>[\w\d]+(?:@[\w\d]+)?))*)?/
return fe2UrlRegex.exec(streamUrl)
}
streamWrapperFromUrl(streamUrl) {
this.url = new URL(streamUrl)
this.segments = this.url.pathname.split('/').map((segment) => segment + '/')
this.serverUrl = this.url.origin
if (this.segments.length >= 4 && this.segments[3]?.toLowerCase() === 'branches/') {
if (this.segments.length >= 4 && this.segments[3]?.toLowerCase() === this.branchesKey) {
this.streamId = this.segments[2].replace('/', '')
if (this.segments.length > 5) {
let branchSegments = this.segments.slice(4, this.segments.length - 1)
@@ -28,7 +37,7 @@ export class StreamWrapper {
} else {
switch (this.segments.length) {
case 3: // ie http://speckle.server/streams/8fecc9aa6d
if (this.segments[1].toLowerCase() === 'streams/')
if (this.segments[1].toLowerCase() === this.streamsKey)
this.streamId = this.segments[2].replace('/', '')
else throw new Error(`Cannot parse ${this.originalOutput} into a stream wrapper class`)
break
@@ -40,7 +49,7 @@ export class StreamWrapper {
break
case 5: // ie http://speckle.server/streams/8fecc9aa6d/commits/76a23d7179
switch (this.segments[3].toLowerCase()) {
case 'commits/':
case this.commitsKey:
this.streamId = this.segments[2].replace('/', '')
this.commitId = this.segments[4].replace('/', '')
break
@@ -49,7 +58,7 @@ export class StreamWrapper {
this.branchName = this.segments[3].replace('/', '')
this.commitId = this.segments[4].replace('/', '')
break
case 'branches/':
case this.branchesKey:
this.streamId = this.segments[2].replace('/', '')
this.branchName = this.segments[4].replace('/', '')
break
+3 -3
View File
@@ -43,7 +43,9 @@ export default {
name: 'Login',
data: () => ({
serverUrl:
process.env.NODE_ENV === 'development' ? 'https://latest.speckle.dev' : 'https://speckle.xyz',
process.env.NODE_ENV === 'development'
? 'https://latest.speckle.systems'
: 'https://app.speckle.systems',
validForm: true,
serverError: '',
serverUrlRules: [
@@ -53,8 +55,6 @@ export default {
'URL must be valid, with no trailing slash'
]
}),
mounted() {},
methods: {
async login() {
//when for is not visible (coming from web) don't validate
+5 -4
View File
@@ -17,7 +17,7 @@
small
icon
color="primary"
:href="`${serverUrl}/streams/${savedStream.id}`"
:href="`${serverUrl}/${$store.state.isFE2 ? 'projects' : 'streams'}/${savedStream.id}`"
target="_blank"
>
<v-icon small>mdi-open-in-new</v-icon>
@@ -127,13 +127,15 @@ export default {
}
},
apollo: {
$client: createClient(),
stream: {
prefetch: true,
query: streamQuery,
fetchPolicy: 'network-only',
variables() {
return {
id: this.streamId
id: this.streamId,
limit: 100
}
},
result() {
@@ -178,7 +180,6 @@ export default {
console.log(this.error)
}
},
$client: createClient(),
$subscribe: {
streamUpdated: {
query: gql`
@@ -423,7 +424,7 @@ export default {
speckleIdRange.load('text')
await context.sync()
let idsInViewer = new Array()
let idsInViewer = []
for (let i = 0; i < speckleIdRange.text?.length; i++) {
for (let j = 0; j < speckleIdRange.text[i].length; j++) {
if (speckleIdRange.text[i][j].length < 32) continue
+2 -4
View File
@@ -1,7 +1,7 @@
<template>
<v-container>
<v-container class="pa-0">
<v-row align="center">
<v-col cols="12" align="center" class="mt-5">
<v-col cols="12" align="center" class="mt-3">
<p v-if="search" class="subtitle">No streams found 🧐</p>
<div v-else-if="!$apollo.loading && filteredStreams && filteredStreams.length == 0">
<p class="subtitle">
@@ -122,7 +122,6 @@ export default {
},
computed: {
isAuthenticated() {
console.log(this.user)
return this.$store.getters.isAuthenticated
},
user() {
@@ -132,7 +131,6 @@ export default {
return this.$store.getters.serverUrl
},
filteredStreams() {
console.log('user', this.$store.state.user.user)
if (!this.streams.items) return null
let savedStreams = this.streams.items.filter(
+109
View File
@@ -0,0 +1,109 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */,
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}