├── .env.sample ├── .gitignore ├── .vscode ├── launch.json └── tw-snippets.code-snippets ├── CHANGELOG.md ├── LICENSE ├── README.md ├── metadata.xml ├── package-lock.json ├── package.json ├── src ├── ConfigurationTableShapes │ ├── LinkedPortInfo.ts │ └── PortInfo.ts ├── MyCoreUIMashup.tsx.example ├── MyDataShape.ts ├── MyDatabase.ts ├── MyInstance.ts ├── MyMashup.tsx ├── MyMashupGridConfiguration.json ├── MyMashupStyles.css ├── MyOrganization.ts ├── MyProduct.ts ├── MyShape.ts ├── MyStyleLibrary.ts ├── MyTemplate.ts ├── MyThing.ts ├── MyUserList.ts └── TestThing.ts ├── tsconfig.json ├── twconfig.json └── ui-static ├── MyCustomUIPlugin.js ├── defaults.json └── widgets.d.ts /.env.sample: -------------------------------------------------------------------------------- 1 | THINGWORX_SERVER=http://localhost:8014 2 | THINGWORX_USER=Administrator 3 | THINGWORX_PASSWORD=Administrator12345 4 | THINGWORX_APPKEY=abc-acfdf 5 | DATABASE_JDBC_STRING=jdbc:mydb:/server:port/database?param=value 6 | DATABASE_PASSWORD=Administrator12345 7 | DATABASE_USERNAME=Adminsitrator 8 | MY_CUSTOM_VARIABLE=Test 9 | LOG_FILE_PATH=/log.txt 10 | MOCK_REQUEST_DATA=false 11 | TESTS_INCLUDED=false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | static/gen 4 | zip 5 | tw_imports 6 | .DS_Store 7 | .env -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "thingworx-vscode", 6 | "request": "attach", 7 | "name": "Attach to Thingworx", 8 | "thingworxDomain": "localhost", 9 | "thingworxPort": 8015, 10 | "thingworxAppKey": "abc-def", 11 | "useSSL": false 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /.vscode/tw-snippets.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "Subscription": { 3 | "prefix": ["sub", "twsub"], 4 | "body": "@subscription('${2:thingName}', '${3:eventName}')\n${1:name}(alertName: STRING, eventData: INFOTABLE<${4:dataShapeName}>, eventName: STRING, eventTime: DATETIME, source: STRING, sourceProperty: STRING) {\n\t$5\n}\n", 5 | "description": "Inserts a subscription definition." 6 | }, 7 | "Remote Property": { 8 | "prefix": ["rprop", "twrprop"], 9 | "body": "@remote('${3:remoteName}'${4: , {cacheTime: ${5:0}, foldType: '${6:NONE}', pushType: '${7:Value}'}})\n${1:propertyName}!: ${2:baseType};", 10 | "description": "Inserts a remote property." 11 | } 12 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 4 Aug 2024 2 | 3 | Resolves an issue where bindings to and from mashup parameters did not work. 4 | 5 | # 25 May 2024 6 | 7 | Adds support for specifying visibility permissions on mashups. ([kklorenzotesta](https://github.com/kklorenzotesta)) 8 | 9 | Resolves an issue where using `this` in an arrow function produced incorrect code. ([kklorenzotesta](https://github.com/kklorenzotesta)) 10 | 11 | # 20 Apr 2024 12 | 13 | Updates the Typescript version to 5.4. ([stefan-lacatus](https://github.com/stefan-lacatus)) 14 | 15 | Fixes a build error when using recursive global functions. ([stefan-lacatus](https://github.com/stefan-lacatus)) 16 | 17 | Fixes a build error when certain binding types such as `MASHUPNAME` were used. 18 | 19 | Adds the typings for the widgets added in Thingworx 9.4. 20 | 21 | # 13 Apr 2024 22 | 23 | Fixes a bug where enum members were not properly inlined when referenced by global functions defined on the first line of a file. 24 | 25 | Update the typings for the `DisableSubscription` and `EnableSubscription` to support the new signatures in Thingworx 9. ([kklorenzotesta](https://github.com/kklorenzotesta)) 26 | 27 | # 6 Apr 2024 28 | 29 | Resolves an issue where certain global functions were not properly copied over into services. 30 | 31 | # 31 Mar 2024 32 | 33 | Adds support for creating Mashups, StyleDefinitions and StateFormatting entities and for importing CSS and JSON files into mashup entities. 34 | 35 | # 6 Dec 2023 36 | 37 | Resolves an issue where resulting extensions were not compatible with Thingworx 8.5 even when avoiding 9.0+ features. 38 | 39 | # 28 Mar 2023 40 | 41 | Adds support for removing entities or branches of code from the final build based on environment variables. 42 | 43 | Adds support for invoking superclass implementation of services if the superclass is part of the project. 44 | 45 | Adds support for inline SQL statements on non-database entities by specifying the database on which the SQL code will run. 46 | 47 | # 11 Dec 2022 48 | 49 | Added support for generating trace builds by using the `--trace` flag. 50 | 51 | Added type guards for the built-in `ImplementsShape` and `IsDerivedFromTemplate` methods. 52 | 53 | Added a new `copyEntities` option to `twconfig.json` that, when specified, will add any XML file in the project's src directory to the build output, allowing unsupported entities to be included in the extension. 54 | 55 | Fixed incorrect code generation when specifying default values for service parameters. 56 | 57 | # 21 May 2022 58 | 59 | Improved the error messages that appear when declaring a service parameter with certain unsupported types. ([stefan-lacatus](https://github.com/stefan-lacatus)) 60 | 61 | Adds support for the `@category` decorator to specify the category of properties, events and services. ([stefan-lacatus](https://github.com/stefan-lacatus)) 62 | 63 | Improves the type inferrence of expressions used in inline SQL statements. 64 | 65 | # 15 May 2022 66 | 67 | Adds support for data shape inheritance. ([stefan-lacatus](https://github.com/stefan-lacatus)) 68 | 69 | Adds support for using the `declare` modifier on class fields, which are ignored when emitting. ([stefan-lacatus](https://github.com/stefan-lacatus)) 70 | 71 | # 4 May 2022 72 | 73 | Resolves an issue with inline SQL statements where code with syntax errors was generated. 74 | 75 | # 2 May 2022 76 | 77 | Adds support for using inline SQL statements in database services. These are extracted into their own SQL services when building. 78 | 79 | Adds support for using Typescript's built-in `override` keyword in place of the `@override` decorator and sets the `noImplicitOverride` flag to `true` in `tsconfig.json`. 80 | 81 | Adds a schema to `twconfig.json` for better documentation and validation. 82 | 83 | Improves the speed with which declarations and api exports are generated. 84 | 85 | Adds additional validation to overriden members, reporting certain errors that would lead to a failed import during build time. 86 | 87 | Adds support for generating static field definitions objects and a function to create an infotable given an array of row objects and a data shape name when generating an API declarations file, to be used when invoking thingworx endpoints. ([stefan-lacatus](https://github.com/stefan-lacatus)) 88 | 89 | # 22 Apr 2022 90 | 91 | Adds support for declaring and using global functions. Whenever a service makes a call to a global function, that global function's definition will be copied to the service's code during compilation. This will also copy over any other global functions that the initial function calls. Added support for a new property `"globalFunctions"` in `twconfig.json` to control whether these declarations are allowed. 92 | 93 | Adds experimental support for generating an API declarations file that can be consumed by a separate frontend or node project. ([stefan-lacatus](https://github.com/stefan-lacatus)) 94 | 95 | # 8 Apr 2022 96 | 97 | Adds support for generating method helpers, that are useful variables that can be used for logging. The following variables can be enabled: `METHOD_NAME`, `CLASS_NAME`, `FILE_PATH` and `LOG_PREFIX`. Added support for a new property `"methodHelpers"` in `twconfig.json` to control which of these are generated. ([stefan-lacatus](https://github.com/stefan-lacatus)) 98 | 99 | # 28 Mar 2022 100 | 101 | Resolves an issue that caused multiproject builds to fail on windows systems. 102 | 103 | Added support for using inferred types in property declarations and data shape field declarations. ([stefan-lacatus](https://github.com/stefan-lacatus)) 104 | 105 | Resolved an issue that cause base type errors to report the invalid base types as `undefined` rather than the actual types. 106 | 107 | Resolved an issue that caused permission decorators applied to templates to be incorrectly emitted as runtime permissions instead of instance runtime permissions. ([stefan-lacatus](https://github.com/stefan-lacatus)) 108 | 109 | # 19 Mar 2022 110 | 111 | Moved the various build and dependency tasks into an external `bm-thing-cli` command line tool and removed `gulpfile.js` as well as the gulp-related dependencies. Additionally, the decorator and default entity declarations have been moved into `bm-thing-transformer`. These changes should make it easier to update to new verions as fewer local files will need to be updated. Existing gulp projects can use the `npx twc upgrade` command to update to the new format. 112 | 113 | Added support for creating and building multiple thingworx projects from the same typescript project. These can be built into either separate extensions for each project or a single extension containing all projects. Multi-project mode can be enabled by using the `npx twc add-project` command. Local project dependencies can be specified in each project's `tsconfig.json` file, by adding a relative to another project's source folder (e.g. `"../"`). 114 | 115 | Added support for SQL services using the `@SQLQuery` and `@SQLCommand` decorators, and the `SQLQuery` and `SQLCommand` tagged literal functions. Added an example database thing using them. 116 | 117 | Added a new `generateThingInstances` option to `twconfig.json` that, when enabled, will cause a number of random things to be declared whenever a thing template or thing shape is defined. This allows types that use subsets of `keyof Things` to not resolve to `never` and enable proper autocomplete and type checking for `THINGNAME` values for types that have no instances by default. The generated names change during every build to prevent referencing those virtual entities directly by accident. 118 | 119 | The `GROUPNAME` type is now properly defined as `keyof Groups` instead of `string`. 120 | 121 | The `minimumThingWorxVersion` option can now be specified in `twconfig.json` instead of `package.json`. 122 | 123 | Added the type definitions for the following filter types: `Matches`, `NotMatches`, `TAGGED`, `NOTTAGGED`, `IN`, `NOTIN`, `MissingValue`, `NotMissingValue`, `Near`, `NotNear`. 124 | 125 | Improved type checking on queries by matching the type of the property specified by `fieldName` to the type of the values specified in the filter. For example, the following query will report a type error if the `"pressure"` property is a `NUMBER`: 126 | ```ts 127 | ThingShapes.ExampleThingShape.QueryImplementingThingsWithData({query: { 128 | filters: { 129 | type: 'EQ', 130 | fieldName: 'pressure', // An error is reported here because '3' cannot be assigned to 'pressure' property 131 | value: '3', 132 | } 133 | }}) 134 | ``` 135 | 136 | Resolves various issues with importing declarations from a thingworx server: 137 | - When importing declaration from thingworx, importing entities with the same names but different types will no longer lead to duplicate identifiers. 138 | - Importing entities whose names contain non-alphanumeric characters is now supported. 139 | - Importing data things using data shapes whose names are not valid typescript identifiers will now no longer lead to compile time errors. 140 | - Importing templates no longer causes some superclass members to be redeclared. 141 | - Importing entities whose member names are not valid typescript identifiers is now supported. 142 | 143 | Added the definitions for the `QueryImplementingThingsOptimized` and `QueryImplementingThingsOptimizedWithTotalCount` services on thing templates and thing shapes. Additionally improved the typings for `GetImplementingThingsWithData` so that intellisense can autocomplete all property names on the resulting infotable rows. 144 | 145 | Added the definitions for a `StyleThemeEntity` to resolve compilation errors when style theme entities were imported from a thingworx server. 146 | 147 | Added the definition for the standard `ThingNames` data shape. 148 | 149 | # 29 Jan 2022 150 | 151 | Replaced all instances of the `JSON` type with the `TWJSON` type. 152 | 153 | Improved the formatting of the upload response message. Instead of printing out the JSON response that Thingworx provides, the build script will now try to extract and present the relevant information. 154 | 155 | # 29 Dec 2021 156 | 157 | Added the ability to generate debug builds by using the `--debug` argument. 158 | 159 | # 13 Oct 2021 160 | 161 | Added support for the `@unit` decorator which can be used to specify a unit for numeric properties. 162 | 163 | # 28 Jun 2021 164 | 165 | Added support for the `@allow`, `@allowInstance`, `@deny` and `@denyInstance` decorators which can be used to specify permissions at the entity or property, service or event levels. 166 | 167 | Added support for the `@visible` and `@visibleInstance` decorators which can be used to specify visibility permissions for entities. 168 | 169 | Added support for creating users and groups via a new `UserList` class. 170 | 171 | Added support for creating organizations via the new `OrganizationBase` class. 172 | 173 | Added support for using environment variables in service code or for default values. Like const enums, these are inlined at compile time. Note that these are based on the environment variables of the system used to build the application and not the target thingworx server. 174 | 175 | Added two new `deploy` and `removeAndDeploy` tasks. Added a new `@deploy` decorator that can be added to thing services. When using either of those two tasks, after a successful installation, the services marked with that decorator will be invoked. 176 | 177 | Updates the transformer version which resolves a critical issue resulting from the upgrade to Typescript 4 that caused replacement to fail for references to `this` or constant values in service code. 178 | 179 | # 18 Jun 2021 180 | 181 | Cleaned up `.DS_Store` files from the repo and placeholder values from the `metadata.xml` file. 182 | 183 | Add support for specifying connection details in an `.env` file that can be excluded from git. If present, this file will have priority over the connection details specified in `package.json`. 184 | 185 | Add support for specifying the types of a service's arguments using an interface instead of a type literal. 186 | 187 | # 15 Apr 2021 188 | 189 | Improved documentation and example. 190 | 191 | Declarations are now also generated prior to building. 192 | 193 | # 8 Apr 2021 194 | 195 | Fixed an import error when importing entities with async services. 196 | 197 | Fixed an import error when importing entities that required generic arguments, such as those extending `Stream` or `DataTable`. 198 | 199 | # 8 Mar 2021 200 | 201 | Added support for using an app key instead of username and password for deployment. 202 | 203 | # 6 Nov 2020 204 | 205 | Added the `@ordinal(_)` decorator that can be used on data shape fields. 206 | 207 | ## Updates to Thing Transformer 208 | 209 | Added support for the `@ordinal` decorator and the `autoGenerateDataShapeOrdinals` configuration flag. 210 | 211 | # 8 Apr 2020 212 | 213 | ## Updates to Thing Transformer 214 | 215 | Fixed import error when using infotables without a specified data shape. 216 | 217 | # 6 Apr 2020 218 | 219 | The build phase will now also emit declarations under `build/@types/index.d.ts`. 220 | 221 | Added support for specifying extensions as dependencies. 222 | 223 | # 3 Apr 2020 224 | 225 | Added support for downloading entity declarations from Thingworx. 226 | 227 | # 2 Apr 2020 228 | 229 | Added example for using ES6 features. 230 | 231 | ## Updates to Thing Transformer 232 | 233 | The transformer will now inline helpers when required by services, allowing syntax that requires them in ES5 to be used. 234 | 235 | # 1 Apr 2020 236 | 237 | Improved generics for `Stream` and `DataTable`. 238 | 239 | ## Updates to Thing Transformer 240 | 241 | Added support for specifying Thingworx descriptions via JSDoc comments. 242 | 243 | Added support for using and inlining const enum members. 244 | 245 | # 24 Mar 2020 246 | 247 | When declarations are generated, the generated directory is no longer deleted and recreated, instead the declarations file is overwritten in place. This resolves improper error messages being thrown by the typescript compiler during the declaration generation phase. 248 | 249 | The return types for `CreateValues` and `CreateValuesWithData` on data shapes now have the correct generics applied. 250 | 251 | ## Updates to Thing Transformer 252 | 253 | Resolves a crash when returning infotables or thingnames from services. 254 | 255 | # 23 Mar 2020 256 | 257 | The `upload` task no longer removes the current version of the extension before uploading the new one. Instead, it increments the version number then upgrades the existing extension in place. This is because removing and then reinstalling the extension would cause property and configuration table values to be lost during the upgrade. 258 | 259 | A new `removeAndUpload` task can be used to obtain the previous behaviour. 260 | 261 | Added support for specifying configuration tables via the `@ConfigurationTables` decorator. A new example thing template makes use of this decorator. 262 | 263 | ## Updates to Thing Transformer 264 | 265 | Resolves an issue where the base thing template for a thing template was improperly specified as `thingTemplate` instead of `baseThingTemplate`. 266 | 267 | Resolves an issue where literals other than strings or numbers that were specified in property initializers would be ignored and cause a crash during compilation. 268 | 269 | Added support for configuration table definition decorators. 270 | 271 | # 1.0.0 272 | 273 | Inital Release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Bogdan Mihaicuc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **🖐 NOTE: This is still a work in progress.** 2 | 3 | # Intro 4 | 5 | A project template that allows the development of Thingworx models in a TypeScript IDE like Visual Studio Code. 6 | 7 | # Who is it for 8 | 9 | This template is primarily aimed at Thingworx developer who are already very familiar with the composer but are also used to working in a regular IDE. To be able to succesfully use this, you should at least have an understanding of how the modelling environment works in Thingworx and some basic knowledge of TypeScript. 10 | 11 | # Why use it 12 | 13 | There are many advantages to this, and here are some of them: 14 | 15 | * **Git friendly**: In this project, thingworx entities are really just typescript source files (with some restrictions) so they can be easily used with git clients. 16 | * **Collaboration**: Collaboration in thingworx can be difficult. If developers use the same instance they can overwrite each other's changes by mistake; if they use different instances it can be difficult to merge the work. Using a classic file-based project instead makes it easier for multiple developers to work on the same project against the same git repository. 17 | * **Version management**: The entities are deployed as an extension instead of a regular entity export. Because of this, the entities themselves "gain" the version of the extension they are part of. Additionally, upgrading the extension will cause Thingworx to automatically remove old entities, whereas with a regular export, leftover entities must be found and removed manually. 18 | * **Developer friendly**: Creating entities in thingworx isn't very comfortable for developers. Common features found in IDEs, such as finding references, project-wide searching for code and even having multiple services open at the same time are not really possible. Since this project is just a typescript project, you can make use of all of your IDE's features. 19 | * **Typescript features**: You can also make use of typescript features that don't exist in thingworx, such as creating and using interfaces, marking methods and properties as private and using newer javascript features that can be transpiled. Note that some of these are erased on the Thingworx side, but they can nevertheless be useful while developing. 20 | * **Separate development and runtime**: In thingworx, any change happens immediately and can affect the system. When using this project template, you have to first build & publish your updated code for it to take effect. 21 | * **Debug support**: By using the `ThingworxVSCodeDebugger` visual studio code extension and the `BMDebugServer` thingworx extension, developers can also debug their thingworx typescript projects directly from visual studio code. The debugger supports most common features including setting breakpoints, exception breakpoints, stepping, evaluating expressions and changing variable values. For more information about setting up debugging, see [Usage Guide](https://github.com/BogdanMihaiciuc/ThingworxVSCodeDebugger/wiki/Usage-Guide). 22 | * **Profiling support**: By using the `BMProfiler` thingworx extension, developers can also profile their thingworx typescript projects to quickly identify performance bottlenecks. The profiler is a simple tracing profiler that shows the amount of time spent executing each service or function within a service. The profiler generates reports that can be viewed in the chrome performance inspector. For more information about setting up profiling, see [BMProfiler](https://github.com/BogdanMihaiciuc/BMProfiler). 23 | * **Additional features**: The transformer also supports some additional features not directly available in Thingworx that make development easier such as providing useful constants like `METHOD_NAME`, giving developers the ability to declare and use global functions, writing inline SQL queries on Database things and extending data shapes. 24 | 25 | # Development 26 | 27 | ## Pre-Requisites 28 | 29 | The following software is required: 30 | 31 | * [NodeJS](https://nodejs.org/en/): needs to be installed and added to the `PATH`. You should use the LTS version (v14+). 32 | 33 | The following software is recommended: 34 | 35 | * [Visual Studio Code](https://code.visualstudio.com/): An integrated developer enviroment with great javascript and typescript support. You can also use any IDE of your liking, it just that most of the testing was done using VSCode. 36 | 37 | ## Development Environment 38 | 39 | In order to develop with this project you need to do the following: 40 | 1. Clone this repository 41 | 2. Copy `.env.sample` to `.env` and configure the `THINGWORX_SERVER` and either `THINGWORX_USER` and `THINGWORX_PASSWORD` or `THINGWORX_APP_KEY` and other fields as needed. 42 | 3. Open `package.json` and `twconfig.json` and change the fields as needed. 43 | 4. Run `npm install`. This will install the development dependencies for the project. 44 | 5. Run `npm run watch:declarations`. This will start a background process that is needed for generating certain files. 45 | 6. Start working on the project. 46 | 47 | ## Entity Dependencies 48 | 49 | By default, the project contains the declarations for all the out of the box entities available in a fresh installation of Thingworx, however there may be the case that you need to use some entities that had been previously created on the Thingworx instance using the composer. To handle this, it is possible to define entity dependencies in `twconfig.json`. There are two types of dependencies that can be specified there: 50 | - `projectDependencies`: An array where you specify the Thingworx project names on which your local projects depends. This will download the declaration for all of the entities that are included in the project. 51 | - `entityDependencies`: An array where you specify entity types and names (e.g. `"Things/MyThing"`). This can be used to download specific entities, such as extension templates. 52 | 53 | Whenever you change `twconfig.json`, run `npx twc install` to pull the declarations from the Thingworx server specified in `package.json`. 54 | 55 | Whenever entities are downloaded in this way, all other dependencies are downloaded as well, so if your `MyThing` thing depends on the `MyDataShape` data shape, you don't need to specify both in `twconfig.json`. 56 | 57 | ## File Structure 58 | ``` 59 | ThingworxVSCodeProject 60 | │ README.md // This file 61 | │ package.json // Standard npm package details; the package name is also used as the extension name 62 | │ tsconfig.json // Standard typescript configuration file 63 | │ twconfig.json // Thingworx-specific configuration 64 | │ metadata.xml // Thingworx metadata file for this extension; This is automatically updated based on your package.json and build settings 65 | │ LICENSE // License file 66 | │ .env // Contains environment variables and thingworx connection details 67 | └───src // Main folder where your development will take place 68 | │ └───file1.ts // Thingworx entity file 69 | │ │ ... 70 | │ └───[Project1] // In multi-project mode, each subfolder represents a thingworx project 71 | │ │ └───src // Project's source folder 72 | │ │ │ └───file2.ts // Thingworx entity file 73 | │ │ │ ... 74 | │ │ └───tsconfig.json // Project's typescript configuration file; Project dependencies are extracted from this 75 | └───static // Supporting files required for compilation; Don't change these 76 | └───tw_imports // Entity dependencies downloaded from a Thingworx server 77 | └───build // Temporary folder used during compilation 78 | └───zip // Location of the built extension 79 | ``` 80 | 81 | ## Source Files 82 | 83 | Any TypeScript file you add to the `src` folder will be compiled into a Thingworx entity. There are some restrictions on what is allowed to be included in these files, namely: 84 | 85 | - There may only be a single class definition per file - each file must correspond to a single entity 86 | - The root of the file may only contain: 87 | - One class definition 88 | - Interface definitions 89 | - Enums, but only if they are declared `const` since these are erased at runtime 90 | - Comments 91 | - Property, method argument and return types can use either Thingworx base types or TypeScript standard types such as `string`, `number`. 92 | - Imports and modules are ignored; these will lead to runtime errors if used 93 | 94 | 95 | ## Current Limitations 96 | 97 | - Only Things, ThingTemplates, ThingShapes, DataShapes, Users, Groups, Organizations, Mashups, StyleDefinitions, StateDefinitions and Projects are supported currently. For other types of entities you will still need to use the composer. 98 | - Flow services and other service types except javascript and SQL are not supported. 99 | - Tags cannot be specified currently. 100 | - XML handling via E4X is not and will not be supported. Using E4X syntax will lead to a compilation error. You should use the various XML methods to work around this limitation. 101 | - Session variables and user extensions are not currently supported as data sources in mashups. You should instead obtain this information via services if needed. 102 | 103 | ## Build 104 | 105 | To build the extension, run `npm run build` in the root of the project. This will generate an extension .zip file in the zip folder in the root of the project. 106 | 107 | To build the extension and upload it to Thingworx, run `npm run upload` in the root of the project. The details of the Thingworx server to which the script will upload the extension are declared in the project's environment or `package.json` file. These are: 108 | * `thingworxServer` - The server to which the extension will be uploaded. 109 | * `thingworxAppKey` or `thingworxUser` and `thingworxPassword` - The credentials used for uploading. This should be a user that has permission to install extensions. 110 | 111 | To create a debug build that can be used for debugging, add the `--debug` argument to any task. For example, to create and upload a debug build, run `npx twc upload --debug`. For more information about debugging and debug builds, see [BMDebugServer](https://github.com/BogdanMihaiciuc/BMDebugServer) and [ThingworxVSCodeDebugger](https://github.com/BogdanMihaiciuc/ThingworxVSCodeDebugger). 112 | 113 | ## Deployment 114 | 115 | Deployment to Thingworx is part of the build process as explained above. Alternatively, you can manually install the extension that is generated in the zip folder in the root of the project. 116 | 117 | # Recent Changes 118 | 119 | For a complete changelog see [CHANGELOG.md](CHANGELOG.md). 120 | 121 | ## 31 Mar 2024 122 | 123 | - Support for mashups, Core UI mashups and CSS 124 | - Support for style definitions 125 | - Support for state definitions 126 | 127 | ## 28 Mar 2023 128 | - Support conditionally compiled branches 129 | - Support for calling superclass implementations 130 | - Support for inline SQL on non-database entities 131 | 132 | ## 11 Dec 2022 133 | - Support for trace builds 134 | - Support for XML entities 135 | 136 | ## 2 May 2022 137 | 138 | - Support for inline SQL 139 | - Support for generating `fieldDefinitions` exports 140 | - Support for the `override` keyword 141 | 142 | # Credit/Acknowledgment 143 | 144 | [Petrisor Lacatus](https://github.com/stefan-lacatus) - had the original idea for this, inferred property types and bug fixes, support for method helpers, API generation, data shape inheritance, `declare` on class members. 145 | 146 | [Bogdan Mihaiciuc](https://github.com/BogdanMihaiciuc) - main developer. 147 | 148 | [thomas-thomas-tfs](https://github.com/thomas-thomas-tfs) - deployment using app keys. 149 | 150 | [carlo-quinonez](https://github.com/carlo-quinonez) - bug fixes and documentation improvements. 151 | 152 | [dwil618](https://github.com/dwil618) - support for min/max aspects and date initializers. 153 | 154 | [CozminM](https://github.com/CozminM), [elena-bi](https://github.com/elena-bi) - bug fixes 155 | 156 | [kklorenzotesta](https://github.com/kklorenzotesta) - thingworx API typing updates 157 | 158 | 159 | # Disclaimer 160 | By downloading this software, the user acknowledges that it is unsupported, not reviewed for security purposes, and that the user assumes all risk for running it. 161 | 162 | Users accept all risk whatsoever regarding the security of the code they download. 163 | 164 | This software is not an official PTC product and is not officially supported by PTC. 165 | 166 | PTC is not responsible for any maintenance for this software. 167 | 168 | PTC will not accept technical support cases logged related to this Software. 169 | 170 | This source code is offered freely and AS IS without any warranty. 171 | 172 | The author of this code cannot be held accountable for the well-functioning of it. 173 | 174 | The author shared the code that worked at a specific moment in time using specific versions of PTC products at that time, without the intention to make the code compliant with past, current or future versions of those PTC products. 175 | 176 | The author has not committed to maintain this code and he may not be bound to maintain or fix it. 177 | 178 | 179 | # License 180 | I accept the MIT License (https://opensource.org/licenses/MIT) and agree that any software downloaded/utilized will be in compliance with that Agreement. However, despite anything to the contrary in the License Agreement, I agree as follows: 181 | 182 | I acknowledge that I am not entitled to support assistance with respect to the software, and PTC will have no obligation to maintain the software or provide bug fixes or security patches or new releases. 183 | 184 | The software is provided “As Is” and with no warranty, indemnitees or guarantees whatsoever, and PTC will have no liability whatsoever with respect to the software, including with respect to any intellectual property infringement claims or security incidents or data loss. 185 | -------------------------------------------------------------------------------- /metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bm-thingworx-vscode", 3 | "version": "3.1.6", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "bm-thingworx-vscode", 9 | "version": "3.1.6", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "bm-thing-cli": "^2.1.6" 13 | } 14 | }, 15 | "../ThingCLI": { 16 | "name": "bm-thing-cli", 17 | "version": "1.0.1", 18 | "extraneous": true, 19 | "license": "MIT", 20 | "dependencies": { 21 | "adm-zip": "0.5.9", 22 | "bm-thing-transformer": "0.18.0-beta.1", 23 | "dotenv": "^16.0.0", 24 | "enquirer": "^2.3.6", 25 | "typescript": "4.5.5", 26 | "xml2js": "^0.4.23" 27 | }, 28 | "bin": { 29 | "twc": "dist/index.js" 30 | }, 31 | "devDependencies": { 32 | "@types/adm-zip": "0.4.34", 33 | "@types/node": "^17.0.17" 34 | } 35 | }, 36 | "node_modules/adm-zip": { 37 | "version": "0.5.10", 38 | "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", 39 | "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", 40 | "dev": true, 41 | "engines": { 42 | "node": ">=6.0" 43 | } 44 | }, 45 | "node_modules/ansi-colors": { 46 | "version": "4.1.1", 47 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 48 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 49 | "dev": true, 50 | "engines": { 51 | "node": ">=6" 52 | } 53 | }, 54 | "node_modules/bm-thing-cli": { 55 | "version": "2.1.6", 56 | "resolved": "https://registry.npmjs.org/bm-thing-cli/-/bm-thing-cli-2.1.6.tgz", 57 | "integrity": "sha512-jrValSw4yqr9N5n+Me4knk18w1In8csMxtG8oOBIrTJW7tXiScSgTziBWpvlEQSvMToDIphoGnRwNj4YxFxdDg==", 58 | "dev": true, 59 | "dependencies": { 60 | "adm-zip": "0.5.10", 61 | "bm-thing-transformer": "2.1.6", 62 | "dotenv": "^16.0.0", 63 | "enquirer": "^2.3.6", 64 | "typescript": "5.4.5", 65 | "xml2js": "^0.6.2" 66 | }, 67 | "bin": { 68 | "twc": "dist/index.js" 69 | }, 70 | "engines": { 71 | "node": ">=18.0.0" 72 | } 73 | }, 74 | "node_modules/bm-thing-transformer": { 75 | "version": "2.1.6", 76 | "resolved": "https://registry.npmjs.org/bm-thing-transformer/-/bm-thing-transformer-2.1.6.tgz", 77 | "integrity": "sha512-Dqb4rc0myzxigBMotdDYroHcZBaD7g9EZH5C6XFt2phz5qgBx4GwshRyolN/q93hyr/FqtmZCMmg2/bdOlOrZQ==", 78 | "dev": true, 79 | "dependencies": { 80 | "typescript": "5.4.5", 81 | "typescriptwebpacksupport": "^2.4.0", 82 | "xml2js": "^0.6.2" 83 | } 84 | }, 85 | "node_modules/dotenv": { 86 | "version": "16.0.0", 87 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", 88 | "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==", 89 | "dev": true, 90 | "engines": { 91 | "node": ">=12" 92 | } 93 | }, 94 | "node_modules/enquirer": { 95 | "version": "2.3.6", 96 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 97 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 98 | "dev": true, 99 | "dependencies": { 100 | "ansi-colors": "^4.1.1" 101 | }, 102 | "engines": { 103 | "node": ">=8.6" 104 | } 105 | }, 106 | "node_modules/sax": { 107 | "version": "1.4.1", 108 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", 109 | "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", 110 | "dev": true 111 | }, 112 | "node_modules/typescript": { 113 | "version": "5.4.5", 114 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", 115 | "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", 116 | "dev": true, 117 | "bin": { 118 | "tsc": "bin/tsc", 119 | "tsserver": "bin/tsserver" 120 | }, 121 | "engines": { 122 | "node": ">=14.17" 123 | } 124 | }, 125 | "node_modules/typescriptwebpacksupport": { 126 | "version": "2.4.0", 127 | "resolved": "https://registry.npmjs.org/typescriptwebpacksupport/-/typescriptwebpacksupport-2.4.0.tgz", 128 | "integrity": "sha512-2SlBwlBME6F7nm6NRodC/2LhXpNMQNEzyCuif3i8zs7bKmyXoVRQakXtpbUwHjgGZU4nmu7m83c0QisGbTmYoA==", 129 | "dev": true 130 | }, 131 | "node_modules/xml2js": { 132 | "version": "0.6.2", 133 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", 134 | "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", 135 | "dev": true, 136 | "dependencies": { 137 | "sax": ">=0.6.0", 138 | "xmlbuilder": "~11.0.0" 139 | }, 140 | "engines": { 141 | "node": ">=4.0.0" 142 | } 143 | }, 144 | "node_modules/xmlbuilder": { 145 | "version": "11.0.1", 146 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 147 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", 148 | "dev": true, 149 | "engines": { 150 | "node": ">=4.0" 151 | } 152 | } 153 | }, 154 | "dependencies": { 155 | "adm-zip": { 156 | "version": "0.5.10", 157 | "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", 158 | "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", 159 | "dev": true 160 | }, 161 | "ansi-colors": { 162 | "version": "4.1.1", 163 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 164 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 165 | "dev": true 166 | }, 167 | "bm-thing-cli": { 168 | "version": "2.1.6", 169 | "resolved": "https://registry.npmjs.org/bm-thing-cli/-/bm-thing-cli-2.1.6.tgz", 170 | "integrity": "sha512-jrValSw4yqr9N5n+Me4knk18w1In8csMxtG8oOBIrTJW7tXiScSgTziBWpvlEQSvMToDIphoGnRwNj4YxFxdDg==", 171 | "dev": true, 172 | "requires": { 173 | "adm-zip": "0.5.10", 174 | "bm-thing-transformer": "2.1.6", 175 | "dotenv": "^16.0.0", 176 | "enquirer": "^2.3.6", 177 | "typescript": "5.4.5", 178 | "xml2js": "^0.6.2" 179 | } 180 | }, 181 | "bm-thing-transformer": { 182 | "version": "2.1.6", 183 | "resolved": "https://registry.npmjs.org/bm-thing-transformer/-/bm-thing-transformer-2.1.6.tgz", 184 | "integrity": "sha512-Dqb4rc0myzxigBMotdDYroHcZBaD7g9EZH5C6XFt2phz5qgBx4GwshRyolN/q93hyr/FqtmZCMmg2/bdOlOrZQ==", 185 | "dev": true, 186 | "requires": { 187 | "typescript": "5.4.5", 188 | "typescriptwebpacksupport": "^2.4.0", 189 | "xml2js": "^0.6.2" 190 | } 191 | }, 192 | "dotenv": { 193 | "version": "16.0.0", 194 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", 195 | "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==", 196 | "dev": true 197 | }, 198 | "enquirer": { 199 | "version": "2.3.6", 200 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 201 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 202 | "dev": true, 203 | "requires": { 204 | "ansi-colors": "^4.1.1" 205 | } 206 | }, 207 | "sax": { 208 | "version": "1.4.1", 209 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", 210 | "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", 211 | "dev": true 212 | }, 213 | "typescript": { 214 | "version": "5.4.5", 215 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", 216 | "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", 217 | "dev": true 218 | }, 219 | "typescriptwebpacksupport": { 220 | "version": "2.4.0", 221 | "resolved": "https://registry.npmjs.org/typescriptwebpacksupport/-/typescriptwebpacksupport-2.4.0.tgz", 222 | "integrity": "sha512-2SlBwlBME6F7nm6NRodC/2LhXpNMQNEzyCuif3i8zs7bKmyXoVRQakXtpbUwHjgGZU4nmu7m83c0QisGbTmYoA==", 223 | "dev": true 224 | }, 225 | "xml2js": { 226 | "version": "0.6.2", 227 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", 228 | "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", 229 | "dev": true, 230 | "requires": { 231 | "sax": ">=0.6.0", 232 | "xmlbuilder": "~11.0.0" 233 | } 234 | }, 235 | "xmlbuilder": { 236 | "version": "11.0.1", 237 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 238 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", 239 | "dev": true 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bm-thingworx-vscode", 3 | "version": "3.1.6", 4 | "description": "Develop in ThingWorx with a proper IDE.", 5 | "author": "Thingworx RoIcenter", 6 | "thingworxProjectName": "MyProject", 7 | "thingworxServer": "http://localhost:8014", 8 | "thingworxUser": "Administrator", 9 | "thingworxPassword": "Administrator12345", 10 | "thingworxAppKey": "abc-acfdf", 11 | "minimumThingWorxVersion": "6.0.0", 12 | "homepage": "https://github.com/BogdanMihaiciuc/", 13 | "autoUpdate": { 14 | "gitHubURL": "https://api.github.com/repos/ptc-iot-sharing/ThingworxVSCodeProject/releases/latest" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/ptc-iot-sharing/ThingworxVSCodeProject.git" 19 | }, 20 | "scripts": { 21 | "test": "echo \"Error: no test specified\" && exit 1", 22 | "build": "twc build", 23 | "buildDebug": "twc build --debug", 24 | "upload": "twc upload", 25 | "uploadDebug": "twc upload --debug", 26 | "watch:declarations": "twc watch" 27 | }, 28 | "license": "MIT", 29 | "files": [ 30 | "lib", 31 | "build", 32 | "LICENSE" 33 | ], 34 | "devDependencies": { 35 | "bm-thing-cli": "^2.1.6" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ConfigurationTableShapes/LinkedPortInfo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * It is possible to extend **locally defined** data shapes by using the base 3 | * `DataShapeBase` class as a function, which takes the data shapes to be extended 4 | * as arguments. 5 | */ 6 | class LinkedPortInfo extends DataShapeBase(LinkedList, PortInfo) { 7 | 8 | /** 9 | * Additional fields can be added to the new data shape. 10 | */ 11 | description!: string; 12 | 13 | /** 14 | * Fields declared on the parent types may be overriden on the new 15 | * data shape, but only if they have compatible types. 16 | * The overriden field will be used on the final data shape. 17 | * 18 | * This can be used to clear out modifiers declared on the parent, such as 19 | * `@primaryKey` or `@ordinal`. 20 | */ 21 | override name!: string; 22 | } -------------------------------------------------------------------------------- /src/ConfigurationTableShapes/PortInfo.ts: -------------------------------------------------------------------------------- 1 | class PortInfo extends DataShapeBase { 2 | 3 | server: string = 'localhost'; 4 | 5 | port: number = 80; 6 | 7 | useSSL: boolean = false; 8 | 9 | } -------------------------------------------------------------------------------- /src/MyCoreUIMashup.tsx.example: -------------------------------------------------------------------------------- 1 | /** 2 | * This file contains an example of a mashup that also uses Core UI to enable custom code 3 | * beyond just the mashup JSX structure and the global declarations. 4 | * 5 | * Since these mashups can use the DOM library it might be a good ideea to create 6 | * a separate UI sub-project, otherwise the DOM types will also be available in all other 7 | * backend files where they won't work. 8 | * 9 | * Additionally, it is also required to set the compilation target to ES6 or higher, since the Typescript Class 10 | * widget requires support for extending classes using typical ES6 syntax. 11 | * 12 | * This example imports the dom library via a triple slash directive. 13 | * 14 | * NOTE: To get this example file to work, it is needed to change its extension to .tsx AND set the compilation target 15 | * to ES6 or newer. 16 | */ 17 | /// 18 | // Mashup files need an additional import for UI definitions. Core UI mashups import from a 19 | // different file than plain mashups. 20 | import "bm-thing-transformer/ui/core-ui" 21 | // A second import may be used for any non-standard widgets to be used. 22 | // These are imported from a Thingworx server via the "install-widgets" command. 23 | import "../ui-static/widgets" 24 | 25 | /** 26 | * All services that are referenced by the mashup must be declared in advance as global constants. 27 | * Their value must be the result of invoking `defineService`, passing in the service to use as the argument. 28 | */ 29 | const QueryImplementingThings = defineService(ThingTemplates.GenericThing.QueryImplementingThings); 30 | 31 | /** 32 | * Dynamic services can be defined with a different `defineDynamicService` function. Specifically for thing templates 33 | * and thing shapes, their dynamic entity types can be obtained through a `dyanmicEntity` service 34 | */ 35 | const ExampleProperty = defineDynamicService(dynamicEntity(ThingTemplates.ExampleThingTemplate).GetExampleProperty); 36 | 37 | /** 38 | * All widgets that act as binding sources for service inputs or other widget properties must also 39 | * be declared in advance as global constants. Their value must be the result of invoking `defineWidget`, passing 40 | * in the name of the widget to use as the argument. 41 | */ 42 | const MaxItemsInput = defineWidget(Numericentry); 43 | 44 | const WindowView = defineWidget<"MyMashup">(BMWindowController); 45 | 46 | 47 | /** 48 | * Mashup parameters, if needed, are similarly defined as global constants, this time using the `defineMashup` 49 | * function. This takes a class definition as an argument, similar to configuration tables, that describes 50 | * the parameters defined on this mashup. All parameters are always both binding sources and targets. 51 | */ 52 | const MashupParameters = defineMashup(class { 53 | thingName!: STRING; 54 | selectedThing!: INFOTABLE; 55 | }); 56 | 57 | /** 58 | * Collection view interfaces require two optional type arguments: 59 | * - The first is the data shape of the data infotable. This needs to be set to use 60 | * the event properties as binding sources. This will also restrict the data property 61 | * to an infotable using the specified data shape. 62 | * 63 | * - The second is an interface that describes the global parameters. This needs to be set 64 | * to use the global parameters as binding sources. 65 | */ 66 | const ItemsCollection = defineWidget(BMCollectionView); 67 | 68 | /** 69 | * The mashup content itself is defined in a class that extends from `MashupController` and 70 | * contains at least a `renderMashup` method that returns the mashup structure. 71 | */ 72 | class MyCoreUIMashup extends MashupController { 73 | 74 | /** 75 | * Unlike with typical typescript class properties, it's no longer necessary to annotate properties 76 | * with the `@property` decorator, unless a specific aspect such as `canBind` is required. Even when 77 | * the decorator is specified, it is not required to mark the property as a binding source or target 78 | * since this is inferred automatically by the compiler. 79 | */ 80 | titleLabel!: STRING; 81 | 82 | /** 83 | * A reference to the grid widget. 84 | */ 85 | gridWidget!: TWRuntimeWidget; 86 | 87 | /** 88 | * The `@property` decorator is required, however, to bind properties from other widgets to the controller 89 | * property. Otherwise, regular typescript class features such as setters can be used. 90 | */ 91 | @property(bind(MaxItemsInput.Value)) set maxItems(value: number) { 92 | this.titleLabel = `${value} Items`; 93 | } 94 | 95 | /** 96 | * Services similarly don't need to be annotated. 97 | */ 98 | alertValue(): void { 99 | alert(this.maxItems); 100 | 101 | // Specifying any kind of JSX element outside of the renderMashup function will cause a runtime 102 | // error when loading this mashup e.g.: 103 | // const widget = // Runtime error 104 | } 105 | 106 | deleteItem(): void { 107 | confirm('Are you sure you want to delete this item?'); 108 | } 109 | 110 | /** 111 | * Events do need to be annotated. Their bindings are similarly specified as an argument to the decorator. 112 | */ 113 | @twevent([QueryImplementingThings]) gridClicked!: TWEvent; 114 | 115 | /** 116 | * When working with infotable property types, use the JSONInfoTable type rather than INFOTABLE. 117 | */ 118 | @property(bind(QueryImplementingThings.AllData), didBind('dataDidBind')) data!: JSONInfoTable; 119 | 120 | dataDidBind(prevousValue: JSONInfoTable, info: TWUpdatePropertyInfo) { 121 | console.log(`Data property was updated!`); 122 | } 123 | 124 | renderMashup() { 125 | return 138 | {/* 139 | Services must always be specified as children of the Mashup element; this is where parameters are bound or specified. 140 | They must be associated with their output sources via the "ref" property. 141 | */} 142 | 150 | {/* All JSX expressions, such as this one, cause a compilation error if they contain more than comments. */} 151 | 152 | 153 | {/* 154 | Widgets are specified as a tree structure under the mashup. Unlike typical thingworx mashups you are not actually required 155 | to use a flex container as the root widget. For simpler mashups (e.g. to use in a collection view cell) it can be any other 156 | widget, like a chart. 157 | */} 158 | 159 | 166 | 167 | 168 | 169 | 177 | 178 | 179 | 180 | {/* 181 | A widget output source that was previously defined must be associated with a widget instance via the "ref" property, 182 | similar to how services are associated to their output sources. 183 | */} 184 | 191 | 199 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | ; 225 | } 226 | }; 227 | 228 | /** 229 | * For the controller to participate in bindings, it needs a reference variable, same as widgets and services. 230 | * This takes the mashup class as its argument, so it must be defined after the class itself. 231 | */ 232 | const ControllerInterface = defineController(MyCoreUIMashup); -------------------------------------------------------------------------------- /src/MyDataShape.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is an example of a data shape. Data shapes are classes that must extend from `DataShapeBase`. 3 | */ 4 | class LinkedList extends DataShapeBase { 5 | 6 | /** 7 | * Data shape fields are specified as properties. The `@primaryKey` decorator 8 | * can be used to mark fields as primary keys. 9 | */ 10 | @primaryKey name!: string; 11 | 12 | /** 13 | * The `@ordinal` decorator can be used to specify ordinal values for data shape fields. 14 | */ 15 | @ordinal(4) next!: INFOTABLE 16 | } -------------------------------------------------------------------------------- /src/MyDatabase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * SQL commands and queries can be added as services to things and templates 3 | * that extends from the `Database` template. 4 | * 5 | * These services are marked with either the `@SQLCommand` or `@SQLQuery` decorators and must have a specific syntax: 6 | * * For commands, these must only contain a single tagged template literal using the `SQLCommand` function. 7 | * * For queries, these must only contain a single return statement that returns the result of a tagged 8 | * template literal using the `SQLQuery` function. 9 | * 10 | * Prepared statement parameters are specified via template literal expressions, while literal SQL code is specified 11 | * via template literals enclosed using `<<` and `>>` (e.g. `<<${mySQLCode}>>`) 12 | */ 13 | @config({ 14 | ConnectionInfo: { 15 | connectionValidationString: 'SELECT NOW()', 16 | jDBCConnectionURL: process.env.DATABASE_JDBC_STRING, 17 | jDBCDriverClass: 'com.yourdatabase.yourDriver', 18 | maxConnections: 5, 19 | password: process.env.DATABASE_PASSWORD, 20 | userName: process.env.DATABASE_USERNAME 21 | } 22 | }) 23 | @ThingDefinition class MyDatabase extends Database { 24 | 25 | /** 26 | * SQL commands are created using the `@SQLCommand` decorator. The command itself 27 | * is specified as a template literal string using the `SQLCommand` function and it 28 | * must be the only statement in this service. 29 | * @param name The name of the product to add. 30 | * @param price The price for the product. 31 | */ 32 | @SQLCommand addProduct({name, price}: {name: string, price: number}): void { 33 | // Prepared statement parameters are specified as template literal expressions. 34 | // These may only contain argument names. 35 | // Consider using a vscode extension such as es6-string-html to get syntax coloring for these strings. 36 | // Note that all javascript comments within a SQLCommand service will be removed at compile time. 37 | /*sql*/ 38 | SQLCommand`INSERT INTO products VALUES (${name}, ${price})`; 39 | } 40 | 41 | /** 42 | * The timeout of a SQL command can be optionally specified as an argument to the `@SQLCommand` decorator. 43 | * If omitted, it will use the default value of 60 seconds. 44 | */ 45 | @SQLCommand(600) clearProducts(): void { 46 | /*sql*/ 47 | SQLCommand`DELETE FROM products`; 48 | } 49 | 50 | /** 51 | * SQL queries are created using the `@SQLQuery` decorator. The query itself 52 | * is specified as a template literal string using the `SQLCommand` function and it 53 | * must be the only statement in this service. 54 | * @param condition The condition with which to filter products. 55 | */ 56 | @SQLQuery getProducts({condition}: {condition: string}): INFOTABLE { 57 | // SQL literal replacements can also be specified using template literal expressions, by 58 | // enclosing them in the standard thingworx SQl literal `<<`, `>>` delimiters. 59 | /*sql*/ 60 | return SQLQuery` 61 | SELECT 62 | product_name, 63 | price 64 | FROM 65 | products 66 | WHERE 67 | <<${condition}>> 68 | `; 69 | } 70 | 71 | /** 72 | * Similar to commands, the timeout and max rows can be configured on queries as arguments to the `@SQLQuery` decorator. 73 | * If omitted, they will use the default values of 60 seconds for the timeout and 500 max rows. 74 | */ 75 | @SQLQuery(600, 5000) getAllProducts(): INFOTABLE { 76 | /*sql*/ 77 | return SQLQuery` 78 | SELECT 79 | product_name, 80 | price 81 | FROM 82 | products 83 | `; 84 | } 85 | 86 | /** 87 | * Gets the increased price of the given product after applying the given percent increase. 88 | * @param product The product whose increased price should be determined. 89 | * @param percent The percent by which to increase the price. 90 | * @returns The increased price. 91 | */ 92 | getIncreasedProductPrice({product, percent}: {product: INFOTABLE, percent: number}): number { 93 | return product.price + product.price * percent; 94 | } 95 | 96 | /** 97 | * When inline SQL is enabled, it is possible to write SQL commands and queries directly within javascript services. 98 | * The transformer will extract the sql statements and convert them into service. 99 | * @param name The name of the product whose info should be retrieved. A value of "*" will update the prices of all products. 100 | * @param percent A percentage by which to increase the product's price. 101 | * @returns An infotable containing the products that were updated. 102 | */ 103 | increaseProductPrice({name, percent}: {name: string, percent: number}): INFOTABLE { 104 | // If the name is "*" update all prices 105 | if (name == '*') { 106 | /*sql*/ 107 | SQLCommand`UPDATE products SET price = price * ${1 + percent}`; 108 | 109 | /*sql*/ 110 | return SQLQuery`SELECT * from products`; 111 | } 112 | 113 | // Find the product to update 114 | let productToUpdate: INFOTABLE | undefined; 115 | 116 | // Inefficiently loop through a query of all products to find the one whose 117 | // price should be updated 118 | // Inline SQL statements can be used anywhere within a database service, for example 119 | // within a for-loop 120 | // For SQLQuery it is required to specify the type of the return value as a generic argument 121 | for (const product of SQLQuery/*sql*/`SELECT * FROM products`) { 122 | if (product.product_name == name) { 123 | productToUpdate = product.toInfoTable(); 124 | break; 125 | } 126 | } 127 | 128 | // If the product wasn't found, throw an error 129 | if (!productToUpdate) { 130 | throw new Error(`${LOG_PREFIX} The product "${name}" does not exist.`); 131 | } 132 | 133 | // Update the database; when using inline commands it is possible to use javascript expressions in substitutions 134 | // but note that these must have a valid inferred type (e.g. cannot be any, unknown, or a non-thingworx type) 135 | /*sql*/ 136 | SQLCommand`UPDATE products SET price = ${this.getIncreasedProductPrice({product: productToUpdate, percent})} WHERE id = ${productToUpdate.id}`; 137 | 138 | // Return the updated product 139 | // For queries and commands, it is possible to specify the timeout and max rows properties as additional generic arguments 140 | /*sql*/ 141 | return SQLQuery`SELECT * FROM products WHERE id = ${productToUpdate.id}`; 142 | } 143 | 144 | 145 | } -------------------------------------------------------------------------------- /src/MyInstance.ts: -------------------------------------------------------------------------------- 1 | @database('MyDatabase') 2 | @ThingDefinition class MyInstance extends ExampleThingTemplate { 3 | 4 | /** 5 | * When overriding a service on a template or shape that is part of the projects, 6 | * it is possible to invoke the base implementation in any subclass. 7 | */ 8 | override GetExampleProperty(): number { 9 | const baseExampleProperty = super.GetExampleProperty(); 10 | 11 | if (baseExampleProperty < 0) { 12 | return 0; 13 | } 14 | else if (baseExampleProperty > 100) { 15 | return 100; 16 | } 17 | 18 | return baseExampleProperty; 19 | } 20 | 21 | /** 22 | * The superclass implementation of any service may be invoked from any service, not just 23 | * the ones that override the base implementation. 24 | * 25 | * Additionally, in most cases it is not required to directly specify the return type of a service. 26 | * The compiler can infer the type. However, the type must be annotated if the inferred type 27 | * does not match to a thingworx base type. 28 | */ 29 | GetAllProperties() { 30 | const exampleProperty = super.GetExampleProperty(); 31 | 32 | const properties = DataShapes.NumberArrayElement.CreateValues(); 33 | properties.AddRow({ element: exampleProperty }); 34 | 35 | return properties; 36 | } 37 | 38 | /** 39 | * By specifying a target database via the `@database` decorator, any service can include inline 40 | * SQL commands and queries. These will be transformed into services on the target database thing. 41 | */ 42 | SaveProperties() { 43 | const exampleProperty = this.GetExampleProperty(); 44 | const name = this.name; 45 | const time = new Date(); 46 | 47 | SQLCommand/*SQL*/` 48 | INSERT INTO properties (name, value, time) 49 | VALUES (${name}, ${exampleProperty}, ${time}) 50 | `; 51 | } 52 | } -------------------------------------------------------------------------------- /src/MyMashup.tsx: -------------------------------------------------------------------------------- 1 | // Mashup files need an additional import for UI definitions. 2 | import "bm-thing-transformer/ui" 3 | // A second import may be used for any non-standard widgets to be used. 4 | // These are imported from a Thingworx server via the "install-widgets" command. 5 | import "../ui-static/widgets" 6 | 7 | /** 8 | * All services that are referenced by the mashup must be declared in advance as global constants. 9 | * Their value must be the result of invoking `defineService`, passing in the service to use as the argument. 10 | */ 11 | const QueryImplementingThings = defineService(ThingTemplates.GenericThing.QueryImplementingThings); 12 | 13 | /** 14 | * Dynamic services can be defined with a different `defineDynamicService` function. Specifically for thing templates 15 | * and thing shapes, their dynamic services can be obtained through a `dyanmicEntity` service 16 | */ 17 | const ExampleProperty = defineDynamicService(dynamicEntity(ThingTemplates.ExampleThingTemplate).GetExampleProperty); 18 | 19 | /** 20 | * All widgets that act as binding sources for service inputs or other widget properties must also 21 | * be declared in advance as global constants. Their value must be the result of invoking `defineWidget`, passing 22 | * in the name of the widget to use as the argument. 23 | */ 24 | const MaxItemsInput = defineWidget(Numericentry); 25 | 26 | /** 27 | * For navigations that need to output data, specify the mashup name as a generic argument to have access to its parameters. 28 | * Same for contained mashup and similar widgets. 29 | */ 30 | const Reload = defineWidget<'MyMashup'>(Navigation); 31 | 32 | /** 33 | * Mashup parameters, if needed, are similarly defined as global constants, this time using the `defineMashup` 34 | * function. This takes a class definition as an argument, similar to configuration tables, that describes 35 | * the parameters defined on this mashup. All parameters are always both binding sources and targets. 36 | */ 37 | const MashupParameters = defineMashup(class { 38 | thingName!: STRING; 39 | selectedThing!: INFOTABLE; 40 | }); 41 | 42 | /** 43 | * The mashup content itself is defined in a class that extends from `MashupBase` and 44 | * contains a single method `renderMashup` that returns the mashup structure. 45 | */ 46 | export class MyMashup extends MashupBase { 47 | 48 | renderMashup() { 49 | return 63 | {/* 64 | Services must always be specified as children of the Mashup element; this is where parameters are bound or specified. 65 | They must be associated with their output sources via the "ref" property. 66 | */} 67 | 75 | {/* All JSX expressions, such as this one, cause a compilation error if they contain more than comments. */} 76 | 77 | 78 | {/* 79 | Widgets are specified as a tree structure under the mashup. Unlike typical thingworx mashups you are not actually required 80 | to use a flex container as the root widget. For simpler mashups (e.g. to use in a collection view cell) it can be any other 81 | widget, like a chart. 82 | */} 83 | 84 | 92 | 93 | 94 | 95 | 96 | 97 | 104 | 105 | {/* 106 | A widget output source that was previously defined must be associated with a widget instance via the "ref" property, 107 | similar to how services are associated to their output sources. 108 | */} 109 | 116 | 126 | " command. The file is obtained by downloading 129 | // from a thingworx composer instance using the widget exporter extension 130 | Data={QueryImplementingThings.AllData} 131 | /> 132 | 133 | 134 | 135 | 136 | 137 | 138 | ; 139 | } 140 | }; -------------------------------------------------------------------------------- /src/MyMashupGridConfiguration.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /src/MyMashupStyles.css: -------------------------------------------------------------------------------- 1 | .app-header { 2 | border-bottom: 1px solid gray; 3 | background-color: rgba(0, 0, 0, .1); 4 | } 5 | 6 | .app-header > .widget-content { 7 | background-color: rgba(0, 0, 0, .1); 8 | } 9 | 10 | .app-details { 11 | border-left: 1px solid gray; 12 | } -------------------------------------------------------------------------------- /src/MyOrganization.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Organiations can be created via a class that extends from `OrganizationBase`. Each class represents a single organization. 3 | */ 4 | class MyOrganization extends OrganizationBase { 5 | 6 | /** 7 | * Organizations classes are only allowed to have a single 'units' property which is used to describe 8 | * the organizational units that are part of the organization. 9 | * 10 | * Each organization must have a single root unit. 11 | */ 12 | override units: OrganizationUnit = { 13 | /** 14 | * A description can be provided for the organizational unit by adding a JSDoc tag to its name property. 15 | */ 16 | name: 'MyUnit', 17 | 18 | // Members can be optionally added by specifying a "members" property which can contain users and/or groups 19 | members: [Users.JohnDoe], 20 | 21 | // Subunits can be optionally specified via the "units" property, that is an array in which subunits can be created 22 | units: [ 23 | { 24 | name: 'MyUnit2', 25 | members: [Groups.ExampleGroup] 26 | }, 27 | { 28 | name: 'MyUnit3' 29 | } 30 | ] 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/MyProduct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A datashape that is used as a SQL query service result. 3 | */ 4 | class MyProduct extends DataShapeBase { 5 | /** 6 | * The name of the product. 7 | */ 8 | product_name!: string; 9 | 10 | /** 11 | * The price of the product. 12 | */ 13 | price!: number; 14 | 15 | /** 16 | * The product's id. 17 | */ 18 | id!: string; 19 | } -------------------------------------------------------------------------------- /src/MyShape.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * We can define and use interfaces throughout the project. 3 | */ 4 | interface StatusResponse { 5 | 6 | pressure: number; 7 | 8 | humidity: number; 9 | 10 | } 11 | 12 | /** 13 | * Global functions can be declared and used throughout the project. 14 | * Whenever a global function is used in any service, it will be copied 15 | * to the service during compilation. 16 | * @param message The message to log. 17 | */ 18 | function log(message: string): void { 19 | // Method helpers may be used from within global functions; if any is used 20 | // its declaration will be added to all of the services using it 21 | logger.debug(`${LOG_PREFIX} ${message}`); 22 | 23 | // Global functions may also call other global functions, in this case 24 | // all of their dependencies will be inlined as well 25 | logToFile(message); 26 | } 27 | 28 | /** 29 | * This example class demonstrates how Thing Shapes are created. 30 | * 31 | * Thing shapes are classes that extend from the `ThingShapeBase` class; they follow 32 | * the same general rules as things. 33 | */ 34 | class ExampleThingShape extends ThingShapeBase { 35 | 36 | /** 37 | * For most types, the standard TypeScript types such as `string` and `number` can be used. 38 | */ 39 | @logged @persistent pressure: number = 10; 40 | 41 | /** 42 | * Thingworx type names are also supported and can be used interchangeably. Note that some Thingworx 43 | * types such as `INTEGER` map to primitive types like `number` on the compiler side but may have additional 44 | * semantics or behaviours at runtime. 45 | */ 46 | @remote('humidity') humidity!: NUMBER; 47 | 48 | GetPressure(): number { 49 | return this.pressure; 50 | } 51 | 52 | SetPressure({pressure}: {pressure: number}): void { 53 | // At runtime, this service will gain a copy of the log function 54 | // and any other global functions it calls. 55 | log(`Setting pressure to ${pressure}`); 56 | this.pressure = pressure; 57 | } 58 | 59 | @remoteService('SetHumidity') SetHumidity({humidity}: {humidity: number}): void {} 60 | 61 | /** 62 | * There are no interface types in Thingworx, but in a similar manner to string and number types, 63 | * we can constrain the JSON (renamed to TWJSON to avoid conflicts with the standard JSON global) type 64 | * to another interface via generics. 65 | * 66 | * Any JSDoc tags used will be converted into thingworx descriptions. 67 | * @param factor A factor by which to multiply the values. 68 | * @return An object containing the requested values. 69 | */ 70 | GetValuesWithFactor({factor = 1}: {factor?: number}): TWJSON { 71 | return { 72 | pressure: factor * this.pressure, 73 | humidity: factor * this.humidity 74 | } 75 | } 76 | 77 | /** 78 | * In addition to specifying the types of each parameter as a literal type, interfaces may also be used. 79 | * @param pressure The pressure. 80 | * @param humidity The humidity. 81 | * @returns A copy of the input parameters. 82 | */ 83 | GetStatusResponse({pressure, humidity}: StatusResponse): TWJSON { 84 | return {pressure, humidity}; 85 | } 86 | 87 | /** 88 | * With TypeScript, it is possible to user certain newer javascript features through transpilation. 89 | */ 90 | PrintValuesWithFactor({factor = 1}: {factor?: NUMBER}): NOTHING { 91 | // An example is destructuring objects into variables 92 | const {pressure, humidity} = this.GetValuesWithFactor({factor}); 93 | 94 | // Template literals are another feature that is very useful 95 | logger.info(`The pressure is ${pressure} and the humidity is ${humidity}`); 96 | 97 | // We can also the new for...of syntax for easier looping 98 | for (const row of ThingTemplates.GenericThing.GetImplementingThings()) { 99 | logger.info(`Thing is ${row.name}`); 100 | } 101 | 102 | // We can also easily convert infotables to arrays via the spread syntax 103 | const things: GenericThing[] = [...ThingTemplates.GenericThing.GetImplementingThings()].map(row => Things[row.name]).filter(thing => thing.IsEnabled()); 104 | things.forEach(thing => logger.info(`Thing ${thing.name}'s template is ${thing.GetThingTemplate()}`)); 105 | 106 | // And easily combine object literals 107 | const allValues = {...this.GetValuesWithFactor({factor}), altitude: 0}; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/MyStyleLibrary.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Styles and state definitions can be created via a class that extends from `StyleLibrary`. A single style library can contain 3 | * multiple styles and/or state definitions. 4 | */ 5 | @visible(Organizations.Development) 6 | @allow(Users.JohnDoe, Permission.EventInvoke) 7 | class MyStyleLibrary extends StyleLibrary { 8 | 9 | /** 10 | * A style is declared as a property on the style library. It should be assigned a default value containing 11 | * the values of the fields to use for the style definition, but all fields are optional. 12 | * 13 | * An optional `StyleDefinition` type annotation can be used to provide autocomplete suggestions. 14 | */ 15 | DefaultButtonStyle: StyleDefinition = { 16 | backgroundColor: 'rgba(0, 128, 255, 1)', 17 | foregroundColor: '#FFFFFF', 18 | textSize: '14px' 19 | } 20 | 21 | /** 22 | * A state definition is also declared as a property on the style library. It must be assigned a default value 23 | * that is an object describing the kind of state definition it is and what state definitions it contains. 24 | * 25 | * An optional `StyleDefinition` type annotation can be used to provide autocomplete suggestions. 26 | */ 27 | TextStates: StateDefinition = { 28 | stateType: 'numeric', 29 | stateDefinitions: [ 30 | { comparator: '<', defaultValue: 10, defaultStyleDefinition: 'StatusError', name: 'error' }, 31 | { comparator: '<=', defaultValue: 50, defaultStyleDefinition: 'StatusBusy', name: 'warning' }, 32 | { 33 | comparator: '<', 34 | defaultValue: 200, 35 | defaultStyleDefinition: { 36 | backgroundColor: 'transparent', 37 | foregroundColor: '#000000', 38 | textSize: '14px' 39 | }, 40 | name: 'ok' 41 | }, 42 | // All states require a "default" state that has no comparator or default value properties 43 | { defaultStyleDefinition: 'StatusUnknown', name: 'unknown' } 44 | ] 45 | } 46 | 47 | MenuItems: StateDefinition = { 48 | stateType: 'string', 49 | stateDefinitions: [ 50 | { name: 'Get Info', defaultValue: '', defaultStyleDefinition: {} }, 51 | { name: 'Copy', defaultValue: '', defaultStyleDefinition: {} }, 52 | { name: 'Duplicate', defaultValue: '', defaultStyleDefinition: {} }, 53 | { name: '--', defaultValue: '', defaultStyleDefinition: {} }, 54 | { name: 'Edit', defaultValue: '', defaultStyleDefinition: {} }, 55 | { name: 'Delete', defaultStyleDefinition: {} }, 56 | ] 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /src/MyTemplate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The `@ConfigurationTables` decorator can be applied to thing templates or thing to cause a configuration 3 | * table to be created for it. 4 | * 5 | * This decorator takes a single parameter that must be an anonymous class expression whose property names represent the configuration 6 | * table names and their types must be either `Table` or `MultiRowTable` with a data shape name as a type argument. 7 | * 8 | * Entity classes can be decorated with the `@allow` and `@deny` decorators to specify permissions. When at the class level, these 9 | * can be used to specify permissions for all of the properties, services and events on that entity. 10 | * 11 | * Visibility for organizations can be added via the `@visible` decorator. 12 | * 13 | * For templates and thing shapes, instance permissions can be specified via the `@allowInstance` and `@denyInstance` decorators. 14 | * These have the same 15 | */ 16 | @ConfigurationTables(class { 17 | ConnectionInfo: Table; 18 | Fields: MultiRowTable; 19 | }) 20 | // These decorators can be used to change the permissions for specific services and properties that are inherited 21 | @deny('GetImplementingThings', Permission.ServiceInvoke, Groups.Designers) 22 | // These decorators can be used to change the permissions for Things that use this template 23 | @denyInstance(Permission.PropertyRead, Groups.Designers, Users.System) 24 | // These decorators can be used to change the permissions for the ThingTemplate itself 25 | @allow(Permission.PropertyRead, Users.Administrator) 26 | // This decorator can be used to add visibility for organizations 27 | @visible(Organizations.Development, Unit(Organizations.MyOrganization, 'MyUnit')) 28 | @visibleInstance(Unit(Organizations.MyOrganization, 'MyUnit2')) 29 | @ThingTemplateDefinition class ExampleThingTemplate extends GenericThing { 30 | 31 | /** 32 | * In thing templates and thing shapes, `@deny` and `@allow` decorators specified on memebers 33 | * automatically apply to instances because the template or shape entity itself does not gain 34 | * the properties defined on it. 35 | */ 36 | @allow(Permission.PropertyRead, Groups.Designers, Users.System) 37 | @persistent exampleProperty!: number; 38 | 39 | @allow(Permission.ServiceInvoke, Users.System) 40 | GetExampleProperty(): number { 41 | return this.exampleProperty; 42 | } 43 | } -------------------------------------------------------------------------------- /src/MyThing.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Typescript enums can be used, if they are declared const. 3 | * In Thingworx they will be fully erased. 4 | */ 5 | const enum Cards { 6 | Spades = "Spades", 7 | Hearts = "Hearts", 8 | Diamonds = "Diamonds", 9 | Clubs = "Clubs" 10 | } 11 | 12 | /** 13 | * Numeric const enums are also supported. 14 | */ 15 | const enum Status { 16 | Running = 0, 17 | Idle = 1, 18 | Error = 2 19 | } 20 | 21 | /** 22 | * Global functions may make use of any thingworx entities like any regular service. 23 | * @param message The message to save. 24 | */ 25 | function logToFile(message: string) { 26 | Things.SystemRepository.AppendToTextFile({ 27 | path: process.env.LOG_FILE_PATH, 28 | data: message + '\n' 29 | }); 30 | } 31 | 32 | /** 33 | * This example file shows how to define a Thing using TypeScript. 34 | * 35 | * A Thing is identified via the `@ThingDefinition` decorator. 36 | * Additional aspects are also specified via decorators: 37 | * - `@published`: Marks the thing as published via federation 38 | * - `@valueStream("MyValueStream")`: Assigns a value stream 39 | * - `@editable`: Makes this entity editable in composer 40 | * 41 | * A Thing must extend from one of the thing shape classes. 42 | * If thing shapes must be applied then the superclass changes into `ThingTemplateWithShapes(template, ...)`, where 43 | * - The first parameter represents the name of the base thing template 44 | * - One or more subsequent parameters are each the name of a thing shape that the thing will implement 45 | */ 46 | @ThingDefinition @published class MyThing extends ThingTemplateWithShapes(RemoteThing, Tunneling) { 47 | 48 | /** 49 | * Thing properties are specified as regular class properties. 50 | * Note that a type annotation is required and can be one of the standard 51 | * Typescript types or Thingworx base types (e.g. `STRING` instead of `string`). 52 | * 53 | * Aspects such as `persistent` and `logged` are specified as decorators. 54 | * The `readonly` aspect is specified via the regular `readonly` keyword. 55 | */ 56 | @persistent @logged myProperty: STRING = "This is the default value"; 57 | 58 | /** 59 | * For certain types, it is possible to use the standard TypeScript type 60 | * names such as `number`, `string`, `boolean` and `Date` and they will be 61 | * converted into the appropriate Thingworx type. 62 | */ 63 | @persistent numberProperty!: number; 64 | 65 | /** 66 | * We can constrain strings to enum values on the compiler side. The ThingWorx type 67 | * (ie. `STRING` not `string`) must be used when constraining properties like this. 68 | */ 69 | cardType: STRING = Cards.Clubs; 70 | 71 | /** 72 | * Number types can be constrained in the same way as strings. The ThingWorx type 73 | * (ie. `NUMBER` not `number`) must be used when constraining properties like this. 74 | */ 75 | status: NUMBER = Status.Error; 76 | 77 | /** 78 | * Properties that don't have a default value must be implicitly unwrapped. 79 | * 80 | * Local bindings are specified using the `@local(source, sourceProperty)` decorator, where: 81 | * - The first parameter is the name of the source thing. 82 | * - The second parameter is the name of the property to bind to. 83 | */ 84 | @local('AuditArchiveCleanupScheduler', 'Enabled') readonly locallyBoundProperty!: string; 85 | 86 | /** 87 | * Numeric properties can use some specific decorators such as `minimumValue`, `maximumValue` and `unit`. 88 | */ 89 | @minimumValue(32) @maximumValue(64) @unit('bytes') bytes!: number; 90 | 91 | /** 92 | * Remotely bound properties are specified via the 93 | * `@remote(name, {cacheTime?, pushType?, pushThreshold?, startType?,foldType?, timeout?})` decorator. From this decorator only 94 | * the first parameter is required. It represents the name of the remote property. 95 | */ 96 | @remote('test', {cacheTime: 0, foldType: 'NONE', pushType: "Value"}) 97 | remotelyBoundProperty!: number; 98 | 99 | /** 100 | * The data change type is specified via the `@dataChangeType(type)` decorator. 101 | * 102 | * Note that some base types are generics in TypeScript. THINGNAME is such as a base type; it takes two type arguments: 103 | * - The first argument is the thing template name or `undefined`. If specified, this must be a string literal or the `undefined` keyword. 104 | * - The second argument is the thing shape name or `undefined`. If specified, this must be a string literal or the `undefined` keyword. 105 | */ 106 | @dataChangeType('ALWAYS') streamToUse!: THINGNAME<'Stream'>; 107 | 108 | /** 109 | * Events are just properties with the custom `EVENT` base type. The data shape to use is specified as 110 | * a type argument. 111 | */ 112 | customEvent!: EVENT; 113 | 114 | /** 115 | * Services created through this project are marked overridable as default. Use the `@final` decorator to make a service non-overridable. 116 | * 117 | * Use the `async` keyword to mark a service async. You cannot specify a return type in this case. Note that you cannot use the 118 | * `await` keyword in these services as the `async` modifier is erased at runtime. 119 | * 120 | * Service parameters must be specified as a destructured object like in the example below. 121 | */ 122 | @final async asyncService({stringParameter = "Parameter default value", infoTable}: {stringParameter: string, infoTable?: INFOTABLE}) { 123 | // `this` should be used in place of `me`, unlike in thingworx 124 | // it will be compiled into `me` 125 | const x = Things[this.streamToUse]; 126 | 127 | // Constrained strings will cause a compiler error whenever anything other than 128 | // an enum constant is assigned to it 129 | this.cardType = Cards.Clubs; 130 | 131 | // The numeric or string literal can be used directly instead of the enum member 132 | this.status = 2; 133 | 134 | this.streamToUse = 'AnomalyMonitorStateStream'; 135 | 136 | // Sections of code can be conditionally excluded from the build based on environment variables 137 | // In this example, the if branch will be removed if the `MOCK_STREAM_DATA` environment variable 138 | // is false or missing 139 | let data: INFOTABLE; 140 | if (process.env.MOCK_STREAM_DATA) { 141 | data = DataShapes.AnomalyMonitorStatusEvent.CreateValues(); 142 | data.AddRow({ 143 | watcherStatus: 'Test Status', 144 | alertType: 'Warning', 145 | alertName: 'Test Alert', 146 | message: 'This is only return when stream data is mocked.', 147 | timestamp: new Date(), 148 | }); 149 | } 150 | else { 151 | data = x.QueryStreamEntriesWithData(); 152 | } 153 | 154 | const y = Things.DownloadedSolutions; 155 | y.AnyAlertAck(); 156 | 157 | this.customEvent({item: 'test'}); 158 | this.asyncService({stringParameter: 'test'}); 159 | } 160 | 161 | /** 162 | * Remote services are specified via `@remoteService` decorator. Just like with properties, only the first parameter 163 | * of this decorator is required. 164 | */ 165 | @remoteService('remoteService', {enableQueue: true}) remoteService(): void {} 166 | 167 | /** 168 | * The `@localSubscription(event[, property])` decorator is used to create subscription to an entity's own events. 169 | */ 170 | @localSubscription('DataChange', 'streamToUse') streamToUseChanged(alertName: STRING, eventData: INFOTABLE, eventName: STRING, eventTime: DATETIME, source: STRING, sourceProperty: STRING) { 171 | const table = Resources.InfoTableFunctions.CreateInfoTableFromDataShape({dataShapeName: 'LinkedList'}); 172 | 173 | table.AddRow({name: 'EntityCount', next: DataShapes.LinkedList.CreateValues()}); 174 | 175 | const table2 = Resources.InfoTableFunctions.Clone({t1: table}); 176 | logger.info(`Name is ${table2.name}`); 177 | } 178 | 179 | /** 180 | * The `@subscription(name, event[, property])` decorator is used to create subscriptions to other entities' events. 181 | * 182 | * Note that in this case, the name of the parameters used by this method cannot be changed or it will lead to runtime errors. 183 | */ 184 | @subscription('AuditDataTable', 'DataChange', "thingTemplate") auditDataTableThingTemplateChanged(alertName: string, eventData: INFOTABLE, eventName: string, eventTime: Date, source: string, sourceProperty: string) { 185 | // Const enums will be fully erased in thingworx 186 | // This condition will be compiled as this.status == 0 187 | if (this.status == Status.Running) { 188 | logger.debug('Thing is running'); 189 | } 190 | 191 | logToFile(`${LOG_PREFIX} State changed to ${this.status}`); 192 | } 193 | 194 | /** 195 | * Remote events are specified via the `@remoteEvent(name)` decorator. 196 | */ 197 | @remoteEvent('remoteEvent') remoteEvent!: EVENT; 198 | 199 | /** 200 | * Services can be marked with the `@deploy` decorator. These services are invoked by the build script after installation 201 | * when using the `deploy` task. 202 | */ 203 | @deploy init(): void { 204 | this.cardType = Cards.Diamonds; 205 | 206 | // In addition to using const enums, any environment variables referenced will also be replaced by their values by the transformer 207 | logger.debug(`Deployment finished on ${process.env.THINGWORX_SERVER}`); 208 | logger.debug(`The value of my custom variable is ${process.env.MY_CUSTOM_VARIABLE}`); 209 | } 210 | 211 | } -------------------------------------------------------------------------------- /src/MyUserList.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Users and groups can be created via a class that extends from `UserList`. A single user list can contain 3 | * multiple users and/or groups. 4 | */ 5 | class MyUserList extends UserList { 6 | 7 | /** 8 | * A user is declared as a property on the user list. It can be assigned a default value containing 9 | * the values of the fields to use for the user extensions. 10 | * 11 | * An optional `UserExtensionLiteral` type annotation can be used to provide autocomplete suggestions. 12 | * All other type annotations are invalid in a user list. 13 | */ 14 | JohnDoe: UserExtensionLiteral = { 15 | firstName: 'John', 16 | lastName: 'Doe' 17 | } 18 | 19 | /** 20 | * A user group is also declared as a property on the user list. It must be assigned a default value 21 | * that is an array containing the members that should be included in the group. 22 | * 23 | * If the group should be empty, the default value should be an empty array. 24 | */ 25 | ExampleGroup = [Users.System, Users.JohnDoe] 26 | 27 | } -------------------------------------------------------------------------------- /src/TestThing.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Entire entities can also be excluded from the build based on environment variables. 3 | * For example, a development environment might include additional entities containing various 4 | * utility and debug services.s 5 | */ 6 | @ifenv(process.env.TESTS_INCLUDED) 7 | @ThingDefinition class TestThing extends GenericThing { 8 | 9 | testVersion() { 10 | const extensions = Subsystems.PlatformSubsystem.GetExtensionPackageList(); 11 | if (extensions.rows.toArray().find(r => r.name == 'bm-thingworx-vscode')?.packageVersion != '2.7.0') { 12 | throw new Error('Incorrect version installed!'); 13 | } 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "outDir": "./dist/", 5 | "sourceMap": true, 6 | "noImplicitAny": false, 7 | "module": "ESNext", 8 | "target": "es5", 9 | "downlevelIteration": true, 10 | "experimentalDecorators": true, 11 | "noImplicitOverride": true, 12 | "strict": true, 13 | "declaration": true, 14 | "jsx": "react", 15 | "jsxFactory": "defineWidget", 16 | "jsxFragmentFactory": "defineWidget", 17 | "skipLibCheck": true, 18 | "moduleResolution": "Node", 19 | "lib": [ 20 | "esnext" 21 | ], 22 | "typeRoots": [ 23 | "./typings/", 24 | "./node_modules/@types/" 25 | ] 26 | }, 27 | "references": [], 28 | "include": [ 29 | "./static/**/*.d.ts", 30 | "./src/**/*.d.ts", 31 | "./src/**/*.ts", 32 | "./src/**/*.tsx", 33 | "./tw_imports/**/*.d.ts", 34 | "./node_modules/bm-thing-transformer/static/**/*.d.ts", 35 | "./node_modules/bm-thing-cli/node_modules/bm-thing-transformer/static/**/*.d.ts" 36 | ] 37 | } -------------------------------------------------------------------------------- /twconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://bogdanmihaiciuc.com/twconfig.schema.json", 3 | "experimentalGlobals": false, 4 | "projectName": "ExampleProject", 5 | "generateProjectEntity": true, 6 | "generateThingInstances": true, 7 | "globalFunctions": true, 8 | "inlineSQL": { 9 | "enabled": true, 10 | "permissions": "system" 11 | }, 12 | "superCalls": { 13 | "permissions": "system" 14 | }, 15 | "includeProjectDependencies": true, 16 | "autoGenerateDataShapeOrdinals": false, 17 | "methodHelpers": { 18 | "methodName": true, 19 | "className": false, 20 | "filePath": false, 21 | "logPrefix": "me.name + '::' + METHOD_NAME + ':: '" 22 | }, 23 | "projectDependencies": [ 24 | ], 25 | "entityDependencies": [ 26 | ], 27 | "extensionDependencies": [ 28 | ], 29 | "UIPlugins": { 30 | "D3RangeGraph": "./ui-static/MyCustomUIPlugin" 31 | } 32 | } -------------------------------------------------------------------------------- /ui-static/MyCustomUIPlugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A plugin used for the D3RangeGraph widget used as an example. This can be placed anywhere, including 3 | * in node_modules. 4 | */ 5 | class MyCustomUIPlugin { 6 | transformerDidProcessWidget(transformer, className, element, widget) { 7 | widget.Properties.DisplayName = "Edited by MyCustomUIPlugin" 8 | } 9 | } 10 | exports.default = MyCustomUIPlugin; -------------------------------------------------------------------------------- /ui-static/defaults.json: -------------------------------------------------------------------------------- 1 | {"D3RangeGraph":{"Area":"UI","Type":"D3RangeGraph","__TypeDisplayName":"D3 Range Graph","Width":500,"Height":300,"ShowLegend":true,"AlignLegendToLeft":false,"DataAutoSort":false,"Fill":false,"FillOpacity":0.1,"DisplayConfiguration":"","ShowDataPoints":false,"DataPointSize":100,"Additive":false,"ShowThresholdsOnHover":false,"MinYValue":-1,"MaxYValue":-1,"SharedYAxis":false,"ShowsYAxis":true,"XTicks":10,"TimeFormat":"","TimeFormatLocale":"","YFormat":"","YAxisFormat":"","ShowTimeOnHover":false,"YTicks":5,"ScaleType":"linear","ScaleFactor":"e","NonLinearScaleInterpolationInterval":0,"SelectorHeight":70,"RightMargin":40,"TopMargin":0,"SecondaryChartType":"none","SecondaryChartOnly":false,"TimelineHeight":72,"TimelinePadding":11,"TimelineShowsXAxis":false,"TimelineColorMap":"{}","BarChartIntervalSize":3600000,"BarChartMinYValue":-1,"BarChartMaxYValue":-1,"BarChartHeight":150,"BarChartYFields":"[]","BarChartColors":"[]","BarChartShowsLegend":true,"BarChartLabels":"[]","BarChartNumberOfYTicks":3,"BarChartShowsXAxis":false,"BarChartMultipleValuesDisplay":"together","BarChartPadding":22,"Interpolation":"linear","MissingValues":"break","Minimal":false,"RangeUpdateType":"break","DragToZoom":false,"EnableTrackGesturesPadAndWheel":true,"EnableTouchGestures":true,"ShowsSelector":true,"SnapsToPoints":false,"AnimationsEnabled":true,"AnimationDuration":300,"ExportFilename":"export.png","ShowDataLoading":true,"Visible":true,"Top":0,"Left":0,"Z-index":10,"Margin":"5"}} -------------------------------------------------------------------------------- /ui-static/widgets.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare interface UIInputInterfaceD3RangeGraph { 3 | 4 | /** The width of the widget */ 5 | "Width"?: NUMBER; 6 | 7 | 8 | /** The height of the widget */ 9 | "Height"?: NUMBER; 10 | 11 | 12 | /** If enabled, the widget will show a legend above the graph. */ 13 | "ShowLegend"?: BOOLEAN; 14 | 15 | 16 | /** If enabled, the legend will be aligned to the left. */ 17 | "AlignLegendToLeft"?: BOOLEAN; 18 | 19 | 20 | /** The dataset for this chart. */ 21 | "Data"?: BindingTarget; 22 | 23 | 24 | /** If enabled, the chart will sort the data */ 25 | "DataAutoSort"?: BOOLEAN; 26 | 27 | 28 | /** If enabled, the widget will fill in the content below each line, using a transparent color. */ 29 | "Fill"?: BOOLEAN; 30 | 31 | 32 | /** If fills are enabled, this sets how transparent the fill color should be. */ 33 | "FillOpacity"?: NUMBER; 34 | 35 | 36 | /** If set, controls how to display each field. Setting this value will cause the Fill, FillOpacity, ShowDataPoints and DataPointSize properties to be ignored. */ 37 | "DisplayConfiguration"?: STRING | BindingTarget; 38 | 39 | 40 | /** If enabled, the actual data points will be visible as circles on the graph. */ 41 | "ShowDataPoints"?: BOOLEAN; 42 | 43 | 44 | /** Must be used with ShowsDataPoints. Controls how large the data points should be. */ 45 | "DataPointSize"?: NUMBER; 46 | 47 | 48 | /** The x axis field. */ 49 | "XField"?: FIELDNAME; 50 | 51 | 52 | /** A stringified JSON array of the y fields to be displayed on the graph. */ 53 | "YFields"?: STRING | BindingTarget; 54 | 55 | 56 | /** A stringified JSON array of the y field colors to be displayed on the graph. */ 57 | "YFieldColors"?: STRING | BindingTarget; 58 | 59 | 60 | /** Optional. A stringified JSON array of the stroke widths to be displayed on the graph. If left blank, the default width of 2 will be used. */ 61 | "StrokeWidths"?: STRING | BindingTarget; 62 | 63 | 64 | /** Optional. A serialized JSON array string of the human readable strings to use on the legend and flyouts. If left blank, the actual field names will be used instead. */ 65 | "Labels"?: STRING | BindingTarget; 66 | 67 | 68 | /** A serialized JSON array string of the dash pattern to use on each line. The dash pattern string should be a standard SVG dash pattern. If left blank, no dash pattern will be used. */ 69 | "Patterns"?: STRING | BindingTarget; 70 | 71 | 72 | /** Requires and enables shared Y axis if used. If enabled, the Y values will be added to each other. */ 73 | "Additive"?: BOOLEAN; 74 | 75 | 76 | /** A serialized JSON array string of the threshold lines to draw. Using this property requires and will automatically enable shared Y axis. */ 77 | "Thresholds"?: STRING | BindingTarget; 78 | 79 | 80 | /** If enabled, the threshold values will be displayed when hovering over the chart. */ 81 | "ShowThresholdsOnHover"?: BOOLEAN; 82 | 83 | 84 | /** */ 85 | "MinYValue"?: NUMBER; 86 | 87 | 88 | /** */ 89 | "MaxYValue"?: NUMBER; 90 | 91 | 92 | /** */ 93 | "SharedYAxis"?: BOOLEAN; 94 | 95 | 96 | /** If disabled, the Y axis will not be visible on the main chart. */ 97 | "ShowsYAxis"?: BOOLEAN; 98 | 99 | 100 | /** The number of ticks to show on the x axis. */ 101 | "XTicks"?: INTEGER; 102 | 103 | 104 | /** The time format to use. If left blank, the chart will use D3's default time format. */ 105 | "TimeFormat"?: STRING; 106 | 107 | 108 | /** The serialized locale definition to use for the time axis. If omitted, the default english locale will be used. */ 109 | "TimeFormatLocale"?: STRING | BindingTarget; 110 | 111 | 112 | /** The number format to use. If left blank, the chart will use the default format with no decimal places. */ 113 | "YFormat"?: STRING; 114 | 115 | 116 | /** Optional. If specified, this is the number format to use for the Y axis labels; otherwise, the YFormat will be used instead. */ 117 | "YAxisFormat"?: STRING; 118 | 119 | 120 | /** If enabled, when hovering the chart, the timestamp will be displayed as well. */ 121 | "ShowTimeOnHover"?: BOOLEAN; 122 | 123 | 124 | /** The number of ticks to show on the y axis. */ 125 | "YTicks"?: INTEGER; 126 | 127 | 128 | /** Controls the type of scale used for the y axis. */ 129 | "ScaleType"?: STRING<"linear" | "log" | "pow" | "sqrt">; 130 | 131 | 132 | /** Only used with logarithmic or power scales. For logarithmic scales, this is the base of the logarithm and for power scales this is the exponent. This value must be either a number, "e" or "pi". */ 133 | "ScaleFactor"?: STRING; 134 | 135 | 136 | /** Must be used with a non-linear scale and linear interpolation. If set to a strictly positive number, the chart will add an additional point every value milliseconds to cause the chart to follow the non-linear scale more closely. This property will have a negative impact on performance with small values and many data points. */ 137 | "NonLinearScaleInterpolationInterval"?: NUMBER; 138 | 139 | 140 | /** The height of the range selector. */ 141 | "SelectorHeight"?: INTEGER; 142 | 143 | 144 | /** The margin reserved for the y axis text. */ 145 | "RightMargin"?: INTEGER; 146 | 147 | 148 | /** The margin reserved for the legend. */ 149 | "TopMargin"?: INTEGER; 150 | 151 | 152 | /** The type of seconday chart to use. The secondary chart will appear below the main chart and above the selector and is controlled by the same selector as the main chart. */ 153 | "SecondaryChartType"?: STRING<"none" | "timeline" | "barchart">; 154 | 155 | 156 | /** Must be used with a secondary chart type other than none. If enabled, the main chart will not be visible. */ 157 | "SecondaryChartOnly"?: BOOLEAN; 158 | 159 | 160 | /** The dataset for the event timeline. */ 161 | "TimelineData"?: BindingTarget; 162 | 163 | 164 | /** The x field for the event timeline. */ 165 | "TimelineXField"?: FIELDNAME; 166 | 167 | 168 | /** The state field for the event timeline. */ 169 | "TimelineStateField"?: FIELDNAME; 170 | 171 | 172 | /** How tall the timeline should be. */ 173 | "TimelineHeight"?: NUMBER; 174 | 175 | 176 | /** Controls the spacing between the chart, selector and secondary timeline. */ 177 | "TimelinePadding"?: INTEGER; 178 | 179 | 180 | /** If enabled, the X axis will be duplicated at the bottom of the timeline. */ 181 | "TimelineShowsXAxis"?: BOOLEAN; 182 | 183 | 184 | /** A serialized JSON object that matches states to the colors they should be drawn with. */ 185 | "TimelineColorMap"?: STRING; 186 | 187 | 188 | /** The dataset for the bar chart. */ 189 | "BarChartData"?: BindingTarget; 190 | 191 | 192 | /** Required for bar charts; specifies the time interval, in milliseconds that each bar should occupy. */ 193 | "BarChartIntervalSize"?: NUMBER; 194 | 195 | 196 | /** The minimum value to use for the bar chart. If set to -1, the chart will use the minimum value within the data set. */ 197 | "BarChartMinYValue"?: NUMBER; 198 | 199 | 200 | /** The maximum value to use for the bar chart. If set to -1, the chart will use the maximum value within the data set. */ 201 | "BarChartMaxYValue"?: NUMBER; 202 | 203 | 204 | /** How tall the bar chart should be. */ 205 | "BarChartHeight"?: NUMBER; 206 | 207 | 208 | /** The x field for the bar chart. */ 209 | "BarChartXField"?: FIELDNAME; 210 | 211 | 212 | /** A serialized JSON array containing the y fields for the bar chart. The bar chart Y axis is always shared with all Y fields. */ 213 | "BarChartYFields"?: STRING | BindingTarget; 214 | 215 | 216 | /** A serialized JSON array containing the bar colors for the bar chart. */ 217 | "BarChartColors"?: STRING | BindingTarget; 218 | 219 | 220 | /** Must be used with ShowLegend. If enabled, the bar chart labels will also be shown on the legend. */ 221 | "BarChartShowsLegend"?: BOOLEAN; 222 | 223 | 224 | /** A serialized JSON array containing the bar labels for the bar chart. */ 225 | "BarChartLabels"?: STRING | BindingTarget; 226 | 227 | 228 | /** The number of Y ticks to show on the bar chart axis. This number is primarily a suggestion; the actual number of ticks may differ */ 229 | "BarChartNumberOfYTicks"?: INTEGER; 230 | 231 | 232 | /** If enabled, the X axis will be duplicated at the bottom of the bar chart. */ 233 | "BarChartShowsXAxis"?: BOOLEAN; 234 | 235 | 236 | /** Controls how multiple bars should appear. Together makes them appear one next to another, while stacked makes them appear one over another. */ 237 | "BarChartMultipleValuesDisplay"?: STRING<"together" | "stacked">; 238 | 239 | 240 | /** Controls the spacing between the chart, selector and secondary bar chart. */ 241 | "BarChartPadding"?: INTEGER; 242 | 243 | 244 | /** The interpolation method to use. */ 245 | "Interpolation"?: STRING<"linear" | "step-before" | "step-after" | "basis" | "basis-open" | "bundle" | "cardinal" | "cardinal-open" | "monotone"> | BindingTarget>; 246 | 247 | 248 | /** Controls how missing values are handled. */ 249 | "MissingValues"?: STRING<"break" | "closest" | "interpolate">; 250 | 251 | 252 | /** If enabled, the axes, selector and secondary charts will be hidden. */ 253 | "Minimal"?: BOOLEAN; 254 | 255 | 256 | /** Controls the selected range changes when new data arrives. */ 257 | "RangeUpdateType"?: STRING<"retain" | "extend" | "move" | "release">; 258 | 259 | 260 | /** The currently selected range start time. If bound, the chart will select the given range start. */ 261 | "RangeStart"?: DATETIME | BindingTarget; 262 | 263 | 264 | /** The currently selected range end time. If bound, the chart will select the given range end. */ 265 | "RangeEnd"?: DATETIME | BindingTarget; 266 | 267 | 268 | /** If enabled, the selector will be disabled and you can instead drag over the chart to zoom in or right click to zoom out. */ 269 | "DragToZoom"?: BOOLEAN; 270 | 271 | 272 | /** If enabled, the user can zoom and pan the chart using the mouse wheel and two finger events on macs with trackpads. */ 273 | "EnableTrackGesturesPadAndWheel"?: BOOLEAN; 274 | 275 | 276 | /** If enabled, the user can zoom and pan the chart through pinch-zoom and two-finger scroll gestures on touch device. */ 277 | "EnableTouchGestures"?: BOOLEAN; 278 | 279 | 280 | /** If disabled, the range selector will be hidden. */ 281 | "ShowsSelector"?: BOOLEAN; 282 | 283 | 284 | /** If enabled, when moving the mouse pointer over the graph, the arrow will snap to the closest data point. */ 285 | "SnapsToPoints"?: BOOLEAN; 286 | 287 | 288 | /** Enables or disables all animations. */ 289 | "AnimationsEnabled"?: BOOLEAN; 290 | 291 | 292 | /** Controls how long update animations should last. This will also affect how long the chart will wait to batch property updates together. */ 293 | "AnimationDuration"?: NUMBER | BindingTarget; 294 | 295 | 296 | /** The style to use for the x axis on both the main chart and the selector. */ 297 | "XAxisStyle"?: STYLEDEFINITION; 298 | 299 | 300 | /** The style to use for the y axis on the main chart. If shared Y axis is not enabled, the text color attribute will be ignored. */ 301 | "YAxisStyle"?: STYLEDEFINITION; 302 | 303 | 304 | /** If set, the chart will use this style's background color. */ 305 | "BackgroundStyle"?: STYLEDEFINITION; 306 | 307 | 308 | /** The file name used when exporting this chart as an image. */ 309 | "ExportFilename"?: STRING; 310 | 311 | 312 | /** The display name of the widget */ 313 | "DisplayName"?: STRING; 314 | 315 | 316 | /** The description of the widget */ 317 | "Description"?: STRING; 318 | 319 | 320 | /** Show a spinner icon when data is being loaded */ 321 | "ShowDataLoading"?: BOOLEAN; 322 | 323 | 324 | /** When enabled makes the widget visible in the mashup */ 325 | "Visible"?: BOOLEAN | BindingTarget; 326 | 327 | 328 | /** Position of the widget in pixels from the top of the mashup */ 329 | "Top"?: NUMBER; 330 | 331 | 332 | /** Position of the widget in pixels from the left of the mashup */ 333 | "Left"?: NUMBER; 334 | 335 | 336 | /** */ 337 | "MinWidth"?: NUMBER; 338 | 339 | 340 | /** The z-index of the widget which allows you to put the widget on top or on the bottom of the view stack */ 341 | "Z-index"?: NUMBER; 342 | 343 | 344 | /** */ 345 | "Margin"?: STYLECSSRECTSIZE; 346 | 347 | 348 | ref?: UIOutputInterfaceD3RangeGraph 349 | } 350 | 351 | declare class UIOutputInterfaceD3RangeGraph { 352 | 353 | /** The currently selected range start time. If bound, the chart will select the given range start. */ 354 | RangeStart: BindingTarget; 355 | 356 | 357 | /** The currently selected range end time. If bound, the chart will select the given range end. */ 358 | RangeEnd: BindingTarget; 359 | 360 | 361 | /** Downloads a PNG version of this chart as it appears when this service is invoked. */ 362 | Export: ServiceBindingTarget 363 | } 364 | 365 | declare function D3RangeGraph(props: UIInputInterfaceD3RangeGraph): UIOutputInterfaceD3RangeGraph 366 | 367 | --------------------------------------------------------------------------------