Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 773f6120c6a77a149e6df2f91dee6028c2111538 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
## Documentation

Before you start, please take a look at the [Getting Started Guide](https://git.eclipse.org/c/mdmbl/org.eclipse.mdm.nucleus.git/plain/doc/GettingStarted_mdmbl.pdf) which can also be found in the [Download area](https://projects.eclipse.org/projects/technology.mdmbl/downloads) of the project and in the `docs` directory relative to this README.md

## Minimum requirements
* JDK 1.8.0_45

> This project uses a Gradle Wrapper

## Build dependencies
Before you can build and deploy the application, you have to checkout the following repositories in the same root directory as the current project
* `git clone https://git.eclipse.org/r/mdmbl/org.eclipse.mdm.api.base.git`
* `git clone https://git.eclipse.org/r/mdmbl/org.eclipse.mdm.api.default.git`
* `git clone https://git.eclipse.org/r/mdmbl/org.eclipse.mdm.api.odsadapter.git`

You **MUST** have the following directory structure afterwards

```
.
├── org.eclipse.mdm.api.base
├── org.eclipse.mdm.api.default
├── org.eclipse.mdm.api.odsadapter
└── org.eclipse.mdm.nucleus
```

Now you can use `gradlew install` in the `org.eclipse.mdm.nucleus` directory, but you should configure the application first.

## Build, deploy and configure the application

1. Edit the `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/core/property.service.ts` and set the `data_host` variable according to your deployment  
 (This property will be used to create the REST URLs to communicate with the backend).
 
2. Build the application with `gradlew install`.  
 The command `gradlew install` executed at `org.eclipse.mdm.nucleus` creates a ZIP archive named `build/distributions/openMDM_application-${version}.zip` at `org.eclipse.mdm.nucleus/build/distributions`  
 The ZIP archive contains the backend `org.eclipse.mdm.nucleus-${version}.war` and the configuration files in the `configuration` directory
  
3. Check that the database for the preference service is running, otherwise start it with `asadmin start-database` as described in the Getting Started Guide.

4. Deploy the WAR file `org.eclipse.mdm.nucleuss-${version}.war` on your application server. Make sure to deploy the WAR file with application name `org.eclipse.mdm.nucleus`, otherwise the `LoginRealmModule` is not able to lookup the `ConnectorService` EJB.  
 Additionally in the following examples, we assume that the context root is also set to `org.eclipse.mdm.nucleus`.  
 When deploying from command line you can use `asadmin deploy --name org.eclipse.mdm.nucleus /path/to/org.eclipse.mdm.nucleus-${version}.war`
 
5. Copy the content of the extracted `configuration` folder to `GLASSFISH_ROOT/glassfish/domains/domain1/config`.  
 There is also a system property `org.eclipse.mdm.configPath`, which can be used to redefine the location of the folder to another location.
 
6. Edit the `org.eclipse.mdm.connector/service.xml` file to configure the data sources

7. Configure a `LoginModule` with name `MDMRealm` (See section **Configure LoginModule** for details)

8. Restart the application server

9. Visit the main page of the client to make sure everything works fine.  
 The main page of the client should be available under `http://SERVER:PORT/{APPLICATIONROOT}` (eg: http://localhost:8080/org.eclipse.mdm.nucleus)

## Configure LoginModule

MDM 5 backend implements the delegated approach to roles and permissions, wherein the data sources (ASAM ODS server, PAK cloud)
themselves can implement their own security scheme (which they already have) and then delegates the appropriate
user data to the backends.

In the case of ASAM ODS servers this is done using a technical user and the `for_user` attribute on the established
connection, so it is a form of 'login on behalf'.

In the case of PAK Cloud this is done by passing the user name along with a http header `X-Remote-User` which is then
used by PAK Cloud to establish the users roles (from an internal database or an external authentication provider).

Before the user is 'logged in on behalf' he is authenticated by a `LoginModule` within the Glassfish application server.
There are different implementations available (e.g. LDAP, Certificate, JDBC, ...). To keep this guide simple, we will setup
a `FileRealm`, which stores the user information in a flat file.

The following command will create a `FileRealm` with name `MDMRealm` that stores the users in a file called `mdm-keyfile`
```
asadmin create-auth-realm --classname com.sun.enterprise.security.auth.realm.file.FileRealm --property file=${com.sun.aas.instanceRoot}/config/mdm-keyfile:jaas-context=MDMRealm:assign-groups=Guest MDMRealm
```

To be able to login you need to explicitly add users to the `MDMRealm`. 
Here we add the user `MdmUser`
```
asadmin create-file-user --authrealmname MDMRealm --groups Admin:DescriptiveDataAuthor:Guest MdmUser
```

Currently three roles are supported by openMDM, adapt the users according to your needs
* **Admin**: Administrator role allowed to access the Administration section in the web application
* **DescriptiveDataAuthor**: Users allowed to edit context data
* **Guest**: Default application users allowed to browse and read measurement data

Keep in mind that the roles are mainly used to enable/disable certain actions in the web application.
Authorization in openMDM is still done via the delegation approach, e.g. the adapters are responsible for handling authorization on data.

Next you need to add the following snippet to your `login.conf` in `${com.sun.aas.instanceRoot}/config/`
```
MDMRealm {
  com.sun.enterprise.security.auth.login.FileLoginModule required;
};
```

As a last step you have to provide the credentials of the technical user in adapter configuration in `service.xml`. 
For the ODS adapter you have to specify the parameters `user` and `password` of the technical user. For example
```
<service entityManagerFactoryClass="org.eclipse.mdm.api.odsadapter.ODSContextFactory">
    ...
	<param name="user">sa</param>
	<param name="password">sa</param>
	...
</service>
```

Make sure to restart Glassfish afterwards.

### Remove MDMLoginRealm from previous versions

In versions 5.0.0M1, 0.10 and older the configuration of a custom login realm was necessary. If you configured your Glassfish server instance
for one of these versions, you can remove the old configuration options and artifact, as they are no longer needed.

#### Steps to delete the old configuration and artifacts

* Delete the JAR file `org.eclipse.mdm.realm.login.glassfish-VERSION.jar` from `GLASSFISH_ROOT/glassfish/domains/domain1/lib`  
* Open the Glassfish login **configuration file** at `GLASSFISH_ROOT/glassfish/domains/domain1/config/login.conf`
* Delete custom MDM realm module entry to this config file  
  ```   
  MDMLoginRealm {
    org.eclipse.mdm.realm.login.glassfish.LoginRealmModule required;
  };
  ```
* Remove the `MDMLoginRealm` by executing `asadmin delete-auth-realm MDMLoginRealm` or by deleting it in the Glassfish web console  

## Configure logging

MDM 5 uses SLF4J and Logback for logging. The default configuration file can be found at `org.eclipse.mdm.nucleus/src/main/resources/logback.xml`.
It logs INFO level messages to `mdm5.log` in the **logs** folder of the Glassfish domain.
If you want to customize logging, you can either edit the file within the WAR file or preferably provide your own logging configuration via system parameter in the JVM settings in Glassfish `-Dlogback.configurationFile=/path/to/config.xml`

## Available REST URLs

Please use the generated OpenAPI Specification, which is generated at build time.
The OpenAPI Specification is available in `org.eclipse.mdm.nucleus/build/openapi/openapi.json` or at runtime at `http://{SERVER}:{PORT}/{APPLICATIONROOT}/openapi.json`.
Furthermore a Swagger UI is available at `http://{SERVER}:{PORT}/{APPLICATIONROOT}/swagger.html`

### Structure of the URLs

* `SERVER` the hostname of the Glassfish server
* `PORT` the port of the Glassfish server
* `APPLICATIONROOT` is the context root under which MDM is deployed
* `SOURCENAME` is the source name the underlying data source

#### Environment

* `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments`
* `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}`
* `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}/localizations`

#### Business objects

The following parameters are recurring throughout the different URLs

* `ID` the identifier of the business object

* `CONTEXTTYPE` is one of
  * `unitundertest`
  * `testsequence`
  * `testequipment`
  
* `DATATYPE` is one of
  * `STRING`
  * `STRING_SEQUENCE`
  * `DATE, DATE_SEQUENCE`
  * `BOOLEAN`
  * `BOOLEAN_SEQUENCE`
  * `BYTE`
  * `BYTE_SEQUENCE`
  * `SHORT`
  * `SHORT_SEQUENCE`
  * `INTEGER`
  * `INTEGER_SEQUENCE`
  * `LONG`
  * `LONG_SEQUENCE`
  * `FLOAT`
  * `FLOAT_SEQUENCE`
  * `DOUBLE`
  * `DOUBLE_SEQUENCE`
  * `BYTE_STREAM`
  * `BYTE_STREAM_SEQUENCE`
  * `FLOAT_COMPLEX`
  * `FLOAT_COMPLEX_SEQUENCE`
  * `DOUBLE_COMPLEX`
  * `DOUBLE_COMPLEX_SEQUENCE`
  * `FILE_LINK`
  * `FILE_LINK_SEQUENCE`
   
* `FILTERSTRING` is a String defining a filter. For example `Test.Name eq "t*"` filters for all tests which names begin with `t`.  
 Strings should be quoted with `"`. `"` characters within strings have to be escaped with a backslash. For backward compatibility `'` is also allowed to quote strings, but may be blocked in URLs in some environments, thus `"` should be preferred.
 
* `REMOTE_PATH` is the remote path of a file link as it is returned in the attributes of type `FILE_LINK` and `FILE_LINK_SEQUENCE`  
 Make sure to properly URL escape the value of the remote path. Especially slashes have to be escaped with `%2F`.
 
##### Examples

Most of the Business objects support the following calls (examples for **TestStep**)

* `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}/teststeps?filter={FILTERSTRING}`
* `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}/teststeps/searchattributes`
* `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}/teststeps/localizations`
* `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}/teststeps/{ID}`
* `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}/teststeps/{ID}/contexts/{CONTEXTTYPE}`

For **TestStep** and **Measurement** it is also possible to receive files

* `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}/teststeps/{ID}/files/{REMOTE_PATH}`


#### Query endpoint

* `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/query`   
 Example: `curl -POST -H "Content-Type: application/json" -d '{"resultType": "test", "columns": ["Test.Name", "TestStep.Name"], "filters": { "sourceName": "SOURCENAME", "filter": "Test.Id gt 1", "searchString": ""}}'http://sa:sa@localhost:8080/org.eclipse.mdm.nucleus/mdm/query`
 
* `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/suggestions`  
 Example: `curl -POST -H "Content-Type: application/json" -d '{"sourceNames": ["SOURCENAME"], "type": "Test", "attrName": "Name"}' http://sa:sa@localhost:8080/org.eclipse.mdm.nucleus/mdm/suggestions`

#### ATFX Import

POST: `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}/import`

Imports an ATFX file into the specified environment. The ATFX file must conform to the MDM 5 data model.

> TODO Templates

Three content types are accepted
* `application/xml`: The ATFX file is sent in the body of the POST request.

* `multipart/form-data`: The ATFX file and its binary files are provided as multipart form data. The field name represents the original file name and the field value the contents of the file.  
 The ATFX file is detected by its file extension 'atfx' or the media type 'application/xml'. Only one ATFX file can be sent at a time.

* `application/zip`: The ATFX file and its component files are provided as a zip file. The ATFX file is detected by its file extension 'atfx'. Only one ATFX file can be included in the zip file.

The POST request returns a JSON-Object with the following properties
* state: Status of the import. Either `OK` or `FAILED`.
* message: Error message in case the import fails.
* stacktrace: Stacktrace in case the import fails.

The returned HTTP status is either `200`, if the imported succeeded or `400`, if the import failed. 


#### ATFX Export

POST: `http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/export`

Accepts a shopping basket XML file and exports the contained elements into an ATFX file. 
Currently it is only supported to export objects from one environment. 

Returned content types in response:
* `application/xml`: The response body contains the exported ATFX file

* `application/zip`: The response contains a zip file with the exported ATFX file and accompanying component files.

### Example: File download

* Login  
 `curl -XPOST --cookie-jar cookie -H "Content-Type: application/x-www-form-urlencoded" -d j_username=sa -d j_password=sa http://localhost:8080/org.eclipse.mdm.nucleus/j_security_check`

* Request a test and extract the remotePath from the first file reference  
 `FILE_PATH="$(curl --cookie cookie -s http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMNVH/teststeps/10 | jq -r '.data[0].attributes[] | select (.name | contains("MDMLinks")) | .value[0].remotePath')"`

* Urlescape remotePath if necessary  
 `FILE_PATH="$(echo $FILE_PATH | python -c 'import sys,urllib;print urllib.quote(sys.stdin.read().strip(), "")')"`

* Request the file content  
 `curl --cookie cookie http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMNVH/teststeps/10/files/$FILE_PATH`


## Preference Service
Preference service stores its data to a relational database. The database connection is looked up by JNDI and the JNDI name and other database relevant parameters are specified in `src/main/resources/META-INF/persistence.xml`. The default JNDI name for the JDBC resource is set to `jdbc/openMDM`. This JDBC resource and its dependent JDBC Connection Pool has to be created and configured within the Glassfish web administration console or through `asadmin` command line tool.

Furthermore the schema has to be created in the configured database. Therefore database DDL scripts are available for PostgreSQL and Apache Derby databases in the folder `schema/org.eclipse.mdm.preferences` of the distribution.
Other databases supported by EclipseLink may also work, but is up to the user to adapt the DDL scripts.

### Examples
* Receive values  
 `curl -GET -H "Content-Type: application/json" http://localhost:8080/org.eclipse.mdm.nucleus/mdm/preferences?scope=SYSTEM&key=ignoredAttributes`
 
* Set value  
 `curl -PUT -H "Content-Type: application/json" -d '{"scope": "SYSTEM", "key": "ignoredAttributes", "value": "[\"*.MimeType\"]"}' http://localhost:8080/org.eclipse.mdm.nucleus/mdm/preferences`
 
* Delete entry  
 `curl -DELETE http://localhost:8080/org.eclipse.mdm.nucleus/mdm/preferences/ID`


## FreeTextSearch
### Configuration
1. Start ElasticSearch. ElasticSearch can be downloaded at https://www.elastic.co/products/elasticsearch. The minimum supported Elasticsearch version is 7. For testing purpose, it can be simply started by executing `bin/run.sh`

2. Edit the configuration (`global.properties`) to fit your environment. You need an ODS Server which supports Notifications. All fields have to be there, but can be empty. However certain ODS Servers ignore some parameters (e.g. PeakODS ignores pollingIntervall since it pushes notifications).

3. Start the application. At the first run it will index the database. This might take a while. After that MDM registers itself as `NotificationListener` and adapts all changes one-by-one.

### Run on dedicated server

The Indexing is completely independent from the searching. So the Indexer can be freely deployed at any other machine.
In the simplest case, the same steps as in Configuration have to be done.
The application can then be deployed on any other machine. All components besides the FreeTextIndexer and its dependencies are not user. Those can be left out, if desired.

## Connector and service.xml

The `service.xml` contains all information necessary for the Connector-Service to connect to the available datasources/adapter instances.
Since this information includes secret information like passwords, it is possible to provide lookups, which gives you the possibility to specify tokens as references to properties defined elsewhere.

There are different lookups available
* `sys`: Looks up variables defined as system properties
* `env`: Looks up variables defined as environment variables

Example:

`<param name="password">${env:odsPassword}</param>`


## Known issues

If you run into `java.lang.ClassNotFoundException: javax.xml.parsers.ParserConfigurationException not found by org.eclipse.persistence.moxy` this is a bug described in https://bugs.eclipse.org/bugs/show_bug.cgi?id=463169 and https://java.net/jira/browse/GLASSFISH-21440.
This solution is to replace `GLASSFISH_HOME/glassfish/modules/org.eclipse.persistence.moxy.jar` with this file http://central.maven.org/maven2/org/eclipse/persistence/org.eclipse.persistence.moxy/2.6.1/org.eclipse.persistence.moxy-2.6.1.jar

If you run into `java.lang.ClassNotFoundException: com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector not found by com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider` you have to download http://central.maven.org/maven2/com/fasterxml/jackson/module/jackson-module-jaxb-annotations/2.5.1/jackson-module-jaxb-annotations-2.5.1.jar and put in the `GLASSFISH_HOME/glassfish/domains/domain1/autodeploy/bundles` folder

## Client preferences

The applications preferences are managed in the administration section. This section can be accessed via the **Administration** button in the main navigation bar or via http://localhost:8080/org.eclipse.mdm.nucleus/administration

A preference is a pair of a unique key and a value. The key is composed of a prefix defining the purpose of the preference followed by an arbitrary but unique identifier string.
It is recommended to choose the identifier the same as the preferences `name` field, in case there is one. The value holds the preference's data in a JSON string.

The following preferences, sorted by their scope, can be set

* User
  - Basket
  - View
  - Filter

* System
  - Node provider
  - Shopping basket file extensions

* Source
  - Ignored attributes

However, it might be necessary to reload the application before a newly defined preference is available or any changes on an existing preferences are applied.

**WARNING**: Corrupted preferences can result in malfunctions of the application.

### User scope

A user scoped preference's area of effect is limited to the logged in user. All user scoped preferences can also be set in dialogs in the main application.

1. Basket  
 Basket preferences keys must start with the prefix `basket.nodes.`  
 This preference has the fields `items` and `name` and holds all the information for saved baskets.  
   * The field `items` holds an array of MDMItems, providing the relevant information of a related node, i.e. `source`, `type` and `id`.  
   * The field `name` defines the name, which is provided in the main application to load this basket.
   
   Example  
   ```
   {
     "items": [{"source":"MDMNVH","type":"Test","id":38}],
     "name": "basketExample"
   }
   ```

2. View  
  View preferences keys must start with the prefix `tableview.view.`  
  This preference has the fields `columns` and `name` and holds the layout information for the tables displaying the search results and the basket nodes.  
    * The field `columns` holds an array of ViewColumn objects. A ViewColumn is an Object with the fields `type`, `name`, `sortOrder` and an optional field `style`.  
    * The field `type` can be set to all available MDM data types, i.e. `Project`, `Pool`, `Test`, `TestStep`, `Measurement`, `ChannelGroup`, `Channel`.  
    * The field `name` field specifies an attribute, which must be an searchable attribute for the given `type`.  
    * The field `sortOrder` can be set by the number `1` (ascending), `-1` (descending), or null (unsorted). Only one column of the array can have a non-null value `sortOrder` at a time. The ViewColumn's style element can hold any CSS-style object. However, it is supposed to contain only the columns width. The column order in the array is identically with the appearance in the table.  
    * The field `name` defines the name, which is provided in the main application to load this view.    

    Example
    ```
    {
     "columns": [
       {
         "type": "Test",
         "name": "Id",
         "style": {"width":"75px"},
         "sortOrder": null
       }
     ],
     "name": "viewExample" 
    }
    ```

3. Filter  
  Filter preferences keys must start with the prefix `filter.nodes.`  
  This preference has the fields `conditions`, `name`, `environments`, `resultType` and `fulltextQuery`.  
  It provides the information for the attribute based / advanced search.  
    * The field `conditions` holds an array of Condition objects. A Condition specifies a search condition for attribute based search. It consists of the fields `type`, `name`, `operator`, `value` and `valueType`. The Condition's `type` can be set to all available MDM data types, i.e. `Project`, `Pool`, `Test`, `TestStep`, `Measurement`, `ChannelGroup`, `Channel`.
      * The Condition's `name` field specifies an attribute, which must be an searchable attribute for the given `type`.  
      * The Condition's `operator` field, holds on of the following numbers: `0`(=), `1`(<), `2`(>), `3`(like).
      * The Condition's `value` field holds a string array containing input for the attribute based search.
      * The Condition's `resultType` field should match the type corresponding to the attribute specified in the `'name` filed, e.g. `string`, `date` or `long`.
    * The field `name` defines the name, which is provided in the main application to load this filter.
    * The field `environments` holds an string array with the names of the sources that should be included in the search.
    * The field `resultType` can be set to all available MDM data types (see above). Only nodes of this type will be included in the search.
    * The field `fulltextQuery` holds a string containing full text search input.  
  
    Example
    ```
    { "conditions": [
        {
          "type": "Test",
          "attribute": "Name",
          "operator": 0,
          "value": [],
          "valueType":"string"
        }
      ],
      "name": "filterExample",
      "environments": ["sourceName"],
      "resultType": "Test",
      "fulltextQuery": ""
    }
    ```


### System scope

System scoped preference are applied globally.

* Node provider  
  The navigation tree structure can be defined via a node provider. The default node provider is set in `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/defaultnodeprovider.json`.  
  It is recommended not to change the default node provider. Instead new node providers can be added as preferences.  
  Their keys must start with the prefix `nodeprovider.`. Once a custom node provider is supplied it can be selected in the dropdown menu in the navigation tree header.  
  
  * Structure

     * First layer/root nodes  
      In the node provider each layer of nodes of the navigation tree is defined in a nested object.  
      The first layer of nodes, is always the environment level. This is necessary due to the provided endpoints.  
      The first layer consists of the fields `name`, `type`, and `children`.  
       * The field `name` sets the name, which is displayed in the application to select the corresponding node provider.  
       * The field `type` defines the data type of the nodes, which is always `Environments` on the first layer.
         
       The next layer of nodes are defined via the field `children`.

     * Children  
      A child object consists of the fields `type`, `query`, and `children`.  
       * The field `type` sets the data type of this layer of nodes. It can be set to all available MDM data types, i.e. `Project`, `Pool`, `Test`, `TestStep`, `Measurement`, `ChannelGroup`, `Channel`.  
       * The filed `query` holds the URL to load this layer of nodes. The first part of the query for each child object should be `/` plus the type of the layer in small letters followed by an `s`.  
         For a nested child objected a filter is attached to the query in the form: `?filter=<parentType>.Id eq {<parentType>.Id}`. The placeholder <parentType> has to be replaced by the actual parent type (set in the field `type` in the parent child or root layer, see example below).  
         At runtime the curly braces will be replaced by the Id of the parent node. Further child objects, and thereby more sublayers, can be nested via the field `children`.
           
       The children field does not need to be set for the last layer of nodes.
  
  * Examples

    * Minimal node provider  
      ```
      { "name": "My name to display", "type": "Environment"}  
      ```    

    * Node provider with two child layers  
      ```
       {
          "name": "My name to display",
          "type": "Environment",
          "children": {
            "type": "Project",
            "query": "/projects",
            "children": {
              "type": "Pool",
              "query": "/pools?filter=Project.Id eq {Project.Id}"
            }
          }
        }
      ```

* Shopping basket file extensions

  When downloading the contents of a shopping basket, a file with extension `mdm` is generated.  
  Additional file extensions can be adding by poviding a preference with key `shoppingbasket.fileextensions`.  
  Here you can define a list of objects with attributes `label` and `extension`.  
  For example: `[ { "label": "MyTool", "extension": "mdm-mytool" }, { "label": "OtherTool", "extension": "mdm-other" } ]`.  
  If `MyTool` has a file handler registered for the extension `mdm-mytool`, the application will be launched if the browser automatically opens the file after download.

### Source scope

Source scoped preferences are applied at any user, but are limited to the specified source. The source can be specified in the **Add Preference** or **Edit Preference** dialog.

* Ignored Attributes  
  The ignore attributes preference must have the exact key `ignoredAttributes`. An identifier must not be added.  
  The preference specifies all attributes, which are supposed to be ignored in the detail view. The preference is a simple JSON string holding a list of attributes in the form {"<type>.<AttributeName>"}.  
  The placeholders <type> and <AttributeName> have to be replaced by the actual type and name of the attribute which should be ignored, respectively.

  Example:  
  `["*.MimeType", "TestStep.Sortindex"]`

## Create a module for the Web application

Any MDM module needs to be a valid [Angular module](https://angular.io/guide/architecture-modules) aka NgModule.  
A NgModule consists of the module definition, components, services and other files that are in the scope of the module. 
The component can hold any content. The component must be declared in a module definition to grant accessibility in the rest of the application.  
All related files should be stored in a new module subfolder in the app folder `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app` (eg. `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/new-module`)

### Example module

An example for a new module can be found at `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/example-module`

### Creating a MDM module

1. Create a new folder eg. `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/new-module`

2. Create an Angular component (eg. `mdm-new.component.ts`) inside that new folder   
   ```typescript
    import {Component} from '@angular/core';
    @Component({template: '<h1>Example Module</h1>'})
    export class MDMNewComponent {}
   ```
   A component is defined in a Typescript file with the `@Component()` decorator.  
   Any HTML content can be provided here in an inline template or via a link to an external HTML resource. Thereafter the component itself, which is supposed to hold any logic needed, is defined and exported.  
   For more details see https://angular.io/guide/architecture-components.
   
3. Create a minimal NgModule  (eg. `mdm-new.module.ts` ) inside that new folder    
   ```typescript
    import { NgModule } from '@angular/core';
    import { MDMCoreModule } from '../core/mdm-core.module';
    import { MDMNewComponent } from './mdm-new.component';
    @NgModule({imports: [MDMCoreModule], declarations: [MDMNewComponent]})
    export class MDMNewModule {}
   ```
   The `imports` array holds all modules from the application needed in this MDM module. It should always hold the `MDMCoreModule`, which provides basic functionalities.  
   On the other hand a NgModule grants accessibility of components of this module in other directives (including the HTML template) within the module (in a `declaration` array) or even in other parts of the application (in an `export` array).  
   For more details see https://angular.io/guide/architecture-modules.

### Embedding a module (no lazy loading)

To embed this new module in MDM you have to register this module in the `MDMModules` Module.
* Import the new module at `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.module.ts`

* Register a route to the new module at `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules-routing.module.ts`  
  ```
  { path: 'new', component: MDMNewComponent}
  ```

 * Furthermore you have to define a display name for the registered route in the `links` array in `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.component.ts`    
  ```
  { path: 'new', name: 'New Module' }
  ```

For further information refer to the Angular documentation:
* https://angular.io/guide/architecture-components
* https://angular.io/guide/architecture-modules
* https://angular.io/guide/router


### Lazy loading and routing module

For lazy-loading (recommended in case there is a high number of modules) embedding of the module is slightly different.
```
  { path: 'example', loadChildren: '../example-module/mdm-example.module#MDMExampleModule'}
```

Additionally, a NgModule, the so called routing module (eg. `mdm-new-routing.module.ts`), is needed to provide the routes to this modules components.
```typescript
  const moduleRoutes: Routes = [{ path: '', component: MDMExampleComponent }];
  @NgModule({imports: [RouterModule.forChild(moduleRoutes)], exports: [RouterModule]})
  export class MDMExampleRoutingModule {}
```   

### Filerelease module
The filerelease module is stored in the following folder `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease`

It can be embedded as any other module described above.  
```
{ path: 'filerelease', component: MDMFilereleaseComponent }
```

```
{ name: 'MDM Files', path: 'filerelease'}
```

### Adding a module to the detail view (eg. filerelease module)
To make the filerelease module available in the detail view it needs to be imported in the corresponding MDM Module `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.module.ts`  
Thereafter, the `MDMFilereleaseCreateComponent` can be imported to the `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/details/components/mdm-detail-view/mdm-detail-view.component.ts`.  
Then the following has to be added to the `org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/src/app/details/components/mdm-detail-view/mdm-detail-view.component.html` file:
```
  <mdm-filerelease-create [node]=selectedNode [disabled]="isReleasable()"></mdm-filerelease-create>
```

It should be located right after the add to basket button:
```
<div class="btn-group pull-right" role="group">
  <button type="button" class="btn btn-default" (click)="add2Basket()" [disabled]="isShopable()">In den Warenkorb</button>
  <mdm-filerelease-create [node]=selectedNode [disabled]="isReleasable()"></mdm-filerelease-create>
</div>
```

## Copyright and License
Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

 See the NOTICE file(s) distributed with this work for additional  
 information regarding copyright ownership.

 This program and the accompanying materials are made available under the  
 terms of the Eclipse Public License v. 2.0 which is available at  
 http://www.eclipse.org/legal/epl-2.0.  

 SPDX-License-Identifier: EPL-2.0

Back to the top