adapted existing /sample methods to condition, removed /condition route
This commit is contained in:
		
							
								
								
									
										2
									
								
								.idea/dictionaries/VLE2FE.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								.idea/dictionaries/VLE2FE.xml
									
									
									
										generated
									
									
									
								
							@@ -4,6 +4,8 @@
 | 
				
			|||||||
      <w>bcrypt</w>
 | 
					      <w>bcrypt</w>
 | 
				
			||||||
      <w>cfenv</w>
 | 
					      <w>cfenv</w>
 | 
				
			||||||
      <w>dfopdb</w>
 | 
					      <w>dfopdb</w>
 | 
				
			||||||
 | 
					      <w>janedoe</w>
 | 
				
			||||||
 | 
					      <w>testcomment</w>
 | 
				
			||||||
    </words>
 | 
					    </words>
 | 
				
			||||||
  </dictionary>
 | 
					  </dictionary>
 | 
				
			||||||
</component>
 | 
					</component>
 | 
				
			||||||
							
								
								
									
										13
									
								
								.idea/libraries/dist.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								.idea/libraries/dist.xml
									
									
									
										generated
									
									
									
								
							@@ -1,13 +0,0 @@
 | 
				
			|||||||
<component name="libraryTable">
 | 
					 | 
				
			||||||
  <library name="dist" type="javaScript">
 | 
					 | 
				
			||||||
    <properties>
 | 
					 | 
				
			||||||
      <sourceFilesUrls>
 | 
					 | 
				
			||||||
        <item url="file://$PROJECT_DIR$/dist" />
 | 
					 | 
				
			||||||
      </sourceFilesUrls>
 | 
					 | 
				
			||||||
    </properties>
 | 
					 | 
				
			||||||
    <CLASSES>
 | 
					 | 
				
			||||||
      <root url="file://$PROJECT_DIR$/dist" />
 | 
					 | 
				
			||||||
    </CLASSES>
 | 
					 | 
				
			||||||
    <SOURCES />
 | 
					 | 
				
			||||||
  </library>
 | 
					 | 
				
			||||||
</component>
 | 
					 | 
				
			||||||
@@ -66,7 +66,6 @@ paths:
 | 
				
			|||||||
    - $ref: 'others.yaml'
 | 
					    - $ref: 'others.yaml'
 | 
				
			||||||
    - $ref: 'sample.yaml'
 | 
					    - $ref: 'sample.yaml'
 | 
				
			||||||
    - $ref: 'material.yaml'
 | 
					    - $ref: 'material.yaml'
 | 
				
			||||||
    - $ref: 'condition.yaml'
 | 
					 | 
				
			||||||
    - $ref: 'measurement.yaml'
 | 
					    - $ref: 'measurement.yaml'
 | 
				
			||||||
    - $ref: 'template.yaml'
 | 
					    - $ref: 'template.yaml'
 | 
				
			||||||
    - $ref: 'model.yaml'
 | 
					    - $ref: 'model.yaml'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,111 +0,0 @@
 | 
				
			|||||||
/condition/{id}:
 | 
					 | 
				
			||||||
  parameters:
 | 
					 | 
				
			||||||
    - $ref: 'api.yaml#/components/parameters/Id'
 | 
					 | 
				
			||||||
  get:
 | 
					 | 
				
			||||||
    summary: condition by id
 | 
					 | 
				
			||||||
    description: 'Auth: all, levels: read, write, maintain, dev, admin'
 | 
					 | 
				
			||||||
    x-doc: status handling (accessible (only for maintain/admin))?  # TODO after decision
 | 
					 | 
				
			||||||
    tags:
 | 
					 | 
				
			||||||
      - /condition
 | 
					 | 
				
			||||||
    responses:
 | 
					 | 
				
			||||||
      200:
 | 
					 | 
				
			||||||
        description: condition details
 | 
					 | 
				
			||||||
        content:
 | 
					 | 
				
			||||||
          application/json:
 | 
					 | 
				
			||||||
            schema:
 | 
					 | 
				
			||||||
              $ref: 'api.yaml#/components/schemas/Condition'
 | 
					 | 
				
			||||||
      400:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/400'
 | 
					 | 
				
			||||||
      401:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/401'
 | 
					 | 
				
			||||||
      404:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/404'
 | 
					 | 
				
			||||||
      500:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/500'
 | 
					 | 
				
			||||||
  put:
 | 
					 | 
				
			||||||
    summary: change condition
 | 
					 | 
				
			||||||
    description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to reference samples created by another user'
 | 
					 | 
				
			||||||
    x-doc: status is reset to 0 on any changes
 | 
					 | 
				
			||||||
    tags:
 | 
					 | 
				
			||||||
      - /condition
 | 
					 | 
				
			||||||
    security:
 | 
					 | 
				
			||||||
      - BasicAuth: []
 | 
					 | 
				
			||||||
    requestBody:
 | 
					 | 
				
			||||||
      required: true
 | 
					 | 
				
			||||||
      content:
 | 
					 | 
				
			||||||
        application/json:
 | 
					 | 
				
			||||||
          schema:
 | 
					 | 
				
			||||||
            allOf:
 | 
					 | 
				
			||||||
              - $ref: 'api.yaml#/components/schemas/_Id'
 | 
					 | 
				
			||||||
            properties:
 | 
					 | 
				
			||||||
              parameters:
 | 
					 | 
				
			||||||
                type: object
 | 
					 | 
				
			||||||
    responses:
 | 
					 | 
				
			||||||
      200:
 | 
					 | 
				
			||||||
        description: condition details
 | 
					 | 
				
			||||||
        content:
 | 
					 | 
				
			||||||
          application/json:
 | 
					 | 
				
			||||||
            schema:
 | 
					 | 
				
			||||||
              $ref: 'api.yaml#/components/schemas/Condition'
 | 
					 | 
				
			||||||
      400:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/400'
 | 
					 | 
				
			||||||
      401:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/401'
 | 
					 | 
				
			||||||
      403:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/403'
 | 
					 | 
				
			||||||
      404:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/404'
 | 
					 | 
				
			||||||
      500:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/500'
 | 
					 | 
				
			||||||
  delete:
 | 
					 | 
				
			||||||
    summary: delete condition
 | 
					 | 
				
			||||||
    description: 'Auth: basic, levels: write, maintain, dev, admin'
 | 
					 | 
				
			||||||
    x-doc: sets status to -1
 | 
					 | 
				
			||||||
    tags:
 | 
					 | 
				
			||||||
      - /condition
 | 
					 | 
				
			||||||
    security:
 | 
					 | 
				
			||||||
      - BasicAuth: []
 | 
					 | 
				
			||||||
    responses:
 | 
					 | 
				
			||||||
      200:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/Ok'
 | 
					 | 
				
			||||||
      400:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/400'
 | 
					 | 
				
			||||||
      401:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/401'
 | 
					 | 
				
			||||||
      403:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/403'
 | 
					 | 
				
			||||||
      404:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/404'
 | 
					 | 
				
			||||||
      500:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/500'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/condition/new:
 | 
					 | 
				
			||||||
  post:
 | 
					 | 
				
			||||||
    summary: add condition
 | 
					 | 
				
			||||||
    description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to reference samples created by another user'
 | 
					 | 
				
			||||||
    x-doc: 'Adds status: 0 automatically'
 | 
					 | 
				
			||||||
    tags:
 | 
					 | 
				
			||||||
      - /condition
 | 
					 | 
				
			||||||
    security:
 | 
					 | 
				
			||||||
      - BasicAuth: []
 | 
					 | 
				
			||||||
    requestBody:
 | 
					 | 
				
			||||||
      required: true
 | 
					 | 
				
			||||||
      content:
 | 
					 | 
				
			||||||
        application/json:
 | 
					 | 
				
			||||||
          schema:
 | 
					 | 
				
			||||||
            $ref: 'api.yaml#/components/schemas/Condition'
 | 
					 | 
				
			||||||
    responses:
 | 
					 | 
				
			||||||
      200:
 | 
					 | 
				
			||||||
        description: condition details
 | 
					 | 
				
			||||||
        content:
 | 
					 | 
				
			||||||
          application/json:
 | 
					 | 
				
			||||||
            schema:
 | 
					 | 
				
			||||||
              $ref: 'api.yaml#/components/schemas/Condition'
 | 
					 | 
				
			||||||
      400:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/400'
 | 
					 | 
				
			||||||
      401:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/401'
 | 
					 | 
				
			||||||
      403:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/403'
 | 
					 | 
				
			||||||
      500:
 | 
					 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/500'
 | 
					 | 
				
			||||||
@@ -19,7 +19,7 @@
 | 
				
			|||||||
      500:
 | 
					      500:
 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/500'
 | 
					        $ref: 'api.yaml#/components/responses/500'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/samples{group}:
 | 
					/samples/{group}:
 | 
				
			||||||
  parameters:
 | 
					  parameters:
 | 
				
			||||||
    - $ref: 'api.yaml#/components/parameters/Group'
 | 
					    - $ref: 'api.yaml#/components/parameters/Group'
 | 
				
			||||||
  get:
 | 
					  get:
 | 
				
			||||||
@@ -48,7 +48,7 @@
 | 
				
			|||||||
  get:
 | 
					  get:
 | 
				
			||||||
    summary: TODO sample details
 | 
					    summary: TODO sample details
 | 
				
			||||||
    description: 'Auth: all, levels: read, write, maintain, dev, admin'
 | 
					    description: 'Auth: all, levels: read, write, maintain, dev, admin'
 | 
				
			||||||
    x-doc: status handling (accessible (only for maintain/admin))?  # TODO after decision
 | 
					    x-doc: deleted samples are available only for maintain/admin
 | 
				
			||||||
    tags:
 | 
					    tags:
 | 
				
			||||||
      - /sample
 | 
					      - /sample
 | 
				
			||||||
    responses:
 | 
					    responses:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,15 @@ SampleProperties:
 | 
				
			|||||||
    batch:
 | 
					    batch:
 | 
				
			||||||
      type: string
 | 
					      type: string
 | 
				
			||||||
      example: 1560237365
 | 
					      example: 1560237365
 | 
				
			||||||
 | 
					    condition:
 | 
				
			||||||
 | 
					      type: object
 | 
				
			||||||
 | 
					      properties:
 | 
				
			||||||
 | 
					        condition_template:
 | 
				
			||||||
 | 
					          $ref: 'api.yaml#/components/schemas/Id'
 | 
				
			||||||
 | 
					      example:
 | 
				
			||||||
 | 
					        condition_template: 5ea0450ed851c30a90e70894
 | 
				
			||||||
 | 
					        material: hot air
 | 
				
			||||||
 | 
					        weeks: 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SampleRefs:
 | 
					SampleRefs:
 | 
				
			||||||
  allOf:
 | 
					  allOf:
 | 
				
			||||||
@@ -55,7 +64,7 @@ Sample:
 | 
				
			|||||||
          type: array
 | 
					          type: array
 | 
				
			||||||
          items:
 | 
					          items:
 | 
				
			||||||
            properties:
 | 
					            properties:
 | 
				
			||||||
              id:
 | 
					              sample_id:
 | 
				
			||||||
                $ref: 'api.yaml#/components/schemas/Id'
 | 
					                $ref: 'api.yaml#/components/schemas/Id'
 | 
				
			||||||
              relation:
 | 
					              relation:
 | 
				
			||||||
                type: string
 | 
					                type: string
 | 
				
			||||||
@@ -67,7 +76,8 @@ SampleDetail:
 | 
				
			|||||||
    - $ref: 'api.yaml#/components/schemas/SampleProperties'
 | 
					    - $ref: 'api.yaml#/components/schemas/SampleProperties'
 | 
				
			||||||
  properties:
 | 
					  properties:
 | 
				
			||||||
    material:
 | 
					    material:
 | 
				
			||||||
      $ref: 'api.yaml#/components/schemas/Material'
 | 
					      allOf:
 | 
				
			||||||
 | 
					        - $ref: 'api.yaml#/components/schemas/Material'
 | 
				
			||||||
    notes:
 | 
					    notes:
 | 
				
			||||||
      type: object
 | 
					      type: object
 | 
				
			||||||
      properties:
 | 
					      properties:
 | 
				
			||||||
@@ -77,10 +87,14 @@ SampleDetail:
 | 
				
			|||||||
          type: array
 | 
					          type: array
 | 
				
			||||||
          items:
 | 
					          items:
 | 
				
			||||||
            $ref: 'api.yaml#/components/schemas/Id'
 | 
					            $ref: 'api.yaml#/components/schemas/Id'
 | 
				
			||||||
    conditions:
 | 
					    measurements:
 | 
				
			||||||
      type: array
 | 
					      type: array
 | 
				
			||||||
      items:
 | 
					      items:
 | 
				
			||||||
        $ref: 'api.yaml#/components/schemas/Condition'
 | 
					        allOf:
 | 
				
			||||||
 | 
					          - $ref: 'api.yaml#/components/schemas/Measurement'
 | 
				
			||||||
 | 
					    user:
 | 
				
			||||||
 | 
					      type: string
 | 
				
			||||||
 | 
					      example: admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Material:
 | 
					Material:
 | 
				
			||||||
  allOf:
 | 
					  allOf:
 | 
				
			||||||
@@ -115,21 +129,6 @@ Material:
 | 
				
			|||||||
            type: string
 | 
					            type: string
 | 
				
			||||||
            example: 5514263423
 | 
					            example: 5514263423
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Condition:
 | 
					 | 
				
			||||||
  allOf:
 | 
					 | 
				
			||||||
    - $ref: 'api.yaml#/components/schemas/_Id'
 | 
					 | 
				
			||||||
  properties:
 | 
					 | 
				
			||||||
    sample_id:
 | 
					 | 
				
			||||||
      $ref: 'api.yaml#/components/schemas/Id'
 | 
					 | 
				
			||||||
    number:
 | 
					 | 
				
			||||||
      type: string
 | 
					 | 
				
			||||||
      readOnly: true
 | 
					 | 
				
			||||||
      example: B1
 | 
					 | 
				
			||||||
    parameters:
 | 
					 | 
				
			||||||
      type: object
 | 
					 | 
				
			||||||
    treatment_template:
 | 
					 | 
				
			||||||
      $ref: 'api.yaml#/components/schemas/Id'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Measurement:
 | 
					Measurement:
 | 
				
			||||||
  allOf:
 | 
					  allOf:
 | 
				
			||||||
    - $ref: 'api.yaml#/components/schemas/_Id'
 | 
					    - $ref: 'api.yaml#/components/schemas/_Id'
 | 
				
			||||||
@@ -166,7 +165,7 @@ Template:
 | 
				
			|||||||
              min: 0
 | 
					              min: 0
 | 
				
			||||||
              max: 2
 | 
					              max: 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TreatmentTemplate:
 | 
					ConditionTemplate:
 | 
				
			||||||
  allOf:
 | 
					  allOf:
 | 
				
			||||||
    - $ref: 'api.yaml#/components/schemas/Template'
 | 
					    - $ref: 'api.yaml#/components/schemas/Template'
 | 
				
			||||||
  properties:
 | 
					  properties:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/template/treatments:
 | 
					/template/conditions:
 | 
				
			||||||
  get:
 | 
					  get:
 | 
				
			||||||
    summary: all available treatment methods
 | 
					    summary: all available condition methods
 | 
				
			||||||
    description: 'Auth: basic, levels: read, write, maintain, dev, admin'
 | 
					    description: 'Auth: basic, levels: read, write, maintain, dev, admin'
 | 
				
			||||||
    tags:
 | 
					    tags:
 | 
				
			||||||
      - /template
 | 
					      - /template
 | 
				
			||||||
@@ -8,23 +8,23 @@
 | 
				
			|||||||
      - BasicAuth: []
 | 
					      - BasicAuth: []
 | 
				
			||||||
    responses:
 | 
					    responses:
 | 
				
			||||||
      200:
 | 
					      200:
 | 
				
			||||||
        description: list of treatments
 | 
					        description: list of conditions
 | 
				
			||||||
        content:
 | 
					        content:
 | 
				
			||||||
          application/json:
 | 
					          application/json:
 | 
				
			||||||
            schema:
 | 
					            schema:
 | 
				
			||||||
              type: array
 | 
					              type: array
 | 
				
			||||||
              items:
 | 
					              items:
 | 
				
			||||||
                $ref: 'api.yaml#/components/schemas/TreatmentTemplate'
 | 
					                $ref: 'api.yaml#/components/schemas/ConditionTemplate'
 | 
				
			||||||
      401:
 | 
					      401:
 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/401'
 | 
					        $ref: 'api.yaml#/components/responses/401'
 | 
				
			||||||
      500:
 | 
					      500:
 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/500'
 | 
					        $ref: 'api.yaml#/components/responses/500'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/template/treatment/{id}:
 | 
					/template/condition/{id}:
 | 
				
			||||||
  parameters:
 | 
					  parameters:
 | 
				
			||||||
    - $ref: 'api.yaml#/components/parameters/Id'
 | 
					    - $ref: 'api.yaml#/components/parameters/Id'
 | 
				
			||||||
  get:
 | 
					  get:
 | 
				
			||||||
    summary: treatment method details
 | 
					    summary: condition method details
 | 
				
			||||||
    description: 'Auth: basic, levels: read, write, maintain, admin'
 | 
					    description: 'Auth: basic, levels: read, write, maintain, admin'
 | 
				
			||||||
    tags:
 | 
					    tags:
 | 
				
			||||||
      - /template
 | 
					      - /template
 | 
				
			||||||
@@ -32,11 +32,11 @@
 | 
				
			|||||||
      - BasicAuth: []
 | 
					      - BasicAuth: []
 | 
				
			||||||
    responses:
 | 
					    responses:
 | 
				
			||||||
      200:
 | 
					      200:
 | 
				
			||||||
        description: treatment details
 | 
					        description: condition details
 | 
				
			||||||
        content:
 | 
					        content:
 | 
				
			||||||
          application/json:
 | 
					          application/json:
 | 
				
			||||||
            schema:
 | 
					            schema:
 | 
				
			||||||
              $ref: 'api.yaml#/components/schemas/TreatmentTemplate'
 | 
					              $ref: 'api.yaml#/components/schemas/ConditionTemplate'
 | 
				
			||||||
      401:
 | 
					      401:
 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/401'
 | 
					        $ref: 'api.yaml#/components/responses/401'
 | 
				
			||||||
      404:
 | 
					      404:
 | 
				
			||||||
@@ -44,7 +44,7 @@
 | 
				
			|||||||
      500:
 | 
					      500:
 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/500'
 | 
					        $ref: 'api.yaml#/components/responses/500'
 | 
				
			||||||
  put:
 | 
					  put:
 | 
				
			||||||
    summary: change treatment method
 | 
					    summary: change condition method
 | 
				
			||||||
    description: 'Auth: basic, levels: maintain, admin'
 | 
					    description: 'Auth: basic, levels: maintain, admin'
 | 
				
			||||||
    x-doc: With a change a new version is set, resulting in a new template with a new id
 | 
					    x-doc: With a change a new version is set, resulting in a new template with a new id
 | 
				
			||||||
    tags:
 | 
					    tags:
 | 
				
			||||||
@@ -56,14 +56,14 @@
 | 
				
			|||||||
      content:
 | 
					      content:
 | 
				
			||||||
        application/json:
 | 
					        application/json:
 | 
				
			||||||
          schema:
 | 
					          schema:
 | 
				
			||||||
            $ref: 'api.yaml#/components/schemas/TreatmentTemplate'
 | 
					            $ref: 'api.yaml#/components/schemas/ConditionTemplate'
 | 
				
			||||||
    responses:
 | 
					    responses:
 | 
				
			||||||
      200:
 | 
					      200:
 | 
				
			||||||
        description: treatment details
 | 
					        description: condition details
 | 
				
			||||||
        content:
 | 
					        content:
 | 
				
			||||||
          application/json:
 | 
					          application/json:
 | 
				
			||||||
            schema:
 | 
					            schema:
 | 
				
			||||||
              $ref: 'api.yaml#/components/schemas/TreatmentTemplate'
 | 
					              $ref: 'api.yaml#/components/schemas/ConditionTemplate'
 | 
				
			||||||
      400:
 | 
					      400:
 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/400'
 | 
					        $ref: 'api.yaml#/components/responses/400'
 | 
				
			||||||
      401:
 | 
					      401:
 | 
				
			||||||
@@ -75,9 +75,9 @@
 | 
				
			|||||||
      500:
 | 
					      500:
 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/500'
 | 
					        $ref: 'api.yaml#/components/responses/500'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/template/treatment/new:
 | 
					/template/condition/new:
 | 
				
			||||||
  post:
 | 
					  post:
 | 
				
			||||||
    summary: add treatment method
 | 
					    summary: add condition method
 | 
				
			||||||
    description: 'Auth: basic, levels: maintain, admin'
 | 
					    description: 'Auth: basic, levels: maintain, admin'
 | 
				
			||||||
    tags:
 | 
					    tags:
 | 
				
			||||||
      - /template
 | 
					      - /template
 | 
				
			||||||
@@ -88,14 +88,14 @@
 | 
				
			|||||||
      content:
 | 
					      content:
 | 
				
			||||||
        application/json:
 | 
					        application/json:
 | 
				
			||||||
          schema:
 | 
					          schema:
 | 
				
			||||||
            $ref: 'api.yaml#/components/schemas/TreatmentTemplate'
 | 
					            $ref: 'api.yaml#/components/schemas/ConditionTemplate'
 | 
				
			||||||
    responses:
 | 
					    responses:
 | 
				
			||||||
      200:
 | 
					      200:
 | 
				
			||||||
        description: treatment details
 | 
					        description: condition details
 | 
				
			||||||
        content:
 | 
					        content:
 | 
				
			||||||
          application/json:
 | 
					          application/json:
 | 
				
			||||||
            schema:
 | 
					            schema:
 | 
				
			||||||
              $ref: 'api.yaml#/components/schemas/TreatmentTemplate'
 | 
					              $ref: 'api.yaml#/components/schemas/ConditionTemplate'
 | 
				
			||||||
      400:
 | 
					      400:
 | 
				
			||||||
        $ref: 'api.yaml#/components/responses/400'
 | 
					        $ref: 'api.yaml#/components/responses/400'
 | 
				
			||||||
      401:
 | 
					      401:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										976
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										976
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -5,10 +5,12 @@
 | 
				
			|||||||
  "main": "index.js",
 | 
					  "main": "index.js",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "tsc": "tsc",
 | 
					    "tsc": "tsc",
 | 
				
			||||||
 | 
					    "tsc-full": "del /q dist\\* & (for /d %x in (dist\\*) do @rd /s /q \"%x\") & tsc",
 | 
				
			||||||
    "test": "mocha dist/**/**.spec.js",
 | 
					    "test": "mocha dist/**/**.spec.js",
 | 
				
			||||||
    "start": "tsc && node dist/index.js || exit 1",
 | 
					    "start": "tsc && node dist/index.js || exit 1",
 | 
				
			||||||
    "dev": "nodemon -e ts,yaml --exec \"npm run start\"",
 | 
					    "dev": "nodemon -e ts,yaml --exec \"npm run start\"",
 | 
				
			||||||
    "loadDev": "node dist/test/loadDev.js"
 | 
					    "loadDev": "node dist/test/loadDev.js",
 | 
				
			||||||
 | 
					    "coverage": "nyc --reporter=html --reporter=tex mocha dist/**/**.spec.js"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "keywords": [],
 | 
					  "keywords": [],
 | 
				
			||||||
  "author": "",
 | 
					  "author": "",
 | 
				
			||||||
@@ -44,6 +46,7 @@
 | 
				
			|||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@types/lodash": "^4.14.150",
 | 
					    "@types/lodash": "^4.14.150",
 | 
				
			||||||
    "mocha": "^7.1.2",
 | 
					    "mocha": "^7.1.2",
 | 
				
			||||||
 | 
					    "nyc": "^15.0.1",
 | 
				
			||||||
    "should": "^13.2.3",
 | 
					    "should": "^13.2.3",
 | 
				
			||||||
    "supertest": "^4.0.2"
 | 
					    "supertest": "^4.0.2"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,13 @@ const globals = {
 | 
				
			|||||||
    'maintain',
 | 
					    'maintain',
 | 
				
			||||||
    'dev',
 | 
					    'dev',
 | 
				
			||||||
    'admin'
 | 
					    'admin'
 | 
				
			||||||
  ]
 | 
					  ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  status: {  // document statuses
 | 
				
			||||||
 | 
					    deleted: -1,
 | 
				
			||||||
 | 
					    new: 0,
 | 
				
			||||||
 | 
					    validated: 10,
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default globals;
 | 
					export default globals;
 | 
				
			||||||
							
								
								
									
										21
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/index.ts
									
									
									
									
									
								
							@@ -11,6 +11,7 @@ import db from './db';
 | 
				
			|||||||
// TODO: validation: VZ, Humidity: min/max value, DPT: filename
 | 
					// TODO: validation: VZ, Humidity: min/max value, DPT: filename
 | 
				
			||||||
// TODO: condition values not needed on initial add
 | 
					// TODO: condition values not needed on initial add
 | 
				
			||||||
// TODO: add multiple samples at once
 | 
					// TODO: add multiple samples at once
 | 
				
			||||||
 | 
					// TODO: coverage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// tell if server is running in debug or production environment
 | 
					// tell if server is running in debug or production environment
 | 
				
			||||||
console.info(process.env.NODE_ENV === 'production' ? '===== PRODUCTION =====' : process.env.NODE_ENV === 'test' ? '' :'===== DEVELOPMENT =====');
 | 
					console.info(process.env.NODE_ENV === 'production' ? '===== PRODUCTION =====' : process.env.NODE_ENV === 'test' ? '' :'===== DEVELOPMENT =====');
 | 
				
			||||||
@@ -48,14 +49,20 @@ app.use((req, res, next) => {  // no database connection error
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
app.use(require('./helpers/authorize'));  // handle authentication
 | 
					app.use(require('./helpers/authorize'));  // handle authentication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// redirect /api routes for Angular proxy in development
 | 
				
			||||||
 | 
					app.use('/api/:url', (req, res) => {
 | 
				
			||||||
 | 
					  req.url = '/' + req.params.url;
 | 
				
			||||||
 | 
					  app.handle(req, res);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// require routes
 | 
					// require routes
 | 
				
			||||||
app.use('/api', require('./routes/root'));
 | 
					app.use('/', require('./routes/root'));
 | 
				
			||||||
app.use('/api', require('./routes/sample'));
 | 
					app.use('/', require('./routes/sample'));
 | 
				
			||||||
app.use('/api', require('./routes/material'));
 | 
					app.use('/', require('./routes/material'));
 | 
				
			||||||
app.use('/api', require('./routes/template'));
 | 
					app.use('/', require('./routes/template'));
 | 
				
			||||||
app.use('/api', require('./routes/user'));
 | 
					app.use('/', require('./routes/user'));
 | 
				
			||||||
app.use('/api', require('./routes/condition'));
 | 
					app.use('/', require('./routes/measurement'));
 | 
				
			||||||
app.use('/api', require('./routes/measurement'));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// static files
 | 
					// static files
 | 
				
			||||||
app.use('/static', express.static('static'));
 | 
					app.use('/static', express.static('static'));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +0,0 @@
 | 
				
			|||||||
import mongoose from 'mongoose';
 | 
					 | 
				
			||||||
import SampleModel from './sample';
 | 
					 | 
				
			||||||
import TreatmentTemplateModel from './treatment_template';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const ConditionSchema = new mongoose.Schema({
 | 
					 | 
				
			||||||
  sample_id: {type: mongoose.Schema.Types.ObjectId, ref: SampleModel},
 | 
					 | 
				
			||||||
  number: String,
 | 
					 | 
				
			||||||
  parameters: mongoose.Schema.Types.Mixed,
 | 
					 | 
				
			||||||
  treatment_template: {type: mongoose.Schema.Types.ObjectId, ref: TreatmentTemplateModel},
 | 
					 | 
				
			||||||
  status: Number
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default mongoose.model('condition', ConditionSchema);
 | 
					 | 
				
			||||||
@@ -1,13 +1,12 @@
 | 
				
			|||||||
import mongoose from 'mongoose';
 | 
					import mongoose from 'mongoose';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TreatmentTemplateSchema = new mongoose.Schema({
 | 
					const ConditionTemplateSchema = new mongoose.Schema({
 | 
				
			||||||
  name: String,
 | 
					  name: String,
 | 
				
			||||||
  version: Number,
 | 
					  version: Number,
 | 
				
			||||||
  number_prefix: String,
 | 
					 | 
				
			||||||
  parameters: [{
 | 
					  parameters: [{
 | 
				
			||||||
    name: String,
 | 
					    name: String,
 | 
				
			||||||
    range: mongoose.Schema.Types.Mixed
 | 
					    range: mongoose.Schema.Types.Mixed
 | 
				
			||||||
  }]
 | 
					  }]
 | 
				
			||||||
}, {minimize: false});  // to allow empty objects
 | 
					}, {minimize: false});  // to allow empty objects
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default mongoose.model('treatment_template', TreatmentTemplateSchema);
 | 
					export default mongoose.model('condition_template', ConditionTemplateSchema);
 | 
				
			||||||
@@ -1,9 +1,11 @@
 | 
				
			|||||||
import mongoose from 'mongoose';
 | 
					import mongoose from 'mongoose';
 | 
				
			||||||
import ConditionModel from './condition';
 | 
					import SampleModel from './sample';
 | 
				
			||||||
import MeasurementTemplateModel from './measurement_template';
 | 
					import MeasurementTemplateModel from './measurement_template';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: change to sample_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MeasurementSchema = new mongoose.Schema({
 | 
					const MeasurementSchema = new mongoose.Schema({
 | 
				
			||||||
  condition_id: {type: mongoose.Schema.Types.ObjectId, ref: ConditionModel},
 | 
					  sample_id: {type: mongoose.Schema.Types.ObjectId, ref: SampleModel},
 | 
				
			||||||
  values: mongoose.Schema.Types.Mixed,
 | 
					  values: mongoose.Schema.Types.Mixed,
 | 
				
			||||||
  measurement_template: {type: mongoose.Schema.Types.ObjectId, ref: MeasurementTemplateModel},
 | 
					  measurement_template: {type: mongoose.Schema.Types.ObjectId, ref: MeasurementTemplateModel},
 | 
				
			||||||
  status: Number
 | 
					  status: Number
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ import mongoose from 'mongoose';
 | 
				
			|||||||
const NoteSchema = new mongoose.Schema({
 | 
					const NoteSchema = new mongoose.Schema({
 | 
				
			||||||
  comment: String,
 | 
					  comment: String,
 | 
				
			||||||
  sample_references: [{
 | 
					  sample_references: [{
 | 
				
			||||||
    id: mongoose.Schema.Types.ObjectId,
 | 
					    sample_id: mongoose.Schema.Types.ObjectId,
 | 
				
			||||||
    relation: String
 | 
					    relation: String
 | 
				
			||||||
  }],
 | 
					  }],
 | 
				
			||||||
  custom_fields: mongoose.Schema.Types.Mixed
 | 
					  custom_fields: mongoose.Schema.Types.Mixed
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,10 +9,11 @@ const SampleSchema = new mongoose.Schema({
 | 
				
			|||||||
  type: String,
 | 
					  type: String,
 | 
				
			||||||
  color: String,
 | 
					  color: String,
 | 
				
			||||||
  batch: String,
 | 
					  batch: String,
 | 
				
			||||||
 | 
					  condition: mongoose.Schema.Types.Mixed,
 | 
				
			||||||
  material_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialModel},
 | 
					  material_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialModel},
 | 
				
			||||||
  note_id: {type: mongoose.Schema.Types.ObjectId, ref: NoteModel},
 | 
					  note_id: {type: mongoose.Schema.Types.ObjectId, ref: NoteModel},
 | 
				
			||||||
  user_id: {type: mongoose.Schema.Types.ObjectId, ref: UserModel},
 | 
					  user_id: {type: mongoose.Schema.Types.ObjectId, ref: UserModel},
 | 
				
			||||||
  status: Number
 | 
					  status: Number
 | 
				
			||||||
});
 | 
					}, {minimize: false});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default mongoose.model('sample', SampleSchema);
 | 
					export default mongoose.model('sample', SampleSchema);
 | 
				
			||||||
@@ -1,583 +0,0 @@
 | 
				
			|||||||
import should from 'should/as-function';
 | 
					 | 
				
			||||||
import ConditionModel from '../models/condition';
 | 
					 | 
				
			||||||
import TestHelper from "../test/helper";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO: adding conditions allowed only for m/a
 | 
					 | 
				
			||||||
// TODO: deleted data only visible for m/a
 | 
					 | 
				
			||||||
// TODO: restore deleted
 | 
					 | 
				
			||||||
// TODO: remove number_prefix
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('/condition', () => {
 | 
					 | 
				
			||||||
  let server;
 | 
					 | 
				
			||||||
  before(done => TestHelper.before(done));
 | 
					 | 
				
			||||||
  beforeEach(done => server = TestHelper.beforeEach(server, done));
 | 
					 | 
				
			||||||
  afterEach(done => TestHelper.afterEach(server, done));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('GET /condition/{id}', () => {
 | 
					 | 
				
			||||||
    it('returns the right condition', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'get',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        res: {_id: '700000000000000000000001', sample_id: '400000000000000000000001', number: 'A1', parameters: {material: 'copper', weeks: 3}, treatment_template: '200000000000000000000001'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('returns the right condition for an API key', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'get',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {key: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        res: {_id: '700000000000000000000001', sample_id: '400000000000000000000001', number: 'A1', parameters: {material: 'copper', weeks: 3}, treatment_template: '200000000000000000000001'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects an invalid id', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'get',
 | 
					 | 
				
			||||||
        url: '/condition/70000000000t000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 404
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects an unknown id', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'get',
 | 
					 | 
				
			||||||
        url: '/condition/000000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 404
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects unauthorized requests', done  => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'get',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        httpStatus: 401
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('PUT /condition{id}', () => {
 | 
					 | 
				
			||||||
    it('returns the right condition', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        req: {},
 | 
					 | 
				
			||||||
        res: {_id: '700000000000000000000001', sample_id: '400000000000000000000001', number: 'A1', treatment_template: '200000000000000000000001', parameters: {material: 'copper', weeks: 3}}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('keeps unchanged properties', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        req: {parameters: {material: 'copper', weeks: 3}}
 | 
					 | 
				
			||||||
      }).end((err, res) => {
 | 
					 | 
				
			||||||
        if (err) return done(err);
 | 
					 | 
				
			||||||
        should(res.body).be.eql({_id: '700000000000000000000001', sample_id: '400000000000000000000001', number: 'A1', treatment_template: '200000000000000000000001', parameters: {material: 'copper', weeks: 3}});
 | 
					 | 
				
			||||||
        ConditionModel.findById('700000000000000000000001').lean().exec((err, data) => {
 | 
					 | 
				
			||||||
          if (err) return done(err);
 | 
					 | 
				
			||||||
          should(data).have.property('status', 10);
 | 
					 | 
				
			||||||
          done();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('keeps only one unchanged parameter', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        req: {parameters: {material: 'copper'}}
 | 
					 | 
				
			||||||
      }).end((err, res) => {
 | 
					 | 
				
			||||||
        if (err) return done(err);
 | 
					 | 
				
			||||||
        should(res.body).be.eql({_id: '700000000000000000000001', sample_id: '400000000000000000000001', number: 'A1', treatment_template: '200000000000000000000001', parameters: {material: 'copper', weeks: 3}});
 | 
					 | 
				
			||||||
        ConditionModel.findById('700000000000000000000001').lean().exec((err, data) => {
 | 
					 | 
				
			||||||
          if (err) return done(err);
 | 
					 | 
				
			||||||
          should(data).have.property('status', 10);
 | 
					 | 
				
			||||||
          done();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('changes the given properties', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        req: {parameters: {material: 'hot air', weeks: 10}}
 | 
					 | 
				
			||||||
      }).end((err, res) => {
 | 
					 | 
				
			||||||
        if (err) return done(err);
 | 
					 | 
				
			||||||
        should(res.body).be.eql({_id: '700000000000000000000001', sample_id: '400000000000000000000001', number: 'A1', treatment_template: '200000000000000000000001', parameters: {material: 'hot air', weeks: 10}});
 | 
					 | 
				
			||||||
        ConditionModel.findById('700000000000000000000001').lean().exec((err, data: any) => {
 | 
					 | 
				
			||||||
          if (err) return done(err);
 | 
					 | 
				
			||||||
          should(data).have.only.keys('_id', 'sample_id', 'number', 'parameters', 'treatment_template', 'status', '__v');
 | 
					 | 
				
			||||||
          should(data.sample_id.toString()).be.eql('400000000000000000000001');
 | 
					 | 
				
			||||||
          should(data).have.property('number', 'A1');
 | 
					 | 
				
			||||||
          should(data.treatment_template.toString()).be.eql('200000000000000000000001');
 | 
					 | 
				
			||||||
          should(data).have.property('status', 0);
 | 
					 | 
				
			||||||
          should(data).have.property('parameters');
 | 
					 | 
				
			||||||
          should(data.parameters).have.property('material', 'hot air');
 | 
					 | 
				
			||||||
          should(data.parameters).have.property('weeks', 10);
 | 
					 | 
				
			||||||
          done();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('allows changing only one parameter', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        req: {parameters: {weeks: 8}},
 | 
					 | 
				
			||||||
        res: {_id: '700000000000000000000001', sample_id: '400000000000000000000001', number: 'A1', treatment_template: '200000000000000000000001', parameters: {material: 'copper', weeks: 8}}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects changing the condition number', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {number: 'C2'},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"number" is not allowed'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects not specified parameters', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {parameters: {xx: 13}},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"xx" is not allowed'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a parameter not in the value range', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {parameters: {material: 'xxx'}},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"material" must be one of [copper, hot air]'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a parameter below minimum range', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {parameters: {weeks: -10}},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"weeks" must be larger than or equal to 1'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a parameter above maximum range', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {parameters: {weeks: 11}},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"weeks" must be less than or equal to 10'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a new treatment_template', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {treatment_template: '200000000000000000000002'},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"treatment_template" is not allowed'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects editing a condition for a write user who did not create this condition', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000003',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 403,
 | 
					 | 
				
			||||||
        req: {parameters: {weeks: 8}}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('accepts editing a condition of another user for a maintain/admin user', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'admin'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        req: {parameters: {material: 'hot air', weeks: 10}},
 | 
					 | 
				
			||||||
        res: {_id: '700000000000000000000001', sample_id: '400000000000000000000001', number: 'A1', treatment_template: '200000000000000000000001', parameters: {material: 'hot air', weeks: 10}}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects an API key', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {key: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 401,
 | 
					 | 
				
			||||||
        req: {parameters: {material: 'hot air', weeks: 10}}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects requests from a read user', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        auth: {basic: 'user'},
 | 
					 | 
				
			||||||
        httpStatus: 403,
 | 
					 | 
				
			||||||
        req: {parameters: {material: 'hot air', weeks: 10}}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects unauthorized requests', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'put',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000001',
 | 
					 | 
				
			||||||
        httpStatus: 401,
 | 
					 | 
				
			||||||
        req: {parameters: {material: 'hot air', weeks: 10}}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('DELETE /condition/{id}', () => {
 | 
					 | 
				
			||||||
    it('sets the status to deleted', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'delete',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000004',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 200
 | 
					 | 
				
			||||||
      }).end((err, res) => {
 | 
					 | 
				
			||||||
        if (err) return done(err);
 | 
					 | 
				
			||||||
        should(res.body).be.eql({status: 'OK'});
 | 
					 | 
				
			||||||
        ConditionModel.findById('700000000000000000000004').lean().exec((err, data: any) => {
 | 
					 | 
				
			||||||
          if (err) return done(err);
 | 
					 | 
				
			||||||
          should(data).have.only.keys('_id', 'sample_id', 'number', 'parameters', 'treatment_template', 'status', '__v');
 | 
					 | 
				
			||||||
          should(data.sample_id.toString()).be.eql('400000000000000000000001');
 | 
					 | 
				
			||||||
          should(data).have.property('number', 'A2');
 | 
					 | 
				
			||||||
          should(data.treatment_template.toString()).be.eql('200000000000000000000001');
 | 
					 | 
				
			||||||
          should(data).have.property('status', -1);
 | 
					 | 
				
			||||||
          should(data).have.property('parameters');
 | 
					 | 
				
			||||||
          should(data.parameters).have.property('material', 'hot air');
 | 
					 | 
				
			||||||
          should(data.parameters).have.property('weeks', 5);
 | 
					 | 
				
			||||||
          done();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects deleting a condition referenced by measurements'/*, done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'delete',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000002',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        res: {status: 'Condition still in use'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    }*/);  // TODO after decision
 | 
					 | 
				
			||||||
    it('rejects an invalid id', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'delete',
 | 
					 | 
				
			||||||
        url: '/condition/70000000000w000000000002',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 404
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects an API key', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'delete',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000004',
 | 
					 | 
				
			||||||
        auth: {key: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 401
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects requests from a read user', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'delete',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000004',
 | 
					 | 
				
			||||||
        auth: {basic: 'user'},
 | 
					 | 
				
			||||||
        httpStatus: 403
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a write user deleting a condition belonging to a sample of another user', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'delete',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000003',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 403
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('accepts an maintain/admin user deleting a condition belonging to a sample of another user', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'delete',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000004',
 | 
					 | 
				
			||||||
        auth: {basic: 'admin'},
 | 
					 | 
				
			||||||
        httpStatus: 200
 | 
					 | 
				
			||||||
      }).end((err, res) => {
 | 
					 | 
				
			||||||
        if (err) return done(err);
 | 
					 | 
				
			||||||
        should(res.body).be.eql({status: 'OK'});
 | 
					 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('returns 404 for an unknown id', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'delete',
 | 
					 | 
				
			||||||
        url: '/condition/000000000000000000000002',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 404
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects unauthorized requests', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'delete',
 | 
					 | 
				
			||||||
        url: '/condition/700000000000000000000004',
 | 
					 | 
				
			||||||
        httpStatus: 401
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('POST /condition/new', () => {
 | 
					 | 
				
			||||||
    it('returns the right condition', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'}
 | 
					 | 
				
			||||||
      }).end((err, res) => {
 | 
					 | 
				
			||||||
        if (err) return done(err);
 | 
					 | 
				
			||||||
        should(res.body).have.only.keys('_id', 'sample_id', 'number', 'parameters', 'treatment_template');
 | 
					 | 
				
			||||||
        should(res.body).have.property('_id').be.type('string');
 | 
					 | 
				
			||||||
        should(res.body).have.property('sample_id', '400000000000000000000002');
 | 
					 | 
				
			||||||
        should(res.body).have.property('number', 'A2');
 | 
					 | 
				
			||||||
        should(res.body).have.property('treatment_template', '200000000000000000000001');
 | 
					 | 
				
			||||||
        should(res.body).have.property('parameters');
 | 
					 | 
				
			||||||
        should(res.body.parameters).have.property('material', 'hot air');
 | 
					 | 
				
			||||||
        should(res.body.parameters).have.property('weeks', 10);
 | 
					 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('stores the condition', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'}
 | 
					 | 
				
			||||||
      }).end((err, res) => {
 | 
					 | 
				
			||||||
        if (err) return done(err);
 | 
					 | 
				
			||||||
        ConditionModel.findById(res.body._id).lean().exec((err, data: any) => {
 | 
					 | 
				
			||||||
          if (err) return done(err);
 | 
					 | 
				
			||||||
          should(data).have.only.keys('_id', 'sample_id', 'number', 'parameters', 'treatment_template', 'status', '__v');
 | 
					 | 
				
			||||||
          should(data.sample_id.toString()).be.eql('400000000000000000000002');
 | 
					 | 
				
			||||||
          should(data).have.property('number', 'A2');
 | 
					 | 
				
			||||||
          should(data.treatment_template.toString()).be.eql('200000000000000000000001');
 | 
					 | 
				
			||||||
          should(data).have.property('status', 0);
 | 
					 | 
				
			||||||
          should(data).have.property('parameters');
 | 
					 | 
				
			||||||
          should(data.parameters).have.property('material', 'hot air');
 | 
					 | 
				
			||||||
          should(data.parameters).have.property('weeks', 10);
 | 
					 | 
				
			||||||
          done();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('stores the first condition as 1', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'admin'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000003', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'}
 | 
					 | 
				
			||||||
      }).end((err, res) => {
 | 
					 | 
				
			||||||
        if (err) return done(err);
 | 
					 | 
				
			||||||
        ConditionModel.findById(res.body._id).lean().exec((err, data: any) => {
 | 
					 | 
				
			||||||
          if (err) return done(err);
 | 
					 | 
				
			||||||
          should(data).have.only.keys('_id', 'sample_id', 'number', 'parameters', 'treatment_template', 'status', '__v');
 | 
					 | 
				
			||||||
          should(data.sample_id.toString()).be.eql('400000000000000000000003');
 | 
					 | 
				
			||||||
          should(data).have.property('number', 'A1');
 | 
					 | 
				
			||||||
          should(data.treatment_template.toString()).be.eql('200000000000000000000001');
 | 
					 | 
				
			||||||
          should(data).have.property('status', 0);
 | 
					 | 
				
			||||||
          should(data).have.property('parameters');
 | 
					 | 
				
			||||||
          should(data.parameters).have.property('material', 'hot air');
 | 
					 | 
				
			||||||
          should(data.parameters).have.property('weeks', 10);
 | 
					 | 
				
			||||||
          done();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects an invalid sample id', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {sample_id: '4000000000h0000000000002', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"sample_id" with value "4000000000h0000000000002" fails to match the required pattern: /[0-9a-f]{24}/'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a sample id not available', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {sample_id: '000000000000000000000002', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'},
 | 
					 | 
				
			||||||
        res: {status: 'Sample id not available'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects an invalid treatment_template id', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000h00000000001'},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"treatment_template" with value "200000000000h00000000001" fails to match the required pattern: /[0-9a-f]{24}/'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a treatment_template which does not exist', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: 10}, treatment_template: '000000000000000000000001'},
 | 
					 | 
				
			||||||
        res: {status: 'Treatment template not available'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects setting a condition number', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000001', number: 'A7', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"number" is not allowed'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects not specified parameters', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: 10, xx: 12}, treatment_template: '200000000000000000000001'},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"xx" is not allowed'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects missing parameters', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air'}, treatment_template: '200000000000000000000001'},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"weeks" is required'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a parameter not in the value range', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'xxx', weeks: 10}, treatment_template: '200000000000000000000001'},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"material" must be one of [copper, hot air]'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a parameter below minimum range', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: -10}, treatment_template: '200000000000000000000001'},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"weeks" must be larger than or equal to 1'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a parameter above maximum range', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: 11}, treatment_template: '200000000000000000000001'},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"weeks" must be less than or equal to 10'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a missing sample id', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"sample_id" is required'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects a missing treatment_template', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 400,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: 10}},
 | 
					 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"treatment_template" is required'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects adding a condition to the sample of an other user for a write user', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 403,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000003', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('accepts adding a condition to the sample of an other user for a maintain/admin user', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'admin'},
 | 
					 | 
				
			||||||
        httpStatus: 200,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'}
 | 
					 | 
				
			||||||
      }).end((err, res) => {
 | 
					 | 
				
			||||||
        if (err) return done(err);
 | 
					 | 
				
			||||||
        should(res.body).have.only.keys('_id', 'sample_id', 'number', 'parameters', 'treatment_template');
 | 
					 | 
				
			||||||
        should(res.body).have.property('_id').be.type('string');
 | 
					 | 
				
			||||||
        should(res.body).have.property('sample_id', '400000000000000000000002');
 | 
					 | 
				
			||||||
        should(res.body).have.property('number', 'A2');
 | 
					 | 
				
			||||||
        should(res.body).have.property('treatment_template', '200000000000000000000001');
 | 
					 | 
				
			||||||
        should(res.body).have.property('parameters');
 | 
					 | 
				
			||||||
        should(res.body.parameters).have.property('material', 'hot air');
 | 
					 | 
				
			||||||
        should(res.body.parameters).have.property('weeks', 10);
 | 
					 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects an API key', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {key: 'janedoe'},
 | 
					 | 
				
			||||||
        httpStatus: 401,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects requests from a read user', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        auth: {basic: 'user'},
 | 
					 | 
				
			||||||
        httpStatus: 403,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it('rejects unauthorized requests', done => {
 | 
					 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
        method: 'post',
 | 
					 | 
				
			||||||
        url: '/condition/new',
 | 
					 | 
				
			||||||
        httpStatus: 401,
 | 
					 | 
				
			||||||
        req: {sample_id: '400000000000000000000002', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'}
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
@@ -1,133 +0,0 @@
 | 
				
			|||||||
import express from 'express';
 | 
					 | 
				
			||||||
import _ from 'lodash';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import ConditionValidate from './validate/condition';
 | 
					 | 
				
			||||||
import ParametersValidate from './validate/parameters';
 | 
					 | 
				
			||||||
import res400 from './validate/res400';
 | 
					 | 
				
			||||||
import SampleModel from '../models/sample';
 | 
					 | 
				
			||||||
import ConditionModel from '../models/condition';
 | 
					 | 
				
			||||||
import TreatmentTemplateModel from '../models/treatment_template';
 | 
					 | 
				
			||||||
import IdValidate from './validate/id';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const router = express.Router();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
router.get('/condition/' + IdValidate.parameter(), (req, res, next) => {
 | 
					 | 
				
			||||||
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ConditionModel.findById(req.params.id).lean().exec((err, data) => {
 | 
					 | 
				
			||||||
    if (err) return next(err);
 | 
					 | 
				
			||||||
    if (data) {
 | 
					 | 
				
			||||||
      res.json(ConditionValidate.output(data));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else {
 | 
					 | 
				
			||||||
      res.status(404).json({status: 'Not found'});
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
router.put('/condition/' + IdValidate.parameter(), async (req, res, next) => {
 | 
					 | 
				
			||||||
  if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const {error, value: condition} = ConditionValidate.input(req.body, 'change');
 | 
					 | 
				
			||||||
  if (error) return res400(error, res);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const data = await ConditionModel.findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
 | 
					 | 
				
			||||||
  if (data instanceof Error) return;
 | 
					 | 
				
			||||||
  if (!data) {
 | 
					 | 
				
			||||||
    res.status(404).json({status: 'Not found'});
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // add properties needed for sampleIdCheck
 | 
					 | 
				
			||||||
  condition.treatment_template = data.treatment_template;
 | 
					 | 
				
			||||||
  condition.sample_id = data.sample_id;
 | 
					 | 
				
			||||||
  if (!await sampleIdCheck(condition, req, res, next)) return;
 | 
					 | 
				
			||||||
  if (condition.parameters) {
 | 
					 | 
				
			||||||
    condition.parameters = _.assign({}, data.parameters, condition.parameters);
 | 
					 | 
				
			||||||
    if (!_.isEqual(condition.parameters, data.parameters)) {  // parameters did not change
 | 
					 | 
				
			||||||
      condition.status = 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  if (!await treatmentCheck(condition, 'change', res, next)) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  await ConditionModel.findByIdAndUpdate(req.params.id, condition, {new: true}).lean().exec((err, data) => {
 | 
					 | 
				
			||||||
    if (err) return next(err);
 | 
					 | 
				
			||||||
    res.json(ConditionValidate.output(data));
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
router.delete('/condition/' + IdValidate.parameter(), (req, res, next) => {
 | 
					 | 
				
			||||||
  if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ConditionModel.findById(req.params.id).lean().exec(async (err, data: any) => {
 | 
					 | 
				
			||||||
    if (err) return next(err);
 | 
					 | 
				
			||||||
    if (!data) {
 | 
					 | 
				
			||||||
      res.status(404).json({status: 'Not found'});
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!await sampleIdCheck(data, req, res, next)) return;
 | 
					 | 
				
			||||||
    await ConditionModel.findByIdAndUpdate(req.params.id, {status: -1}).lean().exec(err => {
 | 
					 | 
				
			||||||
      if (err) return next(err);
 | 
					 | 
				
			||||||
      res.json({status: 'OK'});
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
router.post('/condition/new', async (req, res, next) => {
 | 
					 | 
				
			||||||
  if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const {error, value: condition} = ConditionValidate.input(req.body, 'new');
 | 
					 | 
				
			||||||
  if (error) return res400(error, res);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!await sampleIdCheck(condition, req, res, next)) return;
 | 
					 | 
				
			||||||
  const treatmentData = await treatmentCheck(condition, 'new', res, next)
 | 
					 | 
				
			||||||
  if (!treatmentData) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  condition.number = await numberGenerate(condition, treatmentData, next);
 | 
					 | 
				
			||||||
  if (!condition.number) return;
 | 
					 | 
				
			||||||
  condition.status = 0;  // set status to new
 | 
					 | 
				
			||||||
  await new ConditionModel(condition).save((err, data) => {
 | 
					 | 
				
			||||||
    if (err) return next(err);
 | 
					 | 
				
			||||||
    res.json(ConditionValidate.output(data.toObject()));
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = router;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function sampleIdCheck (condition, req, res, next) {  // validate sample_id, returns false if invalid
 | 
					 | 
				
			||||||
  const sampleData = await SampleModel.findById(condition.sample_id).lean().exec().catch(err => {next(err); return false;}) as any;
 | 
					 | 
				
			||||||
  if (!sampleData) {  // sample_id not found
 | 
					 | 
				
			||||||
    res.status(400).json({status: 'Sample id not available'});
 | 
					 | 
				
			||||||
    return false
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['maintain', 'admin'], 'basic')) return false;  // sample does not belong to user
 | 
					 | 
				
			||||||
  return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function numberGenerate (condition, treatmentData, next) {  // generate number, returns false on error
 | 
					 | 
				
			||||||
  const conditionData = await ConditionModel  // find condition with highest number belonging to the same sample
 | 
					 | 
				
			||||||
    .find({sample_id: condition.sample_id, number: new RegExp('^' + treatmentData.number_prefix + '[0-9]+$', 'm')})
 | 
					 | 
				
			||||||
    .sort({number: -1})
 | 
					 | 
				
			||||||
    .limit(1)
 | 
					 | 
				
			||||||
    .lean()
 | 
					 | 
				
			||||||
    .exec()
 | 
					 | 
				
			||||||
    .catch(err => next(err)) as any;
 | 
					 | 
				
			||||||
  if (conditionData instanceof Error) return false;
 | 
					 | 
				
			||||||
  return treatmentData.number_prefix + (conditionData.length > 0 ? Number(conditionData[0].number.replace(/[^0-9]+/g, '')) + 1 : 1);  // return new number
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function treatmentCheck (condition, param, res, next) {  // validate treatment template, returns false if invalid, otherwise template data
 | 
					 | 
				
			||||||
  const treatmentData = await TreatmentTemplateModel.findById(condition.treatment_template).lean().exec().catch(err => next(err)) as any;
 | 
					 | 
				
			||||||
  if (treatmentData instanceof Error) return false;
 | 
					 | 
				
			||||||
  if (!treatmentData) {  // template not found
 | 
					 | 
				
			||||||
    res.status(400).json({status: 'Treatment template not available'});
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // validate parameters
 | 
					 | 
				
			||||||
  const {error, value: ignore} = ParametersValidate.input(condition.parameters, treatmentData.parameters, param);
 | 
					 | 
				
			||||||
  if (error) {res400(error, res); return false;}
 | 
					 | 
				
			||||||
  return treatmentData;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -2,6 +2,7 @@ import should from 'should/as-function';
 | 
				
			|||||||
import _ from 'lodash';
 | 
					import _ from 'lodash';
 | 
				
			||||||
import MaterialModel from '../models/material';
 | 
					import MaterialModel from '../models/material';
 | 
				
			||||||
import TestHelper from "../test/helper";
 | 
					import TestHelper from "../test/helper";
 | 
				
			||||||
 | 
					import globals from '../globals';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: color name must be unique to get color number
 | 
					// TODO: color name must be unique to get color number
 | 
				
			||||||
// TODO: separate supplier/ material name into own collections
 | 
					// TODO: separate supplier/ material name into own collections
 | 
				
			||||||
@@ -22,7 +23,7 @@ describe('/material', () => {
 | 
				
			|||||||
      }).end((err, res) => {
 | 
					      }).end((err, res) => {
 | 
				
			||||||
        if (err) return done(err);
 | 
					        if (err) return done(err);
 | 
				
			||||||
        const json = require('../test/db.json');
 | 
					        const json = require('../test/db.json');
 | 
				
			||||||
        should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 10).length);
 | 
					        should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === globals.status.validated).length);
 | 
				
			||||||
        should(res.body).matchEach(material => {
 | 
					        should(res.body).matchEach(material => {
 | 
				
			||||||
          should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
 | 
					          should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
 | 
				
			||||||
          should(material).have.property('_id').be.type('string');
 | 
					          should(material).have.property('_id').be.type('string');
 | 
				
			||||||
@@ -50,7 +51,7 @@ describe('/material', () => {
 | 
				
			|||||||
      }).end((err, res) => {
 | 
					      }).end((err, res) => {
 | 
				
			||||||
        if (err) return done(err);
 | 
					        if (err) return done(err);
 | 
				
			||||||
        const json = require('../test/db.json');
 | 
					        const json = require('../test/db.json');
 | 
				
			||||||
        should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 10).length);
 | 
					        should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === globals.status.validated).length);
 | 
				
			||||||
        should(res.body).matchEach(material => {
 | 
					        should(res.body).matchEach(material => {
 | 
				
			||||||
          should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
 | 
					          should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
 | 
				
			||||||
          should(material).have.property('_id').be.type('string');
 | 
					          should(material).have.property('_id').be.type('string');
 | 
				
			||||||
@@ -89,7 +90,7 @@ describe('/material', () => {
 | 
				
			|||||||
        if (err) return done(err);
 | 
					        if (err) return done(err);
 | 
				
			||||||
        const json = require('../test/db.json');
 | 
					        const json = require('../test/db.json');
 | 
				
			||||||
        let asyncCounter = res.body.length;
 | 
					        let asyncCounter = res.body.length;
 | 
				
			||||||
        should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 0).length);
 | 
					        should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status ===globals.status.new).length);
 | 
				
			||||||
        should(res.body).matchEach(material => {
 | 
					        should(res.body).matchEach(material => {
 | 
				
			||||||
          should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
 | 
					          should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
 | 
				
			||||||
          should(material).have.property('_id').be.type('string');
 | 
					          should(material).have.property('_id').be.type('string');
 | 
				
			||||||
@@ -105,7 +106,7 @@ describe('/material', () => {
 | 
				
			|||||||
            should(number).have.property('number').be.type('string');
 | 
					            should(number).have.property('number').be.type('string');
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
          MaterialModel.findById(material._id).lean().exec((err, data) => {
 | 
					          MaterialModel.findById(material._id).lean().exec((err, data) => {
 | 
				
			||||||
            should(data).have.property('status', 0);
 | 
					            should(data).have.property('status',globals.status.new);
 | 
				
			||||||
            if (--asyncCounter === 0) {
 | 
					            if (--asyncCounter === 0) {
 | 
				
			||||||
              done();
 | 
					              done();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -123,7 +124,7 @@ describe('/material', () => {
 | 
				
			|||||||
        if (err) return done(err);
 | 
					        if (err) return done(err);
 | 
				
			||||||
        const json = require('../test/db.json');
 | 
					        const json = require('../test/db.json');
 | 
				
			||||||
        let asyncCounter = res.body.length;
 | 
					        let asyncCounter = res.body.length;
 | 
				
			||||||
        should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === -1).length);
 | 
					        should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status ===globals.status.deleted).length);
 | 
				
			||||||
        should(res.body).matchEach(material => {
 | 
					        should(res.body).matchEach(material => {
 | 
				
			||||||
          should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
 | 
					          should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
 | 
				
			||||||
          should(material).have.property('_id').be.type('string');
 | 
					          should(material).have.property('_id').be.type('string');
 | 
				
			||||||
@@ -139,7 +140,7 @@ describe('/material', () => {
 | 
				
			|||||||
            should(number).have.property('number').be.type('string');
 | 
					            should(number).have.property('number').be.type('string');
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
          MaterialModel.findById(material._id).lean().exec((err, data) => {
 | 
					          MaterialModel.findById(material._id).lean().exec((err, data) => {
 | 
				
			||||||
            should(data).have.property('status', -1);
 | 
					            should(data).have.property('status',globals.status.deleted);
 | 
				
			||||||
            if (--asyncCounter === 0) {
 | 
					            if (--asyncCounter === 0) {
 | 
				
			||||||
              done();
 | 
					              done();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -249,7 +250,7 @@ describe('/material', () => {
 | 
				
			|||||||
        should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', mineral: 0, glass_fiber: 40, carbon_fiber: 0, numbers: [{color: 'black', number: '5514263423'}, {color: 'natural', number: '5514263422'}]});
 | 
					        should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', mineral: 0, glass_fiber: 40, carbon_fiber: 0, numbers: [{color: 'black', number: '5514263423'}, {color: 'natural', number: '5514263422'}]});
 | 
				
			||||||
        MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
 | 
					        MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(data).have.property('status', 10);
 | 
					          should(data).have.property('status',globals.status.validated);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@@ -266,7 +267,7 @@ describe('/material', () => {
 | 
				
			|||||||
        should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', mineral: 0, glass_fiber: 40, carbon_fiber: 0, numbers: [{color: 'black', number: '5514263423'}, {color: 'natural', number: '5514263422'}]});
 | 
					        should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', mineral: 0, glass_fiber: 40, carbon_fiber: 0, numbers: [{color: 'black', number: '5514263423'}, {color: 'natural', number: '5514263422'}]});
 | 
				
			||||||
        MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
 | 
					        MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(data).have.property('status', 10);
 | 
					          should(data).have.property('status',globals.status.validated);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@@ -513,7 +514,7 @@ describe('/material', () => {
 | 
				
			|||||||
          should(data[0]).have.property('mineral', '0');
 | 
					          should(data[0]).have.property('mineral', '0');
 | 
				
			||||||
          should(data[0]).have.property('glass_fiber', '30');
 | 
					          should(data[0]).have.property('glass_fiber', '30');
 | 
				
			||||||
          should(data[0]).have.property('carbon_fiber', '0');
 | 
					          should(data[0]).have.property('carbon_fiber', '0');
 | 
				
			||||||
          should(data[0]).have.property('status', 0);
 | 
					          should(data[0]).have.property('status',globals.status.new);
 | 
				
			||||||
          should(data[0].numbers).have.lengthOf(0);
 | 
					          should(data[0].numbers).have.lengthOf(0);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -552,7 +553,7 @@ describe('/material', () => {
 | 
				
			|||||||
          should(data[0]).have.property('mineral', '0');
 | 
					          should(data[0]).have.property('mineral', '0');
 | 
				
			||||||
          should(data[0]).have.property('glass_fiber', '30');
 | 
					          should(data[0]).have.property('glass_fiber', '30');
 | 
				
			||||||
          should(data[0]).have.property('carbon_fiber', '0');
 | 
					          should(data[0]).have.property('carbon_fiber', '0');
 | 
				
			||||||
          should(data[0]).have.property('status', 0);
 | 
					          should(data[0]).have.property('status',globals.status.new);
 | 
				
			||||||
          should(_.omit(data[0].numbers[0], '_id')).be.eql({color: 'black', number: ''});
 | 
					          should(_.omit(data[0].numbers[0], '_id')).be.eql({color: 'black', number: ''});
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ import SampleModel from '../models/sample';
 | 
				
			|||||||
import IdValidate from './validate/id';
 | 
					import IdValidate from './validate/id';
 | 
				
			||||||
import res400 from './validate/res400';
 | 
					import res400 from './validate/res400';
 | 
				
			||||||
import mongoose from 'mongoose';
 | 
					import mongoose from 'mongoose';
 | 
				
			||||||
 | 
					import globals from '../globals';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,7 +16,7 @@ const router = express.Router();
 | 
				
			|||||||
router.get('/materials', (req, res, next) => {
 | 
					router.get('/materials', (req, res, next) => {
 | 
				
			||||||
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
 | 
					  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  MaterialModel.find({status: 10}).lean().exec((err, data) => {
 | 
					  MaterialModel.find({status:globals.status.validated}).lean().exec((err, data) => {
 | 
				
			||||||
    if (err) return next(err);
 | 
					    if (err) return next(err);
 | 
				
			||||||
    res.json(_.compact(data.map(e => MaterialValidate.output(e))));  // validate all and filter null values from validation errors
 | 
					    res.json(_.compact(data.map(e => MaterialValidate.output(e))));  // validate all and filter null values from validation errors
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
@@ -24,14 +25,7 @@ router.get('/materials', (req, res, next) => {
 | 
				
			|||||||
router.get('/materials/:group(new|deleted)', (req, res, next) => {
 | 
					router.get('/materials/:group(new|deleted)', (req, res, next) => {
 | 
				
			||||||
  if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
 | 
					  if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let status;
 | 
					  MaterialModel.find({status: globals.status[req.params.group]}).lean().exec((err, data) => {
 | 
				
			||||||
  switch (req.params.group) {
 | 
					 | 
				
			||||||
    case 'new': status = 0;
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case 'deleted': status = -1;
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  MaterialModel.find({status: status}).lean().exec((err, data) => {
 | 
					 | 
				
			||||||
    if (err) return next(err);
 | 
					    if (err) return next(err);
 | 
				
			||||||
    res.json(_.compact(data.map(e => MaterialValidate.output(e))));  // validate all and filter null values from validation errors
 | 
					    res.json(_.compact(data.map(e => MaterialValidate.output(e))));  // validate all and filter null values from validation errors
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
@@ -67,7 +61,7 @@ router.put('/material/' + IdValidate.parameter(), (req, res, next) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // check for changes
 | 
					    // check for changes
 | 
				
			||||||
    if (!_.isEqual(_.pick(IdValidate.stringify(materialData), _.keys(material)), material)) {
 | 
					    if (!_.isEqual(_.pick(IdValidate.stringify(materialData), _.keys(material)), material)) {
 | 
				
			||||||
      material.status = 0;  // set status to new
 | 
					      material.status = globals.status.new;  // set status to new
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await MaterialModel.findByIdAndUpdate(req.params.id, material, {new: true}).lean().exec((err, data) => {
 | 
					    await MaterialModel.findByIdAndUpdate(req.params.id, material, {new: true}).lean().exec((err, data) => {
 | 
				
			||||||
@@ -86,7 +80,7 @@ router.delete('/material/' + IdValidate.parameter(), (req, res, next) => {
 | 
				
			|||||||
    if (data.length) {
 | 
					    if (data.length) {
 | 
				
			||||||
      return res.status(400).json({status: 'Material still in use'});
 | 
					      return res.status(400).json({status: 'Material still in use'});
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    MaterialModel.findByIdAndUpdate(req.params.id, {status: -1}).lean().exec((err, data) => {
 | 
					    MaterialModel.findByIdAndUpdate(req.params.id, {status:globals.status.deleted}).lean().exec((err, data) => {
 | 
				
			||||||
      if (err) return next(err);
 | 
					      if (err) return next(err);
 | 
				
			||||||
      if (data) {
 | 
					      if (data) {
 | 
				
			||||||
        res.json({status: 'OK'});
 | 
					        res.json({status: 'OK'});
 | 
				
			||||||
@@ -106,7 +100,7 @@ router.post('/material/new', async (req, res, next) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if (!await nameCheck(material, res, next)) return;
 | 
					  if (!await nameCheck(material, res, next)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  material.status = 0;  // set status to new
 | 
					  material.status = globals.status.new;  // set status to new
 | 
				
			||||||
  await new MaterialModel(material).save((err, data) => {
 | 
					  await new MaterialModel(material).save((err, data) => {
 | 
				
			||||||
    if (err) return next(err);
 | 
					    if (err) return next(err);
 | 
				
			||||||
    res.json(MaterialValidate.output(data.toObject()));
 | 
					    res.json(MaterialValidate.output(data.toObject()));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import should from 'should/as-function';
 | 
					import should from 'should/as-function';
 | 
				
			||||||
import MeasurementModel from '../models/measurement';
 | 
					import MeasurementModel from '../models/measurement';
 | 
				
			||||||
import TestHelper from "../test/helper";
 | 
					import TestHelper from "../test/helper";
 | 
				
			||||||
 | 
					import globals from '../globals';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: allow empty values
 | 
					// TODO: allow empty values
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -78,7 +79,7 @@ describe('/measurement', () => {
 | 
				
			|||||||
        should(res.body).be.eql({_id: '800000000000000000000001', condition_id: '700000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]}, measurement_template: '300000000000000000000001'});
 | 
					        should(res.body).be.eql({_id: '800000000000000000000001', condition_id: '700000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]}, measurement_template: '300000000000000000000001'});
 | 
				
			||||||
        MeasurementModel.findById('800000000000000000000001').lean().exec((err, data: any) => {
 | 
					        MeasurementModel.findById('800000000000000000000001').lean().exec((err, data: any) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(data).have.property('status', 10);
 | 
					          should(data).have.property('status',globals.status.validated);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@@ -95,7 +96,7 @@ describe('/measurement', () => {
 | 
				
			|||||||
        should(res.body).be.eql({_id: '800000000000000000000002', condition_id: '700000000000000000000002', values: {'weight %': 0.5, 'standard deviation': 0.2}, measurement_template: '300000000000000000000002'});
 | 
					        should(res.body).be.eql({_id: '800000000000000000000002', condition_id: '700000000000000000000002', values: {'weight %': 0.5, 'standard deviation': 0.2}, measurement_template: '300000000000000000000002'});
 | 
				
			||||||
        MeasurementModel.findById('800000000000000000000002').lean().exec((err, data: any) => {
 | 
					        MeasurementModel.findById('800000000000000000000002').lean().exec((err, data: any) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(data).have.property('status', 10);
 | 
					          should(data).have.property('status',globals.status.validated);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@@ -114,7 +115,7 @@ describe('/measurement', () => {
 | 
				
			|||||||
          should(data).have.only.keys('_id', 'condition_id', 'values', 'measurement_template', 'status', '__v');
 | 
					          should(data).have.only.keys('_id', 'condition_id', 'values', 'measurement_template', 'status', '__v');
 | 
				
			||||||
          should(data.condition_id.toString()).be.eql('700000000000000000000001');
 | 
					          should(data.condition_id.toString()).be.eql('700000000000000000000001');
 | 
				
			||||||
          should(data.measurement_template.toString()).be.eql('300000000000000000000001');
 | 
					          should(data.measurement_template.toString()).be.eql('300000000000000000000001');
 | 
				
			||||||
          should(data).have.property('status', 0);
 | 
					          should(data).have.property('status',globals.status.new);
 | 
				
			||||||
          should(data).have.property('values');
 | 
					          should(data).have.property('values');
 | 
				
			||||||
          should(data.values).have.property('dpt', [[1,2],[3,4],[5,6]]);
 | 
					          should(data.values).have.property('dpt', [[1,2],[3,4],[5,6]]);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
@@ -256,7 +257,7 @@ describe('/measurement', () => {
 | 
				
			|||||||
        should(res.body).be.eql({status: 'OK'});
 | 
					        should(res.body).be.eql({status: 'OK'});
 | 
				
			||||||
        MeasurementModel.findById('800000000000000000000001').lean().exec((err, data) => {
 | 
					        MeasurementModel.findById('800000000000000000000001').lean().exec((err, data) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(data).have.property('status', -1);
 | 
					          should(data).have.property('status',globals.status.deleted);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,12 +2,12 @@ import express from 'express';
 | 
				
			|||||||
import _ from 'lodash';
 | 
					import _ from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import MeasurementModel from '../models/measurement';
 | 
					import MeasurementModel from '../models/measurement';
 | 
				
			||||||
import ConditionModel from '../models/condition';
 | 
					 | 
				
			||||||
import MeasurementTemplateModel from '../models/measurement_template';
 | 
					import MeasurementTemplateModel from '../models/measurement_template';
 | 
				
			||||||
import MeasurementValidate from './validate/measurement';
 | 
					import MeasurementValidate from './validate/measurement';
 | 
				
			||||||
import IdValidate from './validate/id';
 | 
					import IdValidate from './validate/id';
 | 
				
			||||||
import res400 from './validate/res400';
 | 
					import res400 from './validate/res400';
 | 
				
			||||||
import ParametersValidate from './validate/parameters';
 | 
					import ParametersValidate from './validate/parameters';
 | 
				
			||||||
 | 
					import globals from '../globals';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const router = express.Router();
 | 
					const router = express.Router();
 | 
				
			||||||
@@ -46,7 +46,7 @@ router.put('/measurement/' + IdValidate.parameter(), async (req, res, next) => {
 | 
				
			|||||||
  if (measurement.values) {
 | 
					  if (measurement.values) {
 | 
				
			||||||
    measurement.values = _.assign({}, data.values, measurement.values);
 | 
					    measurement.values = _.assign({}, data.values, measurement.values);
 | 
				
			||||||
    if (!_.isEqual(measurement.values, data.values)) {
 | 
					    if (!_.isEqual(measurement.values, data.values)) {
 | 
				
			||||||
      measurement.status = 0;  // set status to new
 | 
					      measurement.status = globals.status.new;  // set status to new
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -66,7 +66,7 @@ router.delete('/measurement/' + IdValidate.parameter(), (req, res, next) => {
 | 
				
			|||||||
      res.status(404).json({status: 'Not found'});
 | 
					      res.status(404).json({status: 'Not found'});
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (!await conditionIdCheck(data, req, res, next)) return;
 | 
					    if (!await conditionIdCheck(data, req, res, next)) return;
 | 
				
			||||||
    await MeasurementModel.findByIdAndUpdate(req.params.id, {status: -1}).lean().exec(err => {
 | 
					    await MeasurementModel.findByIdAndUpdate(req.params.id, {status:globals.status.deleted}).lean().exec(err => {
 | 
				
			||||||
      if (err) return next(err);
 | 
					      if (err) return next(err);
 | 
				
			||||||
      res.json({status: 'OK'});
 | 
					      res.json({status: 'OK'});
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -93,14 +93,14 @@ router.post('/measurement/new', async (req, res, next) => {
 | 
				
			|||||||
module.exports = router;
 | 
					module.exports = router;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function conditionIdCheck (measurement, req, res, next) {  // validate condition_id, returns false if invalid
 | 
					async function conditionIdCheck (measurement, req, res, next) {  // validate condition_id, returns false if invalid  // TODO
 | 
				
			||||||
  const sampleData = await ConditionModel.findById(measurement.condition_id).populate('sample_id').lean().exec().catch(err => {next(err); return false;}) as any;
 | 
					  // const sampleData = await ConditionModel.findById(measurement.condition_id).populate('sample_id').lean().exec().catch(err => {next(err); return false;}) as any;
 | 
				
			||||||
  if (!sampleData) {  // sample_id not found
 | 
					  // if (!sampleData) {  // sample_id not found
 | 
				
			||||||
    res.status(400).json({status: 'Condition id not available'});
 | 
					  //   res.status(400).json({status: 'Condition id not available'});
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  // }
 | 
				
			||||||
  if (sampleData.sample_id.user_id.toString() !== req.authDetails.id && !req.auth(res, ['maintain', 'admin'], 'basic')) return false;  // sample does not belong to user
 | 
					  // if (sampleData.sample_id.user_id.toString() !== req.authDetails.id && !req.auth(res, ['maintain', 'admin'], 'basic')) return false;  // sample does not belong to user
 | 
				
			||||||
  return true;
 | 
					  // return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function templateCheck (measurement, param, res, next) {  // validate measurement_template and values, param for new/change
 | 
					async function templateCheck (measurement, param, res, next) {  // validate measurement_template and values, param for new/change
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,10 +3,16 @@ import SampleModel from '../models/sample';
 | 
				
			|||||||
import NoteModel from '../models/note';
 | 
					import NoteModel from '../models/note';
 | 
				
			||||||
import NoteFieldModel from '../models/note_field';
 | 
					import NoteFieldModel from '../models/note_field';
 | 
				
			||||||
import TestHelper from "../test/helper";
 | 
					import TestHelper from "../test/helper";
 | 
				
			||||||
 | 
					import globals from '../globals';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: generate output for ML in format DPT -> data, implement filtering, field selection
 | 
					// TODO: generate output for ML in format DPT -> data, implement filtering, field selection
 | 
				
			||||||
 | 
					// TODO: filter by not completely filled/no measurements
 | 
				
			||||||
// TODO: write script for data import
 | 
					// TODO: write script for data import
 | 
				
			||||||
// TODO: delete everything (measurements, condition) with sample
 | 
					// TODO: delete everything (measurements, condition) with sample
 | 
				
			||||||
 | 
					// TODO: allow adding sample numbers for existing samples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Do not allow validation or measurement entry without condition
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('/sample', () => {
 | 
					describe('/sample', () => {
 | 
				
			||||||
  let server;
 | 
					  let server;
 | 
				
			||||||
@@ -24,14 +30,16 @@ describe('/sample', () => {
 | 
				
			|||||||
      }).end((err, res) => {
 | 
					      }).end((err, res) => {
 | 
				
			||||||
        if (err) return done(err);
 | 
					        if (err) return done(err);
 | 
				
			||||||
        const json = require('../test/db.json');
 | 
					        const json = require('../test/db.json');
 | 
				
			||||||
        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === 10).length);
 | 
					        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.validated).length);
 | 
				
			||||||
        should(res.body).matchEach(sample => {
 | 
					        should(res.body).matchEach(sample => {
 | 
				
			||||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id');
 | 
					          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
				
			||||||
          should(sample).have.property('_id').be.type('string');
 | 
					          should(sample).have.property('_id').be.type('string');
 | 
				
			||||||
          should(sample).have.property('number').be.type('string');
 | 
					          should(sample).have.property('number').be.type('string');
 | 
				
			||||||
          should(sample).have.property('type').be.type('string');
 | 
					          should(sample).have.property('type').be.type('string');
 | 
				
			||||||
          should(sample).have.property('color').be.type('string');
 | 
					          should(sample).have.property('color').be.type('string');
 | 
				
			||||||
          should(sample).have.property('batch').be.type('string');
 | 
					          should(sample).have.property('batch').be.type('string');
 | 
				
			||||||
 | 
					          should(sample).have.property('condition').be.type('object');
 | 
				
			||||||
 | 
					          should(sample.condition).have.property('condition_template').be.type('string');
 | 
				
			||||||
          should(sample).have.property('material_id').be.type('string');
 | 
					          should(sample).have.property('material_id').be.type('string');
 | 
				
			||||||
          should(sample).have.property('note_id');
 | 
					          should(sample).have.property('note_id');
 | 
				
			||||||
          should(sample).have.property('user_id').be.type('string');
 | 
					          should(sample).have.property('user_id').be.type('string');
 | 
				
			||||||
@@ -48,17 +56,19 @@ describe('/sample', () => {
 | 
				
			|||||||
      }).end((err, res) => {
 | 
					      }).end((err, res) => {
 | 
				
			||||||
        if (err) return done(err);
 | 
					        if (err) return done(err);
 | 
				
			||||||
        const json = require('../test/db.json');
 | 
					        const json = require('../test/db.json');
 | 
				
			||||||
        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === 10).length);
 | 
					        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.validated).length);
 | 
				
			||||||
        should(res.body).matchEach(material => {
 | 
					        should(res.body).matchEach(sample => {
 | 
				
			||||||
          should(material).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id');
 | 
					          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
				
			||||||
          should(material).have.property('_id').be.type('string');
 | 
					          should(sample).have.property('_id').be.type('string');
 | 
				
			||||||
          should(material).have.property('number').be.type('string');
 | 
					          should(sample).have.property('number').be.type('string');
 | 
				
			||||||
          should(material).have.property('type').be.type('string');
 | 
					          should(sample).have.property('type').be.type('string');
 | 
				
			||||||
          should(material).have.property('color').be.type('string');
 | 
					          should(sample).have.property('color').be.type('string');
 | 
				
			||||||
          should(material).have.property('batch').be.type('string');
 | 
					          should(sample).have.property('batch').be.type('string');
 | 
				
			||||||
          should(material).have.property('material_id').be.type('string');
 | 
					          should(sample).have.property('condition').be.type('object');
 | 
				
			||||||
          should(material).have.property('note_id');
 | 
					          should(sample.condition).have.property('condition_template').be.type('string');
 | 
				
			||||||
          should(material).have.property('user_id').be.type('string');
 | 
					          should(sample).have.property('material_id').be.type('string');
 | 
				
			||||||
 | 
					          should(sample).have.property('note_id');
 | 
				
			||||||
 | 
					          should(sample).have.property('user_id').be.type('string');
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        done();
 | 
					        done();
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@@ -83,25 +93,28 @@ describe('/sample', () => {
 | 
				
			|||||||
        if (err) return done(err);
 | 
					        if (err) return done(err);
 | 
				
			||||||
        const json = require('../test/db.json');
 | 
					        const json = require('../test/db.json');
 | 
				
			||||||
        let asyncCounter = res.body.length;
 | 
					        let asyncCounter = res.body.length;
 | 
				
			||||||
        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === 0).length);
 | 
					        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.new).length);
 | 
				
			||||||
        should(res.body).matchEach(sample => {
 | 
					        should(res.body).matchEach(sample => {
 | 
				
			||||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id');
 | 
					          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
				
			||||||
          should(sample).have.property('_id').be.type('string');
 | 
					          should(sample).have.property('_id').be.type('string');
 | 
				
			||||||
          should(sample).have.property('number').be.type('string');
 | 
					          should(sample).have.property('number').be.type('string');
 | 
				
			||||||
          should(sample).have.property('type').be.type('string');
 | 
					          should(sample).have.property('type').be.type('string');
 | 
				
			||||||
          should(sample).have.property('color').be.type('string');
 | 
					          should(sample).have.property('color').be.type('string');
 | 
				
			||||||
          should(sample).have.property('batch').be.type('string');
 | 
					          should(sample).have.property('batch').be.type('string');
 | 
				
			||||||
 | 
					          should(sample).have.property('condition').be.type('object');
 | 
				
			||||||
 | 
					          if (Object.keys(sample.condition).length > 0) {
 | 
				
			||||||
 | 
					            should(sample.condition).have.property('condition_template').be.type('string');
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
          should(sample).have.property('material_id').be.type('string');
 | 
					          should(sample).have.property('material_id').be.type('string');
 | 
				
			||||||
          should(sample).have.property('note_id');
 | 
					          should(sample).have.property('note_id');
 | 
				
			||||||
          should(sample).have.property('user_id').be.type('string');
 | 
					          should(sample).have.property('user_id').be.type('string');
 | 
				
			||||||
          SampleModel.findById(sample._id).lean().exec((err, data) => {
 | 
					          SampleModel.findById(sample._id).lean().exec((err, data) => {
 | 
				
			||||||
            should(data).have.property('status', 0);
 | 
					            should(data).have.property('status',globals.status.new);
 | 
				
			||||||
            if (--asyncCounter === 0) {
 | 
					            if (--asyncCounter === 0) {
 | 
				
			||||||
              done();
 | 
					              done();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    it('returns all deleted samples', done => {
 | 
					    it('returns all deleted samples', done => {
 | 
				
			||||||
@@ -116,23 +129,26 @@ describe('/sample', () => {
 | 
				
			|||||||
        let asyncCounter = res.body.length;
 | 
					        let asyncCounter = res.body.length;
 | 
				
			||||||
        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === -1).length);
 | 
					        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === -1).length);
 | 
				
			||||||
        should(res.body).matchEach(sample => {
 | 
					        should(res.body).matchEach(sample => {
 | 
				
			||||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id');
 | 
					          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
				
			||||||
          should(sample).have.property('_id').be.type('string');
 | 
					          should(sample).have.property('_id').be.type('string');
 | 
				
			||||||
          should(sample).have.property('number').be.type('string');
 | 
					          should(sample).have.property('number').be.type('string');
 | 
				
			||||||
          should(sample).have.property('type').be.type('string');
 | 
					          should(sample).have.property('type').be.type('string');
 | 
				
			||||||
          should(sample).have.property('color').be.type('string');
 | 
					          should(sample).have.property('color').be.type('string');
 | 
				
			||||||
          should(sample).have.property('batch').be.type('string');
 | 
					          should(sample).have.property('batch').be.type('string');
 | 
				
			||||||
 | 
					          should(sample).have.property('condition').be.type('object');
 | 
				
			||||||
 | 
					          should(sample.condition).have.property('condition_template').be.type('string');
 | 
				
			||||||
 | 
					          should(sample.condition).have.property('condition_template').be.type('string');
 | 
				
			||||||
 | 
					          should(sample.condition).have.property('condition_template').be.type('string');
 | 
				
			||||||
          should(sample).have.property('material_id').be.type('string');
 | 
					          should(sample).have.property('material_id').be.type('string');
 | 
				
			||||||
          should(sample).have.property('note_id');
 | 
					          should(sample).have.property('note_id');
 | 
				
			||||||
          should(sample).have.property('user_id').be.type('string');
 | 
					          should(sample).have.property('user_id').be.type('string');
 | 
				
			||||||
          SampleModel.findById(sample._id).lean().exec((err, data) => {
 | 
					          SampleModel.findById(sample._id).lean().exec((err, data) => {
 | 
				
			||||||
            should(data).have.property('status', -1);
 | 
					            should(data).have.property('status',globals.status.deleted);
 | 
				
			||||||
            if (--asyncCounter === 0) {
 | 
					            if (--asyncCounter === 0) {
 | 
				
			||||||
              done();
 | 
					              done();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    it('rejects requests from a write user', done => {
 | 
					    it('rejects requests from a write user', done => {
 | 
				
			||||||
@@ -160,6 +176,73 @@ describe('/sample', () => {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('GET /sample/{id}', () => {
 | 
				
			||||||
 | 
					    it('returns the right sample', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'get',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000003',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 200,
 | 
				
			||||||
 | 
					        res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, user: 'admin'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('works with an API key', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'get',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000003',
 | 
				
			||||||
 | 
					        auth: {key: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 200,
 | 
				
			||||||
 | 
					        res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, user: 'admin'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('returns a deleted sample for a maintain/admin user', done => {  // TODO: make tests work
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'get',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000005',
 | 
				
			||||||
 | 
					        auth: {basic: 'admin'},
 | 
				
			||||||
 | 
					        httpStatus: 200,
 | 
				
			||||||
 | 
					        res: {_id: '400000000000000000000005', number: 'Rng33', type: 'granulate', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {}, user: 'admin'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('returns 403 for a write user when requesting a deleted sample', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'get',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000005',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 403
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('returns 404 for an unknown sample', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'get',
 | 
				
			||||||
 | 
					        url: '/sample/000000000000000000000005',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 404
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('rejects an invalid id', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'get',
 | 
				
			||||||
 | 
					        url: '/sample/400000000h00000000000005',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 404
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('rejects unauthorized requests', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'get',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000005',
 | 
				
			||||||
 | 
					        httpStatus: 401
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('PUT /sample/{id}', () => {
 | 
					  describe('PUT /sample/{id}', () => {
 | 
				
			||||||
    it('returns the right sample', done => {
 | 
					    it('returns the right sample', done => {
 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
@@ -168,7 +251,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 200,
 | 
					        httpStatus: 200,
 | 
				
			||||||
        req: {},
 | 
					        req: {},
 | 
				
			||||||
        res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}
 | 
					        res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    it('keeps unchanged properties', done => {
 | 
					    it('keeps unchanged properties', done => {
 | 
				
			||||||
@@ -177,21 +260,22 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/400000000000000000000001',
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 200,
 | 
					        httpStatus: 200,
 | 
				
			||||||
        req: {type: 'granulate', color: 'black', batch: '', material_id: '100000000000000000000004', notes: {}}
 | 
					        req: {type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', notes: {}}
 | 
				
			||||||
      }).end((err, res) => {
 | 
					      }).end((err, res) => {
 | 
				
			||||||
        if (err) return done(err);
 | 
					        if (err) return done(err);
 | 
				
			||||||
        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'});
 | 
					        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'});
 | 
				
			||||||
        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
					        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
				
			||||||
          if (err) return done (err);
 | 
					          if (err) return done (err);
 | 
				
			||||||
          should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
					          should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
				
			||||||
          should(data).have.property('_id');
 | 
					          should(data).have.property('_id');
 | 
				
			||||||
          should(data).have.property('number', '1');
 | 
					          should(data).have.property('number', '1');
 | 
				
			||||||
          should(data).have.property('color', 'black');
 | 
					          should(data).have.property('color', 'black');
 | 
				
			||||||
          should(data).have.property('type', 'granulate');
 | 
					          should(data).have.property('type', 'granulate');
 | 
				
			||||||
          should(data).have.property('batch', '');
 | 
					          should(data).have.property('batch', '');
 | 
				
			||||||
 | 
					          should(data).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'});
 | 
				
			||||||
          should(data.material_id.toString()).be.eql('100000000000000000000004');
 | 
					          should(data.material_id.toString()).be.eql('100000000000000000000004');
 | 
				
			||||||
          should(data.user_id.toString()).be.eql('000000000000000000000002');
 | 
					          should(data.user_id.toString()).be.eql('000000000000000000000002');
 | 
				
			||||||
          should(data).have.property('status', 10);
 | 
					          should(data).have.property('status',globals.status.validated);
 | 
				
			||||||
          should(data).have.property('note_id', null);
 | 
					          should(data).have.property('note_id', null);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -206,10 +290,27 @@ describe('/sample', () => {
 | 
				
			|||||||
        req: {type: 'granulate'}
 | 
					        req: {type: 'granulate'}
 | 
				
			||||||
      }).end((err, res) => {
 | 
					      }).end((err, res) => {
 | 
				
			||||||
        if (err) return done(err);
 | 
					        if (err) return done(err);
 | 
				
			||||||
        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'});
 | 
					        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'});
 | 
				
			||||||
        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
					        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
				
			||||||
          if (err) return done (err);
 | 
					          if (err) return done (err);
 | 
				
			||||||
          should(data).have.property('status', 10);
 | 
					          should(data).have.property('status',globals.status.validated);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('keeps an unchanged condition', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'put',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 200,
 | 
				
			||||||
 | 
					        req: {condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}}
 | 
				
			||||||
 | 
					      }).end((err, res) => {
 | 
				
			||||||
 | 
					        if (err) return done(err);
 | 
				
			||||||
 | 
					        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'});
 | 
				
			||||||
 | 
					        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
				
			||||||
 | 
					          if (err) return done (err);
 | 
				
			||||||
 | 
					          should(data).have.property('status',globals.status.validated);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@@ -223,18 +324,21 @@ describe('/sample', () => {
 | 
				
			|||||||
        req: {notes: {comment: 'Stoff gesperrt', sample_references: []}}
 | 
					        req: {notes: {comment: 'Stoff gesperrt', sample_references: []}}
 | 
				
			||||||
      }).end((err, res) => {
 | 
					      }).end((err, res) => {
 | 
				
			||||||
        if (err) return done(err);
 | 
					        if (err) return done(err);
 | 
				
			||||||
        should(res.body).be.eql({_id: '400000000000000000000002', number: '21', type: 'granulate', color: 'natural', batch: '1560237365', material_id: '100000000000000000000001', note_id: '500000000000000000000001', user_id: '000000000000000000000002'});
 | 
					        should(res.body).be.eql({_id: '400000000000000000000002', number: '21', type: 'granulate', color: 'natural', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', note_id: '500000000000000000000001', user_id: '000000000000000000000002'});
 | 
				
			||||||
        SampleModel.findById('400000000000000000000002').lean().exec((err, data: any) => {
 | 
					        SampleModel.findById('400000000000000000000002').lean().exec((err, data: any) => {
 | 
				
			||||||
          if (err) return done (err);
 | 
					          if (err) return done (err);
 | 
				
			||||||
          should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
					          should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
				
			||||||
          should(data).have.property('_id');
 | 
					          should(data).have.property('_id');
 | 
				
			||||||
          should(data).have.property('number', '21');
 | 
					          should(data).have.property('number', '21');
 | 
				
			||||||
          should(data).have.property('color', 'natural');
 | 
					          should(data).have.property('color', 'natural');
 | 
				
			||||||
          should(data).have.property('type', 'granulate');
 | 
					          should(data).have.property('type', 'granulate');
 | 
				
			||||||
          should(data).have.property('batch', '1560237365');
 | 
					          should(data).have.property('batch', '1560237365');
 | 
				
			||||||
 | 
					          should(data.condition).have.property('material', 'copper');
 | 
				
			||||||
 | 
					          should(data.condition).have.property('weeks', 3);
 | 
				
			||||||
 | 
					          should(data.condition.condition_template.toString()).be.eql('200000000000000000000001');
 | 
				
			||||||
          should(data.material_id.toString()).be.eql('100000000000000000000001');
 | 
					          should(data.material_id.toString()).be.eql('100000000000000000000001');
 | 
				
			||||||
          should(data.user_id.toString()).be.eql('000000000000000000000002');
 | 
					          should(data.user_id.toString()).be.eql('000000000000000000000002');
 | 
				
			||||||
          should(data).have.property('status', 10);
 | 
					          should(data).have.property('status',globals.status.validated);
 | 
				
			||||||
          should(data.note_id.toString()).be.eql('500000000000000000000001');
 | 
					          should(data.note_id.toString()).be.eql('500000000000000000000001');
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -246,20 +350,21 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/400000000000000000000001',
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 200,
 | 
					        httpStatus: 200,
 | 
				
			||||||
        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
					        req: {type: 'part', color: 'signalviolet', batch: '114531', condition: {condition_template: '200000000000000000000003'}, material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
				
			||||||
      }).end(err => {
 | 
					      }).end(err => {
 | 
				
			||||||
        if (err) return done (err);
 | 
					        if (err) return done (err);
 | 
				
			||||||
        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
					        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
				
			||||||
          if (err) return done (err);
 | 
					          if (err) return done (err);
 | 
				
			||||||
          should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
					          should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
				
			||||||
          should(data).have.property('_id');
 | 
					          should(data).have.property('_id');
 | 
				
			||||||
          should(data).have.property('number', '1');
 | 
					          should(data).have.property('number', '1');
 | 
				
			||||||
          should(data).have.property('color', 'signalviolet');
 | 
					          should(data).have.property('color', 'signalviolet');
 | 
				
			||||||
          should(data).have.property('type', 'part');
 | 
					          should(data).have.property('type', 'part');
 | 
				
			||||||
          should(data).have.property('batch', '114531');
 | 
					          should(data).have.property('batch', '114531');
 | 
				
			||||||
 | 
					          should(data).have.property('condition', {condition_template: '200000000000000000000003'});
 | 
				
			||||||
          should(data.material_id.toString()).be.eql('100000000000000000000002');
 | 
					          should(data.material_id.toString()).be.eql('100000000000000000000002');
 | 
				
			||||||
          should(data.user_id.toString()).be.eql('000000000000000000000002');
 | 
					          should(data.user_id.toString()).be.eql('000000000000000000000002');
 | 
				
			||||||
          should(data).have.property('status', 0);
 | 
					          should(data).have.property('status',globals.status.new);
 | 
				
			||||||
          should(data).have.property('note_id');
 | 
					          should(data).have.property('note_id');
 | 
				
			||||||
          NoteModel.findById(data.note_id).lean().exec((err, data: any) => {
 | 
					          NoteModel.findById(data.note_id).lean().exec((err, data: any) => {
 | 
				
			||||||
            if (err) return done (err);
 | 
					            if (err) return done (err);
 | 
				
			||||||
@@ -267,7 +372,7 @@ describe('/sample', () => {
 | 
				
			|||||||
            should(data).have.property('comment', 'Testcomment');
 | 
					            should(data).have.property('comment', 'Testcomment');
 | 
				
			||||||
            should(data).have.property('sample_references');
 | 
					            should(data).have.property('sample_references');
 | 
				
			||||||
            should(data.sample_references).have.lengthOf(1);
 | 
					            should(data.sample_references).have.lengthOf(1);
 | 
				
			||||||
            should(data.sample_references[0].id.toString()).be.eql('400000000000000000000003');
 | 
					            should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003');
 | 
				
			||||||
            should(data.sample_references[0]).have.property('relation', 'part to this sample');
 | 
					            should(data.sample_references[0]).have.property('relation', 'part to this sample');
 | 
				
			||||||
            done();
 | 
					            done();
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
@@ -350,7 +455,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/400000000000000000000001',
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Color not available for material'}
 | 
					        res: {status: 'Color not available for material'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -360,7 +465,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/400000000000000000000001',
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '000000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '000000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Material not available'}
 | 
					        res: {status: 'Material not available'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -370,7 +475,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/400000000000000000000001',
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {number: 25, type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {number: 25, type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"number" is not allowed'}
 | 
					        res: {status: 'Invalid body format', details: '"number" is not allowed'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -380,7 +485,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/400000000000000000000001',
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '000000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '000000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Sample reference not available'}
 | 
					        res: {status: 'Sample reference not available'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -390,7 +495,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/400000000000000000000001',
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"material_id" with value "10000000000h000000000001" fails to match the required pattern: /[0-9a-f]{24}/'}
 | 
					        res: {status: 'Invalid body format', details: '"material_id" with value "10000000000h000000000001" fails to match the required pattern: /[0-9a-f]{24}/'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -400,7 +505,87 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/10000000000h000000000001',
 | 
					        url: '/sample/10000000000h000000000001',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 404,
 | 
					        httpStatus: 404,
 | 
				
			||||||
        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects not specified condition parameters', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'put',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {condition: {material: 'copper', weeks: 3, xxx: 44, condition_template: '200000000000000000000001'}},
 | 
				
			||||||
 | 
					        res: {status: 'Invalid body format', details: '"xxx" is not allowed'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects a condition parameter not in the value range', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'put',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {condition: {material: 'xx', weeks: 3, condition_template: '200000000000000000000001'}},
 | 
				
			||||||
 | 
					        res: {status: 'Invalid body format', details: '"material" must be one of [copper, hot air]'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects a condition parameter below minimum range', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'put',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {condition: {material: 'copper', weeks: 0, condition_template: '200000000000000000000001'}},
 | 
				
			||||||
 | 
					        res: {status: 'Invalid body format', details: '"weeks" must be larger than or equal to 1'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects a condition parameter above maximum range', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'put',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {condition: {material: 'copper', weeks: 10.5, condition_template: '200000000000000000000001'}},
 | 
				
			||||||
 | 
					        res: {status: 'Invalid body format', details: '"weeks" must be less than or equal to 10'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects an invalid condition template', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'put',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {condition: {material: 'copper', weeks: 3, condition_template: '200000000000h00000000001'}},
 | 
				
			||||||
 | 
					        res: {status: 'Condition template not available'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects an unknown condition template', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'put',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {condition: {material: 'copper', weeks: 3, condition_template: '000000000000000000000001'}},
 | 
				
			||||||
 | 
					        res: {status: 'Condition template not available'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('allows keeping an empty condition empty', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'put',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000006',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 200,
 | 
				
			||||||
 | 
					        req: {condition: {}},
 | 
				
			||||||
 | 
					        res: {_id: '400000000000000000000006', number: 'Rng36', type: 'granulate', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects an changing back to an empty condition', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'put',
 | 
				
			||||||
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {condition: {}},
 | 
				
			||||||
 | 
					        res: {status: 'Condition template not available'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    it('rejects an API key', done => {
 | 
					    it('rejects an API key', done => {
 | 
				
			||||||
@@ -409,7 +594,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/400000000000000000000001',
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
        auth: {key: 'janedoe'},
 | 
					        auth: {key: 'janedoe'},
 | 
				
			||||||
        httpStatus: 401,
 | 
					        httpStatus: 401,
 | 
				
			||||||
        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    it('rejects changes for samples from another user for a write user', done => {
 | 
					    it('rejects changes for samples from another user for a write user', done => {
 | 
				
			||||||
@@ -428,7 +613,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        auth: {basic: 'admin'},
 | 
					        auth: {basic: 'admin'},
 | 
				
			||||||
        httpStatus: 200,
 | 
					        httpStatus: 200,
 | 
				
			||||||
        req: {},
 | 
					        req: {},
 | 
				
			||||||
        res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}
 | 
					        res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {condition_template: '200000000000000000000001', material: 'copper', weeks: 3}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    it('rejects requests from a read user', done => {
 | 
					    it('rejects requests from a read user', done => {
 | 
				
			||||||
@@ -437,7 +622,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/400000000000000000000001',
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
        auth: {basic: 'user'},
 | 
					        auth: {basic: 'user'},
 | 
				
			||||||
        httpStatus: 403,
 | 
					        httpStatus: 403,
 | 
				
			||||||
        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    it('returns 404 for an unknown sample', done => {
 | 
					    it('returns 404 for an unknown sample', done => {
 | 
				
			||||||
@@ -446,7 +631,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/000000000000000000000001',
 | 
					        url: '/sample/000000000000000000000001',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 404,
 | 
					        httpStatus: 404,
 | 
				
			||||||
        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
					        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    it('rejects unauthorized requests', done => {
 | 
					    it('rejects unauthorized requests', done => {
 | 
				
			||||||
@@ -454,7 +639,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        method: 'put',
 | 
					        method: 'put',
 | 
				
			||||||
        url: '/sample/400000000000000000000001',
 | 
					        url: '/sample/400000000000000000000001',
 | 
				
			||||||
        httpStatus: 401,
 | 
					        httpStatus: 401,
 | 
				
			||||||
        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
@@ -471,15 +656,18 @@ describe('/sample', () => {
 | 
				
			|||||||
        should(res.body).be.eql({status: 'OK'});
 | 
					        should(res.body).be.eql({status: 'OK'});
 | 
				
			||||||
        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
					        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
					          should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
				
			||||||
          should(data).have.property('_id');
 | 
					          should(data).have.property('_id');
 | 
				
			||||||
          should(data).have.property('number', '1');
 | 
					          should(data).have.property('number', '1');
 | 
				
			||||||
          should(data).have.property('color', 'black');
 | 
					          should(data).have.property('color', 'black');
 | 
				
			||||||
          should(data).have.property('type', 'granulate');
 | 
					          should(data).have.property('type', 'granulate');
 | 
				
			||||||
          should(data).have.property('batch', '');
 | 
					          should(data).have.property('batch', '');
 | 
				
			||||||
 | 
					          should(data.condition).have.property('material', 'copper');
 | 
				
			||||||
 | 
					          should(data.condition).have.property('weeks', 3);
 | 
				
			||||||
 | 
					          should(data.condition.condition_template.toString()).be.eql('200000000000000000000001');
 | 
				
			||||||
          should(data.material_id.toString()).be.eql('100000000000000000000004');
 | 
					          should(data.material_id.toString()).be.eql('100000000000000000000004');
 | 
				
			||||||
          should(data.user_id.toString()).be.eql('000000000000000000000002');
 | 
					          should(data.user_id.toString()).be.eql('000000000000000000000002');
 | 
				
			||||||
          should(data).have.property('status', -1);
 | 
					          should(data).have.property('status',globals.status.deleted);
 | 
				
			||||||
          should(data).have.property('note_id', null);
 | 
					          should(data).have.property('note_id', null);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -536,7 +724,7 @@ describe('/sample', () => {
 | 
				
			|||||||
          NoteModel.findById('500000000000000000000003').lean().exec((err, data: any) => {
 | 
					          NoteModel.findById('500000000000000000000003').lean().exec((err, data: any) => {
 | 
				
			||||||
            if (err) return done(err);
 | 
					            if (err) return done(err);
 | 
				
			||||||
            should(data).have.property('sample_references').with.lengthOf(1);
 | 
					            should(data).have.property('sample_references').with.lengthOf(1);
 | 
				
			||||||
            should(data.sample_references[0].id.toString()).be.eql('400000000000000000000003');
 | 
					            should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003');
 | 
				
			||||||
            should(data.sample_references[0]).have.property('relation', 'part to sample');
 | 
					            should(data.sample_references[0]).have.property('relation', 'part to sample');
 | 
				
			||||||
            done();
 | 
					            done();
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
@@ -555,7 +743,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        should(res.body).be.eql({status: 'OK'});
 | 
					        should(res.body).be.eql({status: 'OK'});
 | 
				
			||||||
        SampleModel.findById('400000000000000000000001').lean().exec((err, data) => {
 | 
					        SampleModel.findById('400000000000000000000001').lean().exec((err, data) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(data).have.property('status', -1);
 | 
					          should(data).have.property('status',globals.status.deleted);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@@ -617,15 +805,16 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 200,
 | 
					        httpStatus: 200,
 | 
				
			||||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
				
			||||||
      }).end((err, res) => {
 | 
					      }).end((err, res) => {
 | 
				
			||||||
        if (err) return done (err);
 | 
					        if (err) return done (err);
 | 
				
			||||||
        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id');
 | 
					        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
				
			||||||
        should(res.body).have.property('_id').be.type('string');
 | 
					        should(res.body).have.property('_id').be.type('string');
 | 
				
			||||||
        should(res.body).have.property('number', 'Rng34');
 | 
					        should(res.body).have.property('number', 'Rng37');
 | 
				
			||||||
        should(res.body).have.property('color', 'black');
 | 
					        should(res.body).have.property('color', 'black');
 | 
				
			||||||
        should(res.body).have.property('type', 'granulate');
 | 
					        should(res.body).have.property('type', 'granulate');
 | 
				
			||||||
        should(res.body).have.property('batch', '1560237365');
 | 
					        should(res.body).have.property('batch', '1560237365');
 | 
				
			||||||
 | 
					        should(res.body).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'});
 | 
				
			||||||
        should(res.body).have.property('material_id', '100000000000000000000001');
 | 
					        should(res.body).have.property('material_id', '100000000000000000000001');
 | 
				
			||||||
        should(res.body).have.property('note_id').be.type('string');
 | 
					        should(res.body).have.property('note_id').be.type('string');
 | 
				
			||||||
        should(res.body).have.property('user_id', '000000000000000000000002');
 | 
					        should(res.body).have.property('user_id', '000000000000000000000002');
 | 
				
			||||||
@@ -638,21 +827,22 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 200,
 | 
					        httpStatus: 200,
 | 
				
			||||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
				
			||||||
      }).end(err => {
 | 
					      }).end(err => {
 | 
				
			||||||
        if (err) return done (err);
 | 
					        if (err) return done (err);
 | 
				
			||||||
        SampleModel.find({number: 'Rng34'}).lean().exec((err, data: any) => {
 | 
					        SampleModel.find({number: 'Rng37'}).lean().exec((err, data: any) => {
 | 
				
			||||||
          if (err) return done (err);
 | 
					          if (err) return done (err);
 | 
				
			||||||
          should(data).have.lengthOf(1);
 | 
					          should(data).have.lengthOf(1);
 | 
				
			||||||
          should(data[0]).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
					          should(data[0]).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
				
			||||||
          should(data[0]).have.property('_id');
 | 
					          should(data[0]).have.property('_id');
 | 
				
			||||||
          should(data[0]).have.property('number', 'Rng34');
 | 
					          should(data[0]).have.property('number', 'Rng37');
 | 
				
			||||||
          should(data[0]).have.property('color', 'black');
 | 
					          should(data[0]).have.property('color', 'black');
 | 
				
			||||||
          should(data[0]).have.property('type', 'granulate');
 | 
					          should(data[0]).have.property('type', 'granulate');
 | 
				
			||||||
          should(data[0]).have.property('batch', '1560237365');
 | 
					          should(data[0]).have.property('batch', '1560237365');
 | 
				
			||||||
 | 
					          should(data[0]).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'});
 | 
				
			||||||
          should(data[0].material_id.toString()).be.eql('100000000000000000000001');
 | 
					          should(data[0].material_id.toString()).be.eql('100000000000000000000001');
 | 
				
			||||||
          should(data[0].user_id.toString()).be.eql('000000000000000000000002');
 | 
					          should(data[0].user_id.toString()).be.eql('000000000000000000000002');
 | 
				
			||||||
          should(data[0]).have.property('status', 0);
 | 
					          should(data[0]).have.property('status',globals.status.new);
 | 
				
			||||||
          should(data[0]).have.property('note_id');
 | 
					          should(data[0]).have.property('note_id');
 | 
				
			||||||
          NoteModel.findById(data[0].note_id).lean().exec((err, data: any) => {
 | 
					          NoteModel.findById(data[0].note_id).lean().exec((err, data: any) => {
 | 
				
			||||||
            if (err) return done (err);
 | 
					            if (err) return done (err);
 | 
				
			||||||
@@ -660,7 +850,7 @@ describe('/sample', () => {
 | 
				
			|||||||
            should(data).have.property('comment', 'Testcomment');
 | 
					            should(data).have.property('comment', 'Testcomment');
 | 
				
			||||||
            should(data).have.property('sample_references');
 | 
					            should(data).have.property('sample_references');
 | 
				
			||||||
            should(data.sample_references).have.lengthOf(1);
 | 
					            should(data.sample_references).have.lengthOf(1);
 | 
				
			||||||
            should(data.sample_references[0].id.toString()).be.eql('400000000000000000000003');
 | 
					            should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003');
 | 
				
			||||||
            should(data.sample_references[0]).have.property('relation', 'part to this sample');
 | 
					            should(data.sample_references[0]).have.property('relation', 'part to this sample');
 | 
				
			||||||
            done();
 | 
					            done();
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
@@ -710,10 +900,10 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'johnnydoe'},
 | 
					        auth: {basic: 'johnnydoe'},
 | 
				
			||||||
        httpStatus: 200,
 | 
					        httpStatus: 200,
 | 
				
			||||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
				
			||||||
      }).end((err, res) => {
 | 
					      }).end((err, res) => {
 | 
				
			||||||
        if (err) return done (err);
 | 
					        if (err) return done (err);
 | 
				
			||||||
        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id');
 | 
					        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
				
			||||||
        should(res.body).have.property('_id').be.type('string');
 | 
					        should(res.body).have.property('_id').be.type('string');
 | 
				
			||||||
        should(res.body).have.property('number', 'Fe1');
 | 
					        should(res.body).have.property('number', 'Fe1');
 | 
				
			||||||
        should(res.body).have.property('color', 'black');
 | 
					        should(res.body).have.property('color', 'black');
 | 
				
			||||||
@@ -725,13 +915,35 @@ describe('/sample', () => {
 | 
				
			|||||||
        done();
 | 
					        done();
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    it('accepts a sample without condition', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'post',
 | 
				
			||||||
 | 
					        url: '/sample/new',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 200,
 | 
				
			||||||
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
				
			||||||
 | 
					      }).end((err, res) => {
 | 
				
			||||||
 | 
					        if (err) return done (err);
 | 
				
			||||||
 | 
					        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
				
			||||||
 | 
					        should(res.body).have.property('_id').be.type('string');
 | 
				
			||||||
 | 
					        should(res.body).have.property('number', 'Rng37');
 | 
				
			||||||
 | 
					        should(res.body).have.property('color', 'black');
 | 
				
			||||||
 | 
					        should(res.body).have.property('type', 'granulate');
 | 
				
			||||||
 | 
					        should(res.body).have.property('batch', '1560237365');
 | 
				
			||||||
 | 
					        should(res.body).have.property('condition', {});
 | 
				
			||||||
 | 
					        should(res.body).have.property('material_id', '100000000000000000000001');
 | 
				
			||||||
 | 
					        should(res.body).have.property('note_id').be.type('string');
 | 
				
			||||||
 | 
					        should(res.body).have.property('user_id', '000000000000000000000002');
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    it('rejects a color not defined for the material', done => {
 | 
					    it('rejects a color not defined for the material', done => {
 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
        method: 'post',
 | 
					        method: 'post',
 | 
				
			||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {color: 'green', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {color: 'green', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Color not available for material'}
 | 
					        res: {status: 'Color not available for material'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -741,7 +953,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '000000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '000000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Material not available'}
 | 
					        res: {status: 'Material not available'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -751,7 +963,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {number: 'Rng34', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {number: 'Rng34', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"number" is not allowed'}
 | 
					        res: {status: 'Invalid body format', details: '"number" is not allowed'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -761,17 +973,97 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '000000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '000000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Sample reference not available'}
 | 
					        res: {status: 'Sample reference not available'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects an invalid condition_template id', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'post',
 | 
				
			||||||
 | 
					        url: '/sample/new',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '20000h000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
 | 
					        res: {status: 'Condition template not available'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects a not existing condition_template id', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'post',
 | 
				
			||||||
 | 
					        url: '/sample/new',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '000000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
 | 
					        res: {status: 'Condition template not available'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects not specified condition parameters', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'post',
 | 
				
			||||||
 | 
					        url: '/sample/new',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, xxx: 23, condition_template: '20000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
 | 
					        res: {status: 'Condition template not available'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects missing condition parameters', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'post',
 | 
				
			||||||
 | 
					        url: '/sample/new',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', condition_template: '20000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
 | 
					        res: {status: 'Condition template not available'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects condition parameters not in the value range', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'post',
 | 
				
			||||||
 | 
					        url: '/sample/new',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'xxx', weeks: 3, condition_template: '20000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
 | 
					        res: {status: 'Condition template not available'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects a condition parameter below minimum range', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'post',
 | 
				
			||||||
 | 
					        url: '/sample/new',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 0, condition_template: '20000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
 | 
					        res: {status: 'Condition template not available'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects a condition parameter above maximum range', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'post',
 | 
				
			||||||
 | 
					        url: '/sample/new',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 11, condition_template: '20000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
 | 
					        res: {status: 'Condition template not available'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it('rejects a condition without condition template', done => {
 | 
				
			||||||
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
 | 
					        method: 'post',
 | 
				
			||||||
 | 
					        url: '/sample/new',
 | 
				
			||||||
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
 | 
					        httpStatus: 400,
 | 
				
			||||||
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
 | 
					        res: {status: 'Condition template not available'}
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    it('rejects a missing color', done => {
 | 
					    it('rejects a missing color', done => {
 | 
				
			||||||
      TestHelper.request(server, done, {
 | 
					      TestHelper.request(server, done, {
 | 
				
			||||||
        method: 'post',
 | 
					        method: 'post',
 | 
				
			||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"color" is required'}
 | 
					        res: {status: 'Invalid body format', details: '"color" is required'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -781,7 +1073,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {color: 'black', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {color: 'black', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"type" is required'}
 | 
					        res: {status: 'Invalid body format', details: '"type" is required'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -791,7 +1083,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {color: 'black', type: 'granulate', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {color: 'black', type: 'granulate', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"batch" is required'}
 | 
					        res: {status: 'Invalid body format', details: '"batch" is required'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -801,7 +1093,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"material_id" is required'}
 | 
					        res: {status: 'Invalid body format', details: '"material_id" is required'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -811,7 +1103,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'janedoe'},
 | 
					        auth: {basic: 'janedoe'},
 | 
				
			||||||
        httpStatus: 400,
 | 
					        httpStatus: 400,
 | 
				
			||||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
				
			||||||
        res: {status: 'Invalid body format', details: '"material_id" with value "10000000000h000000000001" fails to match the required pattern: /[0-9a-f]{24}/'}
 | 
					        res: {status: 'Invalid body format', details: '"material_id" with value "10000000000h000000000001" fails to match the required pattern: /[0-9a-f]{24}/'}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -821,7 +1113,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {key: 'janedoe'},
 | 
					        auth: {key: 'janedoe'},
 | 
				
			||||||
        httpStatus: 401,
 | 
					        httpStatus: 401,
 | 
				
			||||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    it('rejects requests from a read user', done => {
 | 
					    it('rejects requests from a read user', done => {
 | 
				
			||||||
@@ -830,7 +1122,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        auth: {basic: 'user'},
 | 
					        auth: {basic: 'user'},
 | 
				
			||||||
        httpStatus: 403,
 | 
					        httpStatus: 403,
 | 
				
			||||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    it('rejects unauthorized requests', done => {
 | 
					    it('rejects unauthorized requests', done => {
 | 
				
			||||||
@@ -838,7 +1130,7 @@ describe('/sample', () => {
 | 
				
			|||||||
        method: 'post',
 | 
					        method: 'post',
 | 
				
			||||||
        url: '/sample/new',
 | 
					        url: '/sample/new',
 | 
				
			||||||
        httpStatus: 401,
 | 
					        httpStatus: 401,
 | 
				
			||||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
					        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,10 +5,15 @@ import SampleValidate from './validate/sample';
 | 
				
			|||||||
import NoteFieldValidate from './validate/note_field';
 | 
					import NoteFieldValidate from './validate/note_field';
 | 
				
			||||||
import res400 from './validate/res400';
 | 
					import res400 from './validate/res400';
 | 
				
			||||||
import SampleModel from '../models/sample'
 | 
					import SampleModel from '../models/sample'
 | 
				
			||||||
 | 
					import MeasurementModel from '../models/measurement';
 | 
				
			||||||
import MaterialModel from '../models/material';
 | 
					import MaterialModel from '../models/material';
 | 
				
			||||||
import NoteModel from '../models/note';
 | 
					import NoteModel from '../models/note';
 | 
				
			||||||
import NoteFieldModel from '../models/note_field';
 | 
					import NoteFieldModel from '../models/note_field';
 | 
				
			||||||
import IdValidate from './validate/id';
 | 
					import IdValidate from './validate/id';
 | 
				
			||||||
 | 
					import mongoose from "mongoose";
 | 
				
			||||||
 | 
					import ConditionTemplateModel from '../models/condition_template';
 | 
				
			||||||
 | 
					import ParametersValidate from './validate/parameters';
 | 
				
			||||||
 | 
					import globals from '../globals';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const router = express.Router();
 | 
					const router = express.Router();
 | 
				
			||||||
@@ -16,7 +21,7 @@ const router = express.Router();
 | 
				
			|||||||
router.get('/samples', (req, res, next) => {
 | 
					router.get('/samples', (req, res, next) => {
 | 
				
			||||||
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
 | 
					  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  SampleModel.find({status: 10}).lean().exec((err, data) => {
 | 
					  SampleModel.find({status: globals.status.validated}).lean().exec((err, data) => {
 | 
				
			||||||
    if (err) return next(err);
 | 
					    if (err) return next(err);
 | 
				
			||||||
    res.json(_.compact(data.map(e => SampleValidate.output(e))));  // validate all and filter null values from validation errors
 | 
					    res.json(_.compact(data.map(e => SampleValidate.output(e))));  // validate all and filter null values from validation errors
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
@@ -25,17 +30,32 @@ router.get('/samples', (req, res, next) => {
 | 
				
			|||||||
router.get('/samples/:group(new|deleted)', (req, res, next) => {
 | 
					router.get('/samples/:group(new|deleted)', (req, res, next) => {
 | 
				
			||||||
  if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
 | 
					  if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let status;
 | 
					  SampleModel.find({status: globals.status[req.params.group]}).lean().exec((err, data) => {
 | 
				
			||||||
  switch (req.params.group) {
 | 
					 | 
				
			||||||
    case 'new': status = 0;
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case 'deleted': status = -1;
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  SampleModel.find({status: status}).lean().exec((err, data) => {
 | 
					 | 
				
			||||||
    if (err) return next(err);
 | 
					    if (err) return next(err);
 | 
				
			||||||
    res.json(_.compact(data.map(e => SampleValidate.output(e))));  // validate all and filter null values from validation errors
 | 
					    res.json(_.compact(data.map(e => SampleValidate.output(e))));  // validate all and filter null values from validation errors
 | 
				
			||||||
  })
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router.get('/sample/' + IdValidate.parameter(), (req, res, next) => {
 | 
				
			||||||
 | 
					  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  SampleModel.findById(req.params.id).populate('material_id').populate('user_id', 'name').populate('note_id').lean().exec((err, sampleData: any) => {
 | 
				
			||||||
 | 
					    if (err) return next(err);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (sampleData) {
 | 
				
			||||||
 | 
					      if (sampleData.status ===globals.status.deleted && !req.auth(res, ['maintain', 'admin'], 'all')) return;  // deleted samples only available for maintain/admin
 | 
				
			||||||
 | 
					      sampleData.material = sampleData.material_id;  // map data to right keys
 | 
				
			||||||
 | 
					      sampleData.user = sampleData.user_id.name;
 | 
				
			||||||
 | 
					      sampleData.notes = sampleData.note_id ? sampleData.note_id : {};
 | 
				
			||||||
 | 
					      MeasurementModel.find({sample_id: mongoose.Types.ObjectId(req.params.id)}).lean().exec((err, data) => {
 | 
				
			||||||
 | 
					        sampleData.measurements = data;
 | 
				
			||||||
 | 
					        res.json(SampleValidate.output(sampleData, 'details'));
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      res.status(404).json({status: 'Not found'});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
 | 
					router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
 | 
				
			||||||
@@ -60,6 +80,10 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
 | 
				
			|||||||
      if (!await materialCheck(sample, res, next, sampleData.material_id)) return;
 | 
					      if (!await materialCheck(sample, res, next, sampleData.material_id)) return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (sample.hasOwnProperty('condition') && !(_.isEmpty(sample.condition) && _.isEmpty(sampleData.condition))) {  // do not execute check if condition is and was empty
 | 
				
			||||||
 | 
					      if (!await conditionCheck(sample.condition, 'change', res, next)) return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (sample.hasOwnProperty('notes')) {
 | 
					    if (sample.hasOwnProperty('notes')) {
 | 
				
			||||||
      let newNotes = true;
 | 
					      let newNotes = true;
 | 
				
			||||||
      if (sampleData.note_id !== null) {  // old notes data exists
 | 
					      if (sampleData.note_id !== null) {  // old notes data exists
 | 
				
			||||||
@@ -89,10 +113,10 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // check for changes
 | 
					    // check for changes
 | 
				
			||||||
    if (!_.isEqual(_.pick(IdValidate.stringify(sampleData), _.keys(sample)), _.omit(sample, ['notes']))) {
 | 
					    if (!_.isEqual(_.pick(IdValidate.stringify(sampleData), _.keys(sample)), _.omit(sample, ['notes']))) {
 | 
				
			||||||
      sample.status = 0;
 | 
					      sample.status = globals.status.new;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await SampleModel.findByIdAndUpdate(req.params.id, sample, {new: true}).lean().exec((err, data) => {
 | 
					    await SampleModel.findByIdAndUpdate(req.params.id, sample, {new: true}).lean().exec((err, data: any) => {
 | 
				
			||||||
      if (err) return next(err);
 | 
					      if (err) return next(err);
 | 
				
			||||||
      res.json(SampleValidate.output(data));
 | 
					      res.json(SampleValidate.output(data));
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -112,7 +136,7 @@ router.delete('/sample/' + IdValidate.parameter(), (req, res, next) => {
 | 
				
			|||||||
    // only maintain and admin are allowed to edit other user's data
 | 
					    // only maintain and admin are allowed to edit other user's data
 | 
				
			||||||
    if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['maintain', 'admin'], 'basic')) return;
 | 
					    if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['maintain', 'admin'], 'basic')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await SampleModel.findByIdAndUpdate(req.params.id, {status: -1}).lean().exec(err => {  // set sample status
 | 
					    await SampleModel.findByIdAndUpdate(req.params.id, {status:globals.status.deleted}).lean().exec(err => {  // set sample status
 | 
				
			||||||
      if (err) return next(err);
 | 
					      if (err) return next(err);
 | 
				
			||||||
      if (sampleData.note_id !== null) {  // handle notes
 | 
					      if (sampleData.note_id !== null) {  // handle notes
 | 
				
			||||||
        NoteModel.findById(sampleData.note_id).lean().exec((err, data: any) => {  // find notes to update note_fields
 | 
					        NoteModel.findById(sampleData.note_id).lean().exec((err, data: any) => {  // find notes to update note_fields
 | 
				
			||||||
@@ -133,6 +157,10 @@ router.delete('/sample/' + IdValidate.parameter(), (req, res, next) => {
 | 
				
			|||||||
router.post('/sample/new', async (req, res, next) => {
 | 
					router.post('/sample/new', async (req, res, next) => {
 | 
				
			||||||
  if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
 | 
					  if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!req.body.hasOwnProperty('condition')) {  // add empty condition if not specified
 | 
				
			||||||
 | 
					    req.body.condition = {};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const {error, value: sample} = SampleValidate.input(req.body, 'new');
 | 
					  const {error, value: sample} = SampleValidate.input(req.body, 'new');
 | 
				
			||||||
  if (error) return res400(error, res);
 | 
					  if (error) return res400(error, res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -143,7 +171,11 @@ router.post('/sample/new', async (req, res, next) => {
 | 
				
			|||||||
    customFieldsChange(Object.keys(sample.notes.custom_fields), 1);
 | 
					    customFieldsChange(Object.keys(sample.notes.custom_fields), 1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sample.status = 0;  // set status to new
 | 
					  if (!_.isEmpty(sample.condition)) {  // do not execute check if condition is empty
 | 
				
			||||||
 | 
					    if (!await conditionCheck(sample.condition, 'change', res, next)) return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sample.status = globals.status.new;  // set status to new
 | 
				
			||||||
  sample.number = await numberGenerate(sample, req, res, next);
 | 
					  sample.number = await numberGenerate(sample, req, res, next);
 | 
				
			||||||
  if (!sample.number) return;
 | 
					  if (!sample.number) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -152,6 +184,7 @@ router.post('/sample/new', async (req, res, next) => {
 | 
				
			|||||||
    delete sample.notes;
 | 
					    delete sample.notes;
 | 
				
			||||||
    sample.note_id = data._id;
 | 
					    sample.note_id = data._id;
 | 
				
			||||||
    sample.user_id = req.authDetails.id;
 | 
					    sample.user_id = req.authDetails.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    new SampleModel(sample).save((err, data) => {
 | 
					    new SampleModel(sample).save((err, data) => {
 | 
				
			||||||
      if (err) return next(err);
 | 
					      if (err) return next(err);
 | 
				
			||||||
      res.json(SampleValidate.output(data.toObject()));
 | 
					      res.json(SampleValidate.output(data.toObject()));
 | 
				
			||||||
@@ -172,14 +205,15 @@ router.get('/sample/notes/fields', (req, res, next) => {
 | 
				
			|||||||
module.exports = router;
 | 
					module.exports = router;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function numberGenerate (sample, req, res, next) {  // generate number, returns false on error
 | 
					async function numberGenerate (sample, req, res, next) {  // generate number in format Location32, returns false on error
 | 
				
			||||||
  const sampleData = await SampleModel
 | 
					  const sampleData = await SampleModel
 | 
				
			||||||
    .find({number: new RegExp('^' + req.authDetails.location + '[0-9]+$', 'm')})
 | 
					    .findOne({number: new RegExp('^' + req.authDetails.location + '[0-9]+$', 'm')})
 | 
				
			||||||
 | 
					    .sort({number: -1})
 | 
				
			||||||
    .lean()
 | 
					    .lean()
 | 
				
			||||||
    .exec()
 | 
					    .exec()
 | 
				
			||||||
    .catch(err => next(err));
 | 
					    .catch(err => next(err));
 | 
				
			||||||
  if (sampleData instanceof Error) return false;
 | 
					  if (sampleData instanceof Error) return false;
 | 
				
			||||||
  return req.authDetails.location + (sampleData.length > 0 ? Number(sampleData[0].number.replace(/[^0-9]+/g, '')) + 1 : 1);
 | 
					  return req.authDetails.location + (sampleData ? Number(sampleData.number.replace(/[^0-9]+/g, '')) + 1 : 1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function materialCheck (sample, res, next, id = sample.material_id) {  // validate material_id and color, returns false if invalid
 | 
					async function materialCheck (sample, res, next, id = sample.material_id) {  // validate material_id and color, returns false if invalid
 | 
				
			||||||
@@ -196,13 +230,31 @@ async function materialCheck (sample, res, next, id = sample.material_id) {  //
 | 
				
			|||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function conditionCheck (condition, param, res, next) {  // validate treatment template, returns false if invalid, otherwise template data
 | 
				
			||||||
 | 
					  if (!condition.condition_template || !IdValidate.valid(condition.condition_template)) {  // template id not found
 | 
				
			||||||
 | 
					    res.status(400).json({status: 'Condition template not available'});
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const conditionData = await ConditionTemplateModel.findById(condition.condition_template).lean().exec().catch(err => next(err)) as any;
 | 
				
			||||||
 | 
					  if (conditionData instanceof Error) return false;
 | 
				
			||||||
 | 
					  if (!conditionData) {  // template not found
 | 
				
			||||||
 | 
					    res.status(400).json({status: 'Condition template not available'});
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // validate parameters
 | 
				
			||||||
 | 
					  const {error, value: ignore} = ParametersValidate.input(_.omit(condition, 'condition_template'), conditionData.parameters, param);
 | 
				
			||||||
 | 
					  if (error) {res400(error, res); return false;}
 | 
				
			||||||
 | 
					  return conditionData;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function sampleRefCheck (sample, res, next) {  // validate sample_references, resolves false for invalid reference
 | 
					function sampleRefCheck (sample, res, next) {  // validate sample_references, resolves false for invalid reference
 | 
				
			||||||
  return new Promise(resolve => {
 | 
					  return new Promise(resolve => {
 | 
				
			||||||
    if (sample.notes.sample_references.length > 0) {  // there are sample_references
 | 
					    if (sample.notes.sample_references.length > 0) {  // there are sample_references
 | 
				
			||||||
      let referencesCount = sample.notes.sample_references.length;  // count to keep track of running async operations
 | 
					      let referencesCount = sample.notes.sample_references.length;  // count to keep track of running async operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      sample.notes.sample_references.forEach(reference => {
 | 
					      sample.notes.sample_references.forEach(reference => {
 | 
				
			||||||
        SampleModel.findById(reference.id).lean().exec((err, data) => {
 | 
					        SampleModel.findById(reference.sample_id).lean().exec((err, data) => {
 | 
				
			||||||
          if (err) {next(err); resolve(false)}
 | 
					          if (err) {next(err); resolve(false)}
 | 
				
			||||||
          if (!data) {
 | 
					          if (!data) {
 | 
				
			||||||
            res.status(400).json({status: 'Sample reference not available'});
 | 
					            res.status(400).json({status: 'Sample reference not available'});
 | 
				
			||||||
@@ -230,7 +282,7 @@ function customFieldsChange (fields, amount) {  // update custom_fields and resp
 | 
				
			|||||||
          if (err) return console.error(err);
 | 
					          if (err) return console.error(err);
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      else if (data.qty <= 0) {
 | 
					      else if (data.qty <= 0) {  // delete document if field is not used anymore
 | 
				
			||||||
        NoteFieldModel.findOneAndDelete({name: field}).lean().exec(err => {
 | 
					        NoteFieldModel.findOneAndDelete({name: field}).lean().exec(err => {
 | 
				
			||||||
          if (err) return console.error(err);
 | 
					          if (err) return console.error(err);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,12 @@
 | 
				
			|||||||
import should from 'should/as-function';
 | 
					import should from 'should/as-function';
 | 
				
			||||||
import _ from 'lodash';
 | 
					import _ from 'lodash';
 | 
				
			||||||
import TemplateTreatmentModel from '../models/treatment_template';
 | 
					import TemplateConditionModel from '../models/condition_template';
 | 
				
			||||||
import TemplateMeasurementModel from '../models/measurement_template';
 | 
					import TemplateMeasurementModel from '../models/measurement_template';
 | 
				
			||||||
import TestHelper from "../test/helper";
 | 
					import TestHelper from "../test/helper";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: do not allow usage of old templates for new samples
 | 
					// TODO: do not allow usage of old templates for new samples
 | 
				
			||||||
 | 
					// TODO: remove number_prefix
 | 
				
			||||||
 | 
					// TODO: template parameters are not allowed to be condition_template
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('/template', () => {
 | 
					describe('/template', () => {
 | 
				
			||||||
  let server;
 | 
					  let server;
 | 
				
			||||||
@@ -12,25 +14,24 @@ describe('/template', () => {
 | 
				
			|||||||
  beforeEach(done => server = TestHelper.beforeEach(server, done));
 | 
					  beforeEach(done => server = TestHelper.beforeEach(server, done));
 | 
				
			||||||
  afterEach(done => TestHelper.afterEach(server, done));
 | 
					  afterEach(done => TestHelper.afterEach(server, done));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('/template/treatment', () => {
 | 
					  describe('/template/condition', () => {
 | 
				
			||||||
    describe('GET /template/treatments', () => {
 | 
					    describe('GET /template/conditions', () => {
 | 
				
			||||||
      it('returns all treatment templates', done => {
 | 
					      it('returns all condition templates', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'get',
 | 
					          method: 'get',
 | 
				
			||||||
          url: '/template/treatments',
 | 
					          url: '/template/conditions',
 | 
				
			||||||
          auth: {basic: 'janedoe'},
 | 
					          auth: {basic: 'janedoe'},
 | 
				
			||||||
          httpStatus: 200
 | 
					          httpStatus: 200
 | 
				
			||||||
        }).end((err, res) => {
 | 
					        }).end((err, res) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          const json = require('../test/db.json');
 | 
					          const json = require('../test/db.json');
 | 
				
			||||||
          should(res.body).have.lengthOf(json.collections.treatment_templates.length);
 | 
					          should(res.body).have.lengthOf(json.collections.condition_templates.length);
 | 
				
			||||||
          should(res.body).matchEach(treatment => {
 | 
					          should(res.body).matchEach(condition => {
 | 
				
			||||||
            should(treatment).have.only.keys('_id', 'name', 'version', 'parameters', 'number_prefix');
 | 
					            should(condition).have.only.keys('_id', 'name', 'version', 'parameters');
 | 
				
			||||||
            should(treatment).have.property('_id').be.type('string');
 | 
					            should(condition).have.property('_id').be.type('string');
 | 
				
			||||||
            should(treatment).have.property('name').be.type('string');
 | 
					            should(condition).have.property('name').be.type('string');
 | 
				
			||||||
            should(treatment).have.property('version').be.type('number');
 | 
					            should(condition).have.property('version').be.type('number');
 | 
				
			||||||
            should(treatment).have.property('number_prefix').be.type('string');
 | 
					            should(condition.parameters).matchEach(number => {
 | 
				
			||||||
            should(treatment.parameters).matchEach(number => {
 | 
					 | 
				
			||||||
              should(number).have.only.keys('name', 'range');
 | 
					              should(number).have.only.keys('name', 'range');
 | 
				
			||||||
              should(number).have.property('name').be.type('string');
 | 
					              should(number).have.property('name').be.type('string');
 | 
				
			||||||
              should(number).have.property('range').be.type('object');
 | 
					              should(number).have.property('range').be.type('object');
 | 
				
			||||||
@@ -42,7 +43,7 @@ describe('/template', () => {
 | 
				
			|||||||
      it('rejects an API key', done => {
 | 
					      it('rejects an API key', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'get',
 | 
					          method: 'get',
 | 
				
			||||||
          url: '/template/treatments',
 | 
					          url: '/template/conditions',
 | 
				
			||||||
          auth: {key: 'janedoe'},
 | 
					          auth: {key: 'janedoe'},
 | 
				
			||||||
          httpStatus: 401
 | 
					          httpStatus: 401
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -50,26 +51,26 @@ describe('/template', () => {
 | 
				
			|||||||
      it('rejects unauthorized requests', done => {
 | 
					      it('rejects unauthorized requests', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'get',
 | 
					          method: 'get',
 | 
				
			||||||
          url: '/template/treatments',
 | 
					          url: '/template/conditions',
 | 
				
			||||||
          httpStatus: 401
 | 
					          httpStatus: 401
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe('GET /template/treatment/{id}', () => {
 | 
					    describe('GET /template/condition/{id}', () => {
 | 
				
			||||||
      it('returns the right treatment template', done => {
 | 
					      it('returns the right condition template', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'get',
 | 
					          method: 'get',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'janedoe'},
 | 
					          auth: {basic: 'janedoe'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, number_prefix: 'A', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
					          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects an API key', done => {
 | 
					      it('rejects an API key', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'get',
 | 
					          method: 'get',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {key: 'janedoe'},
 | 
					          auth: {key: 'janedoe'},
 | 
				
			||||||
          httpStatus: 401
 | 
					          httpStatus: 401
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -77,7 +78,7 @@ describe('/template', () => {
 | 
				
			|||||||
      it('rejects an unknown id', done => {
 | 
					      it('rejects an unknown id', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'get',
 | 
					          method: 'get',
 | 
				
			||||||
          url: '/template/treatment/000000000000000000000001',
 | 
					          url: '/template/condition/000000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'janedoe'},
 | 
					          auth: {basic: 'janedoe'},
 | 
				
			||||||
          httpStatus: 404
 | 
					          httpStatus: 404
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -85,58 +86,57 @@ describe('/template', () => {
 | 
				
			|||||||
      it('rejects unauthorized requests', done => {
 | 
					      it('rejects unauthorized requests', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'get',
 | 
					          method: 'get',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          httpStatus: 401
 | 
					          httpStatus: 401
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe('PUT /template/treatment/{name}', () => {
 | 
					    describe('PUT /template/condition/{name}', () => {
 | 
				
			||||||
      it('returns the right treatment template', done => {
 | 
					      it('returns the right condition template', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          req: {},
 | 
					          req: {},
 | 
				
			||||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, number_prefix: 'A', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
					          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('keeps unchanged properties', done => {
 | 
					      it('keeps unchanged properties', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          req: {name: 'heat treatment', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]},
 | 
					          req: {name: 'heat treatment', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]},
 | 
				
			||||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, number_prefix: 'A', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
					          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('keeps only one unchanged property', done => {
 | 
					      it('keeps only one unchanged property', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          req: {name: 'heat treatment'},
 | 
					          req: {name: 'heat treatment'},
 | 
				
			||||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, number_prefix: 'A', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
					          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('changes the given properties', done => {
 | 
					      it('changes the given properties', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
 | 
					          req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
 | 
				
			||||||
        }).end((err, res) => {
 | 
					        }).end((err, res) => {
 | 
				
			||||||
            if (err) return done(err);
 | 
					            if (err) return done(err);
 | 
				
			||||||
            TemplateTreatmentModel.findById(res.body._id).lean().exec((err, data:any) => {
 | 
					            TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => {
 | 
				
			||||||
              if (err) return done(err);
 | 
					              if (err) return done(err);
 | 
				
			||||||
              should(data).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters', '__v');
 | 
					              should(data).have.only.keys('_id', 'name', 'version', 'parameters', '__v');
 | 
				
			||||||
              should(data).have.property('name', 'heat aging');
 | 
					              should(data).have.property('name', 'heat aging');
 | 
				
			||||||
              should(data).have.property('version', 2);
 | 
					              should(data).have.property('version', 2);
 | 
				
			||||||
              should(data).have.property('number_prefix', 'A');
 | 
					 | 
				
			||||||
              should(data).have.property('parameters').have.lengthOf(1);
 | 
					              should(data).have.property('parameters').have.lengthOf(1);
 | 
				
			||||||
              should(data.parameters[0]).have.property('name', 'time');
 | 
					              should(data.parameters[0]).have.property('name', 'time');
 | 
				
			||||||
              should(data.parameters[0]).have.property('range');
 | 
					              should(data.parameters[0]).have.property('range');
 | 
				
			||||||
@@ -148,18 +148,17 @@ describe('/template', () => {
 | 
				
			|||||||
      it('allows changing only one property', done => {
 | 
					      it('allows changing only one property', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          req: {name: 'heat aging'}
 | 
					          req: {name: 'heat aging'}
 | 
				
			||||||
        }).end((err, res) => {
 | 
					        }).end((err, res) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          TemplateTreatmentModel.findById(res.body._id).lean().exec((err, data:any) => {
 | 
					          TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => {
 | 
				
			||||||
            if (err) return done(err);
 | 
					            if (err) return done(err);
 | 
				
			||||||
            should(data).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters', '__v');
 | 
					            should(data).have.only.keys('_id', 'name', 'version', 'parameters', '__v');
 | 
				
			||||||
            should(data).have.property('name', 'heat aging');
 | 
					            should(data).have.property('name', 'heat aging');
 | 
				
			||||||
            should(data).have.property('version', 2);
 | 
					            should(data).have.property('version', 2);
 | 
				
			||||||
            should(data).have.property('number_prefix', 'A');
 | 
					 | 
				
			||||||
            should(data).have.property('parameters').have.lengthOf(2);
 | 
					            should(data).have.property('parameters').have.lengthOf(2);
 | 
				
			||||||
            should(data.parameters[0]).have.property('name', 'material');
 | 
					            should(data.parameters[0]).have.property('name', 'material');
 | 
				
			||||||
            should(data.parameters[1]).have.property('name', 'weeks');
 | 
					            should(data.parameters[1]).have.property('name', 'weeks');
 | 
				
			||||||
@@ -170,59 +169,59 @@ describe('/template', () => {
 | 
				
			|||||||
      it('supports values ranges', done => {
 | 
					      it('supports values ranges', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          req: {parameters: [{name: 'time', range: {values: [1, 2, 5]}}]}
 | 
					          req: {parameters: [{name: 'time', range: {values: [1, 2, 5]}}]}
 | 
				
			||||||
        }).end((err, res) => {
 | 
					        }).end((err, res) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {values: [1, 2, 5]}}]});
 | 
					          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, parameters: [{name: 'time', range: {values: [1, 2, 5]}}]});
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('supports min max ranges', done => {
 | 
					      it('supports min max ranges', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          req: {parameters: [{name: 'time', range: {min: 1, max: 11}}]}
 | 
					          req: {parameters: [{name: 'time', range: {min: 1, max: 11}}]}
 | 
				
			||||||
        }).end((err, res) => {
 | 
					        }).end((err, res) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {min: 1, max: 11}}]});
 | 
					          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, parameters: [{name: 'time', range: {min: 1, max: 11}}]});
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('supports array type ranges', done => {
 | 
					      it('supports array type ranges', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          req: {parameters: [{name: 'time', range: {type: 'array'}}]}
 | 
					          req: {parameters: [{name: 'time', range: {type: 'array'}}]}
 | 
				
			||||||
        }).end((err, res) => {
 | 
					        }).end((err, res) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {type: 'array'}}]});
 | 
					          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, parameters: [{name: 'time', range: {type: 'array'}}]});
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('supports empty ranges', done => {
 | 
					      it('supports empty ranges', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          req: {parameters: [{name: 'time', range: {}}]}
 | 
					          req: {parameters: [{name: 'time', range: {}}]}
 | 
				
			||||||
        }).end((err, res) => {
 | 
					        }).end((err, res) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {}}]});
 | 
					          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, parameters: [{name: 'time', range: {}}]});
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects not specified parameters', done => {
 | 
					      it('rejects not specified parameters', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 400,
 | 
					          httpStatus: 400,
 | 
				
			||||||
          req: {name: 'heat treatment', parameters: [{name: 'material', range: {xx: 5}}]},
 | 
					          req: {name: 'heat treatment', parameters: [{name: 'material', range: {xx: 5}}]},
 | 
				
			||||||
@@ -232,7 +231,7 @@ describe('/template', () => {
 | 
				
			|||||||
      it('rejects an invalid id', done => {
 | 
					      it('rejects an invalid id', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/2000000000h0000000000001',
 | 
					          url: '/template/condition/2000000000h0000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 404,
 | 
					          httpStatus: 404,
 | 
				
			||||||
          req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
 | 
					          req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
 | 
				
			||||||
@@ -241,26 +240,16 @@ describe('/template', () => {
 | 
				
			|||||||
      it('rejects an unknown id', done => {
 | 
					      it('rejects an unknown id', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/000000000000000000000001',
 | 
					          url: '/template/condition/000000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 404,
 | 
					          httpStatus: 404,
 | 
				
			||||||
          req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
 | 
					          req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects already existing number prefixes', done => {
 | 
					 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
          method: 'put',
 | 
					 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					 | 
				
			||||||
          httpStatus: 400,
 | 
					 | 
				
			||||||
          req: {number_prefix: 'B', parameters: [{name: 'time', range: {min: 1}}]},
 | 
					 | 
				
			||||||
          res: {status: 'Number prefix already taken'}
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      it('rejects an API key', done => {
 | 
					      it('rejects an API key', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {key: 'admin'},
 | 
					          auth: {key: 'admin'},
 | 
				
			||||||
          httpStatus: 401,
 | 
					          httpStatus: 401,
 | 
				
			||||||
          req: {}
 | 
					          req: {}
 | 
				
			||||||
@@ -269,7 +258,7 @@ describe('/template', () => {
 | 
				
			|||||||
      it('rejects requests from a write user', done => {
 | 
					      it('rejects requests from a write user', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          auth: {basic: 'janedoe'},
 | 
					          auth: {basic: 'janedoe'},
 | 
				
			||||||
          httpStatus: 403,
 | 
					          httpStatus: 403,
 | 
				
			||||||
          req: {}
 | 
					          req: {}
 | 
				
			||||||
@@ -278,27 +267,26 @@ describe('/template', () => {
 | 
				
			|||||||
      it('rejects unauthorized requests', done => {
 | 
					      it('rejects unauthorized requests', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'put',
 | 
					          method: 'put',
 | 
				
			||||||
          url: '/template/treatment/200000000000000000000001',
 | 
					          url: '/template/condition/200000000000000000000001',
 | 
				
			||||||
          httpStatus: 401,
 | 
					          httpStatus: 401,
 | 
				
			||||||
          req: {}
 | 
					          req: {}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe('POST /template/treatment/new', () => {
 | 
					    describe('POST /template/condition/new', () => {
 | 
				
			||||||
      it('returns the right treatment template', done => {
 | 
					      it('returns the right condition template', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          req: {name: 'heat treatment3', number_prefix: 'C', parameters: [{name: 'material', range: {values: ['copper']}}]}
 | 
					          req: {name: 'heat treatment3', parameters: [{name: 'material', range: {values: ['copper']}}]}
 | 
				
			||||||
        }).end((err, res) => {
 | 
					        }).end((err, res) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          should(res.body).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters');
 | 
					          should(res.body).have.only.keys('_id', 'name', 'version', 'parameters');
 | 
				
			||||||
          should(res.body).have.property('name', 'heat treatment3');
 | 
					          should(res.body).have.property('name', 'heat treatment3');
 | 
				
			||||||
          should(res.body).have.property('version', 1);
 | 
					          should(res.body).have.property('version', 1);
 | 
				
			||||||
          should(res.body).have.property('number_prefix', 'C');
 | 
					 | 
				
			||||||
          should(res.body).have.property('parameters').have.lengthOf(1);
 | 
					          should(res.body).have.property('parameters').have.lengthOf(1);
 | 
				
			||||||
          should(res.body.parameters[0]).have.property('name', 'material');
 | 
					          should(res.body.parameters[0]).have.property('name', 'material');
 | 
				
			||||||
          should(res.body.parameters[0]).have.property('range');
 | 
					          should(res.body.parameters[0]).have.property('range');
 | 
				
			||||||
@@ -310,18 +298,17 @@ describe('/template', () => {
 | 
				
			|||||||
      it('stores the template', done => {
 | 
					      it('stores the template', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 200,
 | 
					          httpStatus: 200,
 | 
				
			||||||
          req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]}
 | 
					          req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
 | 
				
			||||||
        }).end((err, res) => {
 | 
					        }).end((err, res) => {
 | 
				
			||||||
          if (err) return done(err);
 | 
					          if (err) return done(err);
 | 
				
			||||||
          TemplateTreatmentModel.findById(res.body._id).lean().exec((err, data:any) => {
 | 
					          TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => {
 | 
				
			||||||
            if (err) return done(err);
 | 
					            if (err) return done(err);
 | 
				
			||||||
            should(data).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters', '__v');
 | 
					            should(data).have.only.keys('_id', 'name', 'version', 'parameters', '__v');
 | 
				
			||||||
            should(data).have.property('name', 'heat aging');
 | 
					            should(data).have.property('name', 'heat aging');
 | 
				
			||||||
            should(data).have.property('version', 1);
 | 
					            should(data).have.property('version', 1);
 | 
				
			||||||
            should(data).have.property('number_prefix', 'C');
 | 
					 | 
				
			||||||
            should(data).have.property('parameters').have.lengthOf(1);
 | 
					            should(data).have.property('parameters').have.lengthOf(1);
 | 
				
			||||||
            should(data.parameters[0]).have.property('name', 'time');
 | 
					            should(data.parameters[0]).have.property('name', 'time');
 | 
				
			||||||
            should(data.parameters[0]).have.property('range');
 | 
					            should(data.parameters[0]).have.property('range');
 | 
				
			||||||
@@ -333,117 +320,97 @@ describe('/template', () => {
 | 
				
			|||||||
      it('rejects a missing name', done => {
 | 
					      it('rejects a missing name', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 400,
 | 
					          httpStatus: 400,
 | 
				
			||||||
          req: {number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]},
 | 
					          req: {parameters: [{name: 'time', range: {min: 1}}]},
 | 
				
			||||||
          res: {status: 'Invalid body format', details: '"name" is required'}
 | 
					          res: {status: 'Invalid body format', details: '"name" is required'}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects a missing number prefix', done => {
 | 
					      it('rejects a number prefix', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 400,
 | 
					          httpStatus: 400,
 | 
				
			||||||
          req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]},
 | 
					          req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]},
 | 
				
			||||||
          res: {status: 'Invalid body format', details: '"number_prefix" is required'}
 | 
					          res: {status: 'Invalid body format', details: '"number_prefix" is not allowed'}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects missing parameters', done => {
 | 
					      it('rejects missing parameters', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 400,
 | 
					          httpStatus: 400,
 | 
				
			||||||
          req: {name: 'heat aging', number_prefix: 'C'},
 | 
					          req: {name: 'heat aging'},
 | 
				
			||||||
          res: {status: 'Invalid body format', details: '"parameters" is required'}
 | 
					          res: {status: 'Invalid body format', details: '"parameters" is required'}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects a missing parameter name', done => {
 | 
					      it('rejects a missing parameter name', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 400,
 | 
					          httpStatus: 400,
 | 
				
			||||||
          req: {name: 'heat aging', number_prefix: 'C', parameters: [{range: {min: 1}}]},
 | 
					          req: {name: 'heat aging', parameters: [{range: {min: 1}}]},
 | 
				
			||||||
          res: {status: 'Invalid body format', details: '"parameters[0].name" is required'}
 | 
					          res: {status: 'Invalid body format', details: '"parameters[0].name" is required'}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects a number prefix containing numbers', done => {
 | 
					 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
          method: 'post',
 | 
					 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					 | 
				
			||||||
          httpStatus: 400,
 | 
					 | 
				
			||||||
          req: {name: 'heat aging', number_prefix: 'AB5', parameters: [{name: 'time', range: {min: 1}}]},
 | 
					 | 
				
			||||||
          res: {status: 'Invalid body format', details: '"number_prefix" with value "AB5" fails to match the required pattern: /^[a-zA-Z]+$/'}
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      it('rejects a missing parameter range', done => {
 | 
					      it('rejects a missing parameter range', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 400,
 | 
					          httpStatus: 400,
 | 
				
			||||||
          req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time'}]},
 | 
					          req: {name: 'heat aging', parameters: [{name: 'time'}]},
 | 
				
			||||||
          res: {status: 'Invalid body format', details: '"parameters[0].range" is required'}
 | 
					          res: {status: 'Invalid body format', details: '"parameters[0].range" is required'}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects an invalid parameter range property', done => {
 | 
					      it('rejects an invalid parameter range property', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 400,
 | 
					          httpStatus: 400,
 | 
				
			||||||
          req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {xx: 1}}]},
 | 
					          req: {name: 'heat aging', parameters: [{name: 'time', range: {xx: 1}}]},
 | 
				
			||||||
          res: {status: 'Invalid body format', details: '"parameters[0].range.xx" is not allowed'}
 | 
					          res: {status: 'Invalid body format', details: '"parameters[0].range.xx" is not allowed'}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects wrong properties', done => {
 | 
					      it('rejects wrong properties', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					          auth: {basic: 'admin'},
 | 
				
			||||||
          httpStatus: 400,
 | 
					          httpStatus: 400,
 | 
				
			||||||
          req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {}}], xx: 33},
 | 
					          req: {name: 'heat aging', parameters: [{name: 'time', range: {}}], xx: 33},
 | 
				
			||||||
          res: {status: 'Invalid body format', details: '"xx" is not allowed'}
 | 
					          res: {status: 'Invalid body format', details: '"xx" is not allowed'}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects already existing number prefixes', done => {
 | 
					 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					 | 
				
			||||||
          method: 'post',
 | 
					 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					 | 
				
			||||||
          auth: {basic: 'admin'},
 | 
					 | 
				
			||||||
          httpStatus: 400,
 | 
					 | 
				
			||||||
          req: {name: 'heat aging', number_prefix: 'B', parameters: [{name: 'time', range: {min: 1}}]},
 | 
					 | 
				
			||||||
          res: {status: 'Number prefix already taken'}
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      it('rejects an API key', done => {
 | 
					      it('rejects an API key', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          auth: {key: 'admin'},
 | 
					          auth: {key: 'admin'},
 | 
				
			||||||
          httpStatus: 401,
 | 
					          httpStatus: 401,
 | 
				
			||||||
          req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]}
 | 
					          req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects requests from a write user', done => {
 | 
					      it('rejects requests from a write user', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          auth: {basic: 'janedoe'},
 | 
					          auth: {basic: 'janedoe'},
 | 
				
			||||||
          httpStatus: 403,
 | 
					          httpStatus: 403,
 | 
				
			||||||
          req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]}
 | 
					          req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      it('rejects unauthorized requests', done => {
 | 
					      it('rejects unauthorized requests', done => {
 | 
				
			||||||
        TestHelper.request(server, done, {
 | 
					        TestHelper.request(server, done, {
 | 
				
			||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
          url: '/template/treatment/new',
 | 
					          url: '/template/condition/new',
 | 
				
			||||||
          httpStatus: 401,
 | 
					          httpStatus: 401,
 | 
				
			||||||
          req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]}
 | 
					          req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,8 +2,8 @@ import express from 'express';
 | 
				
			|||||||
import _ from 'lodash';
 | 
					import _ from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import TemplateValidate from './validate/template';
 | 
					import TemplateValidate from './validate/template';
 | 
				
			||||||
import TemplateTreatmentModel from '../models/treatment_template';
 | 
					import ConditionTemplateModel from '../models/condition_template';
 | 
				
			||||||
import TemplateMeasurementModel from '../models/measurement_template';
 | 
					import MeasurementTemplateModel from '../models/measurement_template';
 | 
				
			||||||
import res400 from './validate/res400';
 | 
					import res400 from './validate/res400';
 | 
				
			||||||
import IdValidate from './validate/id';
 | 
					import IdValidate from './validate/id';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -11,23 +11,23 @@ import IdValidate from './validate/id';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const router = express.Router();
 | 
					const router = express.Router();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.get('/template/:collection(measurements|treatments)', (req, res, next) => {
 | 
					router.get('/template/:collection(measurements|conditions)', (req, res, next) => {
 | 
				
			||||||
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
 | 
					  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  req.params.collection = req.params.collection.replace(/s$/g, '');  // remove trailing s
 | 
					  req.params.collection = req.params.collection.replace(/s$/g, '');  // remove trailing s
 | 
				
			||||||
  model(req).find({}).lean().exec((err, data) => {
 | 
					  model(req).find({}).lean().exec((err, data) => {
 | 
				
			||||||
     if (err) next (err);
 | 
					     if (err) next (err);
 | 
				
			||||||
     res.json(_.compact(data.map(e => TemplateValidate.output(e, req.params.collection))));  // validate all and filter null values from validation errors
 | 
					     res.json(_.compact(data.map(e => TemplateValidate.output(e))));  // validate all and filter null values from validation errors
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.get('/template/:collection(measurement|treatment)/' + IdValidate.parameter(), (req, res, next) => {
 | 
					router.get('/template/:collection(measurement|condition)/' + IdValidate.parameter(), (req, res, next) => {
 | 
				
			||||||
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
 | 
					  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  model(req).findById(req.params.id).lean().exec((err, data) => {
 | 
					  model(req).findById(req.params.id).lean().exec((err, data) => {
 | 
				
			||||||
    if (err) next (err);
 | 
					    if (err) next (err);
 | 
				
			||||||
    if (data) {
 | 
					    if (data) {
 | 
				
			||||||
      res.json(TemplateValidate.output(data, req.params.collection));
 | 
					      res.json(TemplateValidate.output(data));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
      res.status(404).json({status: 'Not found'});
 | 
					      res.status(404).json({status: 'Not found'});
 | 
				
			||||||
@@ -35,10 +35,10 @@ router.get('/template/:collection(measurement|treatment)/' + IdValidate.paramete
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.put('/template/:collection(measurement|treatment)/' + IdValidate.parameter(), async (req, res, next) => {
 | 
					router.put('/template/:collection(measurement|condition)/' + IdValidate.parameter(), async (req, res, next) => {
 | 
				
			||||||
  if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
 | 
					  if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const {error, value: template} = TemplateValidate.input(req.body, 'change', req.params.collection);
 | 
					  const {error, value: template} = TemplateValidate.input(req.body, 'change');
 | 
				
			||||||
  if (error) return res400(error, res);
 | 
					  if (error) return res400(error, res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const templateData = await model(req).findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
 | 
					  const templateData = await model(req).findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
 | 
				
			||||||
@@ -47,52 +47,34 @@ router.put('/template/:collection(measurement|treatment)/' + IdValidate.paramete
 | 
				
			|||||||
    res.status(404).json({status: 'Not found'});
 | 
					    res.status(404).json({status: 'Not found'});
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (_.has(template, 'number_prefix') && template.number_prefix !== templateData.number_prefix) {  // got new number_prefix
 | 
					 | 
				
			||||||
    if (!await numberPrefixCheck(template, req, res, next)) return;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!_.isEqual(_.pick(templateData, _.keys(template)), template)) {  // data was changed
 | 
					  if (!_.isEqual(_.pick(templateData, _.keys(template)), template)) {  // data was changed
 | 
				
			||||||
    template.version = templateData.version + 1;  // increase version
 | 
					    template.version = templateData.version + 1;  // increase version
 | 
				
			||||||
    await new (model(req))(_.assign({}, _.omit(templateData, ['_id', '__v']), template)).save((err, data) => {  // save new template, fill with old properties
 | 
					    await new (model(req))(_.assign({}, _.omit(templateData, ['_id', '__v']), template)).save((err, data) => {  // save new template, fill with old properties
 | 
				
			||||||
      if (err) next (err);
 | 
					      if (err) next (err);
 | 
				
			||||||
      res.json(TemplateValidate.output(data.toObject(), req.params.collection));
 | 
					      res.json(TemplateValidate.output(data.toObject()));
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  else {
 | 
					  else {
 | 
				
			||||||
    res.json(TemplateValidate.output(templateData, req.params.collection));
 | 
					    res.json(TemplateValidate.output(templateData));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.post('/template/:collection(measurement|treatment)/new', async (req, res, next) => {
 | 
					router.post('/template/:collection(measurement|condition)/new', async (req, res, next) => {
 | 
				
			||||||
  if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
 | 
					  if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const {error, value: template} = TemplateValidate.input(req.body, 'new', req.params.collection);
 | 
					  const {error, value: template} = TemplateValidate.input(req.body, 'new');
 | 
				
			||||||
  if (error) return res400(error, res);
 | 
					  if (error) return res400(error, res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (_.has(template, 'number_prefix')) {  // got number_prefix
 | 
					 | 
				
			||||||
    if (!await numberPrefixCheck(template, req, res, next)) return;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  template.version = 1;  // set template version
 | 
					  template.version = 1;  // set template version
 | 
				
			||||||
  await new (model(req))(template).save((err, data) => {
 | 
					  await new (model(req))(template).save((err, data) => {
 | 
				
			||||||
    if (err) next (err);
 | 
					    if (err) next (err);
 | 
				
			||||||
    res.json(TemplateValidate.output(data.toObject(), req.params.collection));
 | 
					    res.json(TemplateValidate.output(data.toObject()));
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = router;
 | 
					module.exports = router;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
async function numberPrefixCheck (template, req, res, next) {  // check if number_prefix is available
 | 
					 | 
				
			||||||
  const data = await model(req).findOne({number_prefix: template.number_prefix}).lean().exec().catch(err => {next(err); return false;}) as any;
 | 
					 | 
				
			||||||
  if (data) {
 | 
					 | 
				
			||||||
    res.status(400).json({status: 'Number prefix already taken'});
 | 
					 | 
				
			||||||
    return false
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function model (req) {  // return right template model
 | 
					function model (req) {  // return right template model
 | 
				
			||||||
  return req.params.collection === 'treatment' ? TemplateTreatmentModel : TemplateMeasurementModel;
 | 
					  return req.params.collection === 'condition' ? ConditionTemplateModel : MeasurementTemplateModel;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -40,7 +40,6 @@ router.put('/user:username([/](?!key|new).?*|/?)', async (req, res, next) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const username = getUsername(req, res);
 | 
					  const username = getUsername(req, res);
 | 
				
			||||||
  if (!username) return;
 | 
					  if (!username) return;
 | 
				
			||||||
  console.log(username);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const {error, value: user} = UserValidate.input(req.body, 'change' + (req.authDetails.level === 'admin'? 'admin' : ''));
 | 
					  const {error, value: user} = UserValidate.input(req.body, 'change' + (req.authDetails.level === 'admin'? 'admin' : ''));
 | 
				
			||||||
  if (error) return res400(error, res);
 | 
					  if (error) return res400(error, res);
 | 
				
			||||||
@@ -154,8 +153,6 @@ function getUsername (req, res) {  // returns username or false if action is not
 | 
				
			|||||||
async function usernameCheck (name, res, next) {  // check if username is already taken
 | 
					async function usernameCheck (name, res, next) {  // check if username is already taken
 | 
				
			||||||
  const userData = await UserModel.findOne({name: name}).lean().exec().catch(err => next(err)) as any;
 | 
					  const userData = await UserModel.findOne({name: name}).lean().exec().catch(err => next(err)) as any;
 | 
				
			||||||
  if (userData instanceof Error) return false;
 | 
					  if (userData instanceof Error) return false;
 | 
				
			||||||
  console.log(userData);
 | 
					 | 
				
			||||||
  console.log(UserValidate.isSpecialName(name));
 | 
					 | 
				
			||||||
  if (userData || UserValidate.isSpecialName(name)) {
 | 
					  if (userData || UserValidate.isSpecialName(name)) {
 | 
				
			||||||
    res.status(400).json({status: 'Username already taken'});
 | 
					    res.status(400).json({status: 'Username already taken'});
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,50 +0,0 @@
 | 
				
			|||||||
import Joi from '@hapi/joi';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import IdValidate from './id';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class ConditionValidate {
 | 
					 | 
				
			||||||
  private static condition = {
 | 
					 | 
				
			||||||
    number: Joi.string()
 | 
					 | 
				
			||||||
      .max(128),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    parameters: Joi.object()
 | 
					 | 
				
			||||||
      .pattern(/.*/, Joi.alternatives()
 | 
					 | 
				
			||||||
        .try(
 | 
					 | 
				
			||||||
          Joi.string().max(128),
 | 
					 | 
				
			||||||
          Joi.number(),
 | 
					 | 
				
			||||||
          Joi.boolean(),
 | 
					 | 
				
			||||||
          Joi.array()
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static input (data, param) {  // validate input, set param to 'new' to make all attributes required
 | 
					 | 
				
			||||||
    if (param === 'new') {
 | 
					 | 
				
			||||||
      return Joi.object({
 | 
					 | 
				
			||||||
        sample_id: IdValidate.get().required(),
 | 
					 | 
				
			||||||
        parameters: this.condition.parameters.required(),
 | 
					 | 
				
			||||||
        treatment_template: IdValidate.get().required()
 | 
					 | 
				
			||||||
      }).validate(data);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else if (param === 'change') {
 | 
					 | 
				
			||||||
      return Joi.object({
 | 
					 | 
				
			||||||
        parameters: this.condition.parameters
 | 
					 | 
				
			||||||
      }).validate(data);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else {
 | 
					 | 
				
			||||||
      return{error: 'No parameter specified!', value: {}};
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static output (data) {  // validate output and strip unwanted properties, returns null if not valid
 | 
					 | 
				
			||||||
    data = IdValidate.stringify(data);
 | 
					 | 
				
			||||||
    const {value, error} = Joi.object({
 | 
					 | 
				
			||||||
      _id: IdValidate.get(),
 | 
					 | 
				
			||||||
      sample_id: IdValidate.get(),
 | 
					 | 
				
			||||||
      number: this.condition.number,
 | 
					 | 
				
			||||||
      parameters: this.condition.parameters,
 | 
					 | 
				
			||||||
      treatment_template: IdValidate.get()
 | 
					 | 
				
			||||||
    }).validate(data, {stripUnknown: true});
 | 
					 | 
				
			||||||
    return error !== undefined? null : value;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,39 +1,39 @@
 | 
				
			|||||||
import joi from '@hapi/joi';
 | 
					import Joi from '@hapi/joi';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import IdValidate from './id';
 | 
					import IdValidate from './id';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class MaterialValidate {  // validate input for material
 | 
					export default class MaterialValidate {  // validate input for material
 | 
				
			||||||
  private static material = {
 | 
					  private static material = {
 | 
				
			||||||
    name: joi.string()
 | 
					    name: Joi.string()
 | 
				
			||||||
      .max(128),
 | 
					      .max(128),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    supplier: joi.string()
 | 
					    supplier: Joi.string()
 | 
				
			||||||
      .max(128),
 | 
					      .max(128),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    group: joi.string()
 | 
					    group: Joi.string()
 | 
				
			||||||
      .max(128),
 | 
					      .max(128),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mineral: joi.number()
 | 
					    mineral: Joi.number()
 | 
				
			||||||
      .integer()
 | 
					      .integer()
 | 
				
			||||||
      .min(0)
 | 
					      .min(0)
 | 
				
			||||||
      .max(100),
 | 
					      .max(100),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    glass_fiber: joi.number()
 | 
					    glass_fiber: Joi.number()
 | 
				
			||||||
      .integer()
 | 
					      .integer()
 | 
				
			||||||
      .min(0)
 | 
					      .min(0)
 | 
				
			||||||
      .max(100),
 | 
					      .max(100),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    carbon_fiber: joi.number()
 | 
					    carbon_fiber: Joi.number()
 | 
				
			||||||
      .integer()
 | 
					      .integer()
 | 
				
			||||||
      .min(0)
 | 
					      .min(0)
 | 
				
			||||||
      .max(100),
 | 
					      .max(100),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    numbers: joi.array()
 | 
					    numbers: Joi.array()
 | 
				
			||||||
      .items(joi.object({
 | 
					      .items(Joi.object({
 | 
				
			||||||
        color: joi.string()
 | 
					        color: Joi.string()
 | 
				
			||||||
          .max(128)
 | 
					          .max(128)
 | 
				
			||||||
          .required(),
 | 
					          .required(),
 | 
				
			||||||
        number: joi.string()
 | 
					        number: Joi.string()
 | 
				
			||||||
          .max(128)
 | 
					          .max(128)
 | 
				
			||||||
          .allow('')
 | 
					          .allow('')
 | 
				
			||||||
          .required()
 | 
					          .required()
 | 
				
			||||||
@@ -42,7 +42,7 @@ export default class MaterialValidate {  // validate input for material
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  static input (data, param) {  // validate input, set param to 'new' to make all attributes required
 | 
					  static input (data, param) {  // validate input, set param to 'new' to make all attributes required
 | 
				
			||||||
    if (param === 'new') {
 | 
					    if (param === 'new') {
 | 
				
			||||||
      return joi.object({
 | 
					      return Joi.object({
 | 
				
			||||||
        name: this.material.name.required(),
 | 
					        name: this.material.name.required(),
 | 
				
			||||||
        supplier: this.material.supplier.required(),
 | 
					        supplier: this.material.supplier.required(),
 | 
				
			||||||
        group: this.material.group.required(),
 | 
					        group: this.material.group.required(),
 | 
				
			||||||
@@ -53,7 +53,7 @@ export default class MaterialValidate {  // validate input for material
 | 
				
			|||||||
      }).validate(data);
 | 
					      }).validate(data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else if (param === 'change') {
 | 
					    else if (param === 'change') {
 | 
				
			||||||
      return joi.object({
 | 
					      return Joi.object({
 | 
				
			||||||
        name: this.material.name,
 | 
					        name: this.material.name,
 | 
				
			||||||
        supplier: this.material.supplier,
 | 
					        supplier: this.material.supplier,
 | 
				
			||||||
        group: this.material.group,
 | 
					        group: this.material.group,
 | 
				
			||||||
@@ -70,7 +70,7 @@ export default class MaterialValidate {  // validate input for material
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  static output (data) {  // validate output and strip unwanted properties, returns null if not valid
 | 
					  static output (data) {  // validate output and strip unwanted properties, returns null if not valid
 | 
				
			||||||
    data = IdValidate.stringify(data);
 | 
					    data = IdValidate.stringify(data);
 | 
				
			||||||
    const {value, error} = joi.object({
 | 
					    const {value, error} = Joi.object({
 | 
				
			||||||
      _id: IdValidate.get(),
 | 
					      _id: IdValidate.get(),
 | 
				
			||||||
      name: this.material.name,
 | 
					      name: this.material.name,
 | 
				
			||||||
      supplier: this.material.supplier,
 | 
					      supplier: this.material.supplier,
 | 
				
			||||||
@@ -82,4 +82,17 @@ export default class MaterialValidate {  // validate input for material
 | 
				
			|||||||
    }).validate(data, {stripUnknown: true});
 | 
					    }).validate(data, {stripUnknown: true});
 | 
				
			||||||
    return error !== undefined? null : value;
 | 
					    return error !== undefined? null : value;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static outputV() {  // return output validator
 | 
				
			||||||
 | 
					    return Joi.object({
 | 
				
			||||||
 | 
					      _id: IdValidate.get(),
 | 
				
			||||||
 | 
					      name: this.material.name,
 | 
				
			||||||
 | 
					      supplier: this.material.supplier,
 | 
				
			||||||
 | 
					      group: this.material.group,
 | 
				
			||||||
 | 
					      mineral: this.material.mineral,
 | 
				
			||||||
 | 
					      glass_fiber: this.material.glass_fiber,
 | 
				
			||||||
 | 
					      carbon_fiber: this.material.carbon_fiber,
 | 
				
			||||||
 | 
					      numbers: this.material.numbers
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
import Joi from '@hapi/joi';
 | 
					import Joi from '@hapi/joi';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import IdValidate from './id';
 | 
					import IdValidate from './id';
 | 
				
			||||||
 | 
					import UserValidate from './user';
 | 
				
			||||||
 | 
					import MaterialValidate from './material';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class SampleValidate {
 | 
					export default class SampleValidate {
 | 
				
			||||||
  private static sample = {
 | 
					  private static sample = {
 | 
				
			||||||
@@ -17,13 +19,16 @@ export default class SampleValidate {
 | 
				
			|||||||
      .max(128)
 | 
					      .max(128)
 | 
				
			||||||
      .allow(''),
 | 
					      .allow(''),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    condition: Joi.object(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    notes: Joi.object({
 | 
					    notes: Joi.object({
 | 
				
			||||||
      comment: Joi.string()
 | 
					      comment: Joi.string()
 | 
				
			||||||
        .max(512),
 | 
					        .max(512)
 | 
				
			||||||
 | 
					        .allow(''),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      sample_references: Joi.array()
 | 
					      sample_references: Joi.array()
 | 
				
			||||||
        .items(Joi.object({
 | 
					        .items(Joi.object({
 | 
				
			||||||
          id: IdValidate.get(),
 | 
					          sample_id: IdValidate.get(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          relation: Joi.string()
 | 
					          relation: Joi.string()
 | 
				
			||||||
            .max(128)
 | 
					            .max(128)
 | 
				
			||||||
@@ -47,6 +52,7 @@ export default class SampleValidate {
 | 
				
			|||||||
        color: this.sample.color.required(),
 | 
					        color: this.sample.color.required(),
 | 
				
			||||||
        type: this.sample.type.required(),
 | 
					        type: this.sample.type.required(),
 | 
				
			||||||
        batch: this.sample.batch.required(),
 | 
					        batch: this.sample.batch.required(),
 | 
				
			||||||
 | 
					        condition: this.sample.condition.required(),
 | 
				
			||||||
        material_id: IdValidate.get().required(),
 | 
					        material_id: IdValidate.get().required(),
 | 
				
			||||||
        notes: this.sample.notes.required()
 | 
					        notes: this.sample.notes.required()
 | 
				
			||||||
      }).validate(data);
 | 
					      }).validate(data);
 | 
				
			||||||
@@ -56,6 +62,7 @@ export default class SampleValidate {
 | 
				
			|||||||
        color: this.sample.color,
 | 
					        color: this.sample.color,
 | 
				
			||||||
        type: this.sample.type,
 | 
					        type: this.sample.type,
 | 
				
			||||||
        batch: this.sample.batch,
 | 
					        batch: this.sample.batch,
 | 
				
			||||||
 | 
					        condition: this.sample.condition,
 | 
				
			||||||
        material_id: IdValidate.get(),
 | 
					        material_id: IdValidate.get(),
 | 
				
			||||||
        notes: this.sample.notes,
 | 
					        notes: this.sample.notes,
 | 
				
			||||||
      }).validate(data);
 | 
					      }).validate(data);
 | 
				
			||||||
@@ -65,18 +72,39 @@ export default class SampleValidate {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static output (data) {  // validate output and strip unwanted properties, returns null if not valid
 | 
					  static output (data, param = 'refs') {  // validate output and strip unwanted properties, returns null if not valid
 | 
				
			||||||
    data = IdValidate.stringify(data);
 | 
					    data = IdValidate.stringify(data);
 | 
				
			||||||
    const {value, error} = Joi.object({
 | 
					    let joiObject;
 | 
				
			||||||
 | 
					    if (param === 'refs') {
 | 
				
			||||||
 | 
					      joiObject = {
 | 
				
			||||||
        _id: IdValidate.get(),
 | 
					        _id: IdValidate.get(),
 | 
				
			||||||
        number: this.sample.number,
 | 
					        number: this.sample.number,
 | 
				
			||||||
        color: this.sample.color,
 | 
					        color: this.sample.color,
 | 
				
			||||||
        type: this.sample.type,
 | 
					        type: this.sample.type,
 | 
				
			||||||
        batch: this.sample.batch,
 | 
					        batch: this.sample.batch,
 | 
				
			||||||
 | 
					        condition: this.sample.condition,
 | 
				
			||||||
        material_id: IdValidate.get(),
 | 
					        material_id: IdValidate.get(),
 | 
				
			||||||
        note_id: IdValidate.get().allow(null),
 | 
					        note_id: IdValidate.get().allow(null),
 | 
				
			||||||
        user_id: IdValidate.get()
 | 
					        user_id: IdValidate.get()
 | 
				
			||||||
    }).validate(data, {stripUnknown: true});
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if(param === 'details') {
 | 
				
			||||||
 | 
					      joiObject = {
 | 
				
			||||||
 | 
					        _id: IdValidate.get(),
 | 
				
			||||||
 | 
					        number: this.sample.number,
 | 
				
			||||||
 | 
					        color: this.sample.color,
 | 
				
			||||||
 | 
					        type: this.sample.type,
 | 
				
			||||||
 | 
					        batch: this.sample.batch,
 | 
				
			||||||
 | 
					        condition: this.sample.condition,
 | 
				
			||||||
 | 
					        material: MaterialValidate.outputV(),
 | 
				
			||||||
 | 
					        notes: this.sample.notes,
 | 
				
			||||||
 | 
					        user: UserValidate.username()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const {value, error} = Joi.object(joiObject).validate(data, {stripUnknown: true});
 | 
				
			||||||
    return error !== undefined? null : value;
 | 
					    return error !== undefined? null : value;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -9,11 +9,6 @@ export default class TemplateValidate {
 | 
				
			|||||||
    version: Joi.number()
 | 
					    version: Joi.number()
 | 
				
			||||||
      .min(1),
 | 
					      .min(1),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    number_prefix: Joi.string()
 | 
					 | 
				
			||||||
      .pattern(/^[a-zA-Z]+$/)
 | 
					 | 
				
			||||||
      .min(1)
 | 
					 | 
				
			||||||
      .max(16),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    parameters: Joi.array()
 | 
					    parameters: Joi.array()
 | 
				
			||||||
      .min(1)
 | 
					      .min(1)
 | 
				
			||||||
      .items(
 | 
					      .items(
 | 
				
			||||||
@@ -43,63 +38,32 @@ export default class TemplateValidate {
 | 
				
			|||||||
      )
 | 
					      )
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static input (data, param, template) {  // validate input, set param to 'new' to make all attributes required
 | 
					  static input (data, param) {  // validate input, set param to 'new' to make all attributes required
 | 
				
			||||||
    if (param === 'new') {
 | 
					    if (param === 'new') {
 | 
				
			||||||
      if (template === 'treatment') {
 | 
					 | 
				
			||||||
        return Joi.object({
 | 
					 | 
				
			||||||
          name: this.template.name.required(),
 | 
					 | 
				
			||||||
          number_prefix: this.template.number_prefix.required(),
 | 
					 | 
				
			||||||
          parameters: this.template.parameters.required()
 | 
					 | 
				
			||||||
        }).validate(data);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      else {
 | 
					 | 
				
			||||||
      return Joi.object({
 | 
					      return Joi.object({
 | 
				
			||||||
        name: this.template.name.required(),
 | 
					        name: this.template.name.required(),
 | 
				
			||||||
        parameters: this.template.parameters.required()
 | 
					        parameters: this.template.parameters.required()
 | 
				
			||||||
      }).validate(data);
 | 
					      }).validate(data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else if (param === 'change') {
 | 
					    else if (param === 'change') {
 | 
				
			||||||
      if (template === 'treatment') {
 | 
					 | 
				
			||||||
        return Joi.object({
 | 
					 | 
				
			||||||
          name: this.template.name,
 | 
					 | 
				
			||||||
          number_prefix: this.template.number_prefix,
 | 
					 | 
				
			||||||
          parameters: this.template.parameters
 | 
					 | 
				
			||||||
        }).validate(data);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      else {
 | 
					 | 
				
			||||||
      return Joi.object({
 | 
					      return Joi.object({
 | 
				
			||||||
        name: this.template.name,
 | 
					        name: this.template.name,
 | 
				
			||||||
        parameters: this.template.parameters
 | 
					        parameters: this.template.parameters
 | 
				
			||||||
      }).validate(data);
 | 
					      }).validate(data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
      return{error: 'No parameter specified!', value: {}};
 | 
					      return{error: 'No parameter specified!', value: {}};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static output (data, template) {  // validate output and strip unwanted properties, returns null if not valid
 | 
					  static output (data) {  // validate output and strip unwanted properties, returns null if not valid
 | 
				
			||||||
    data = IdValidate.stringify(data);
 | 
					    data = IdValidate.stringify(data);
 | 
				
			||||||
    let joiObject;
 | 
					    const {value, error} = Joi.object({
 | 
				
			||||||
    if (template === 'treatment') {  // differentiate between measurement and treatment (has number_prefix) template
 | 
					 | 
				
			||||||
      joiObject = {
 | 
					 | 
				
			||||||
        _id: IdValidate.get(),
 | 
					 | 
				
			||||||
        name: this.template.name,
 | 
					 | 
				
			||||||
        version: this.template.version,
 | 
					 | 
				
			||||||
        number_prefix: this.template.number_prefix,
 | 
					 | 
				
			||||||
        parameters: this.template.parameters
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else {
 | 
					 | 
				
			||||||
      joiObject = {
 | 
					 | 
				
			||||||
      _id: IdValidate.get(),
 | 
					      _id: IdValidate.get(),
 | 
				
			||||||
      name: this.template.name,
 | 
					      name: this.template.name,
 | 
				
			||||||
      version: this.template.version,
 | 
					      version: this.template.version,
 | 
				
			||||||
      parameters: this.template.parameters
 | 
					      parameters: this.template.parameters
 | 
				
			||||||
      };
 | 
					    }).validate(data, {stripUnknown: true});
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const {value, error} = Joi.object(joiObject).validate(data, {stripUnknown: true});
 | 
					 | 
				
			||||||
    return error !== undefined? null : value;
 | 
					    return error !== undefined? null : value;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -84,4 +84,8 @@ export default class UserValidate {  // validate input for user
 | 
				
			|||||||
  static isSpecialName (name) {  // true if name belongs to special names
 | 
					  static isSpecialName (name) {  // true if name belongs to special names
 | 
				
			||||||
    return this.specialUsernames.indexOf(name) > -1;
 | 
					    return this.specialUsernames.indexOf(name) > -1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static username() {
 | 
				
			||||||
 | 
					    return this.user.name;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										110
									
								
								src/test/db.json
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								src/test/db.json
									
									
									
									
									
								
							@@ -7,6 +7,11 @@
 | 
				
			|||||||
        "type": "granulate",
 | 
					        "type": "granulate",
 | 
				
			||||||
        "color": "black",
 | 
					        "color": "black",
 | 
				
			||||||
        "batch": "",
 | 
					        "batch": "",
 | 
				
			||||||
 | 
					        "condition": {
 | 
				
			||||||
 | 
					          "material": "copper",
 | 
				
			||||||
 | 
					          "weeks": 3,
 | 
				
			||||||
 | 
					          "condition_template": {"$oid":"200000000000000000000001"}
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "material_id": {"$oid":"100000000000000000000004"},
 | 
					        "material_id": {"$oid":"100000000000000000000004"},
 | 
				
			||||||
        "note_id": null,
 | 
					        "note_id": null,
 | 
				
			||||||
        "user_id": {"$oid":"000000000000000000000002"},
 | 
					        "user_id": {"$oid":"000000000000000000000002"},
 | 
				
			||||||
@@ -19,6 +24,11 @@
 | 
				
			|||||||
        "type": "granulate",
 | 
					        "type": "granulate",
 | 
				
			||||||
        "color": "natural",
 | 
					        "color": "natural",
 | 
				
			||||||
        "batch": "1560237365",
 | 
					        "batch": "1560237365",
 | 
				
			||||||
 | 
					        "condition": {
 | 
				
			||||||
 | 
					          "material": "copper",
 | 
				
			||||||
 | 
					          "weeks": 3,
 | 
				
			||||||
 | 
					          "condition_template": {"$oid":"200000000000000000000001"}
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "material_id": {"$oid":"100000000000000000000001"},
 | 
					        "material_id": {"$oid":"100000000000000000000001"},
 | 
				
			||||||
        "note_id": {"$oid":"500000000000000000000001"},
 | 
					        "note_id": {"$oid":"500000000000000000000001"},
 | 
				
			||||||
        "user_id": {"$oid":"000000000000000000000002"},
 | 
					        "user_id": {"$oid":"000000000000000000000002"},
 | 
				
			||||||
@@ -31,6 +41,11 @@
 | 
				
			|||||||
        "type": "part",
 | 
					        "type": "part",
 | 
				
			||||||
        "color": "black",
 | 
					        "color": "black",
 | 
				
			||||||
        "batch": "1704-005",
 | 
					        "batch": "1704-005",
 | 
				
			||||||
 | 
					        "condition": {
 | 
				
			||||||
 | 
					          "material": "copper",
 | 
				
			||||||
 | 
					          "weeks": 3,
 | 
				
			||||||
 | 
					          "condition_template": {"$oid":"200000000000000000000001"}
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "material_id": {"$oid":"100000000000000000000005"},
 | 
					        "material_id": {"$oid":"100000000000000000000005"},
 | 
				
			||||||
        "note_id": {"$oid":"500000000000000000000002"},
 | 
					        "note_id": {"$oid":"500000000000000000000002"},
 | 
				
			||||||
        "user_id": {"$oid":"000000000000000000000003"},
 | 
					        "user_id": {"$oid":"000000000000000000000003"},
 | 
				
			||||||
@@ -43,6 +58,11 @@
 | 
				
			|||||||
        "type": "granulate",
 | 
					        "type": "granulate",
 | 
				
			||||||
        "color": "black",
 | 
					        "color": "black",
 | 
				
			||||||
        "batch": "1653000308",
 | 
					        "batch": "1653000308",
 | 
				
			||||||
 | 
					        "condition": {
 | 
				
			||||||
 | 
					          "material": "hot air",
 | 
				
			||||||
 | 
					          "weeks": 5,
 | 
				
			||||||
 | 
					          "condition_template": {"$oid":"200000000000000000000001"}
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "material_id": {"$oid":"100000000000000000000005"},
 | 
					        "material_id": {"$oid":"100000000000000000000005"},
 | 
				
			||||||
        "note_id": {"$oid":"500000000000000000000003"},
 | 
					        "note_id": {"$oid":"500000000000000000000003"},
 | 
				
			||||||
        "user_id": {"$oid":"000000000000000000000003"},
 | 
					        "user_id": {"$oid":"000000000000000000000003"},
 | 
				
			||||||
@@ -55,11 +75,27 @@
 | 
				
			|||||||
        "type": "granulate",
 | 
					        "type": "granulate",
 | 
				
			||||||
        "color": "black",
 | 
					        "color": "black",
 | 
				
			||||||
        "batch": "1653000308",
 | 
					        "batch": "1653000308",
 | 
				
			||||||
 | 
					        "condition": {
 | 
				
			||||||
 | 
					          "condition_template": {"$oid":"200000000000000000000003"}
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "material_id": {"$oid":"100000000000000000000005"},
 | 
					        "material_id": {"$oid":"100000000000000000000005"},
 | 
				
			||||||
        "note_id": {"$oid":"500000000000000000000003"},
 | 
					        "note_id": null,
 | 
				
			||||||
        "user_id": {"$oid":"000000000000000000000003"},
 | 
					        "user_id": {"$oid":"000000000000000000000003"},
 | 
				
			||||||
        "status": -1,
 | 
					        "status": -1,
 | 
				
			||||||
        "__v": 0
 | 
					        "__v": 0
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "_id": {"$oid":"400000000000000000000006"},
 | 
				
			||||||
 | 
					        "number": "Rng36",
 | 
				
			||||||
 | 
					        "type": "granulate",
 | 
				
			||||||
 | 
					        "color": "black",
 | 
				
			||||||
 | 
					        "batch": "",
 | 
				
			||||||
 | 
					        "condition": {},
 | 
				
			||||||
 | 
					        "material_id": {"$oid":"100000000000000000000004"},
 | 
				
			||||||
 | 
					        "note_id": null,
 | 
				
			||||||
 | 
					        "user_id": {"$oid":"000000000000000000000002"},
 | 
				
			||||||
 | 
					        "status": 0,
 | 
				
			||||||
 | 
					        "__v": 0
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "notes": [
 | 
					    "notes": [
 | 
				
			||||||
@@ -73,7 +109,7 @@
 | 
				
			|||||||
        "_id": {"$oid":"500000000000000000000002"},
 | 
					        "_id": {"$oid":"500000000000000000000002"},
 | 
				
			||||||
        "comment": "",
 | 
					        "comment": "",
 | 
				
			||||||
        "sample_references": [{
 | 
					        "sample_references": [{
 | 
				
			||||||
          "id": {"$oid":"400000000000000000000004"},
 | 
					          "sample_id": {"$oid":"400000000000000000000004"},
 | 
				
			||||||
          "relation": "granulate to sample"
 | 
					          "relation": "granulate to sample"
 | 
				
			||||||
        }],
 | 
					        }],
 | 
				
			||||||
        "custom_fields": {
 | 
					        "custom_fields": {
 | 
				
			||||||
@@ -85,7 +121,7 @@
 | 
				
			|||||||
        "_id": {"$oid":"500000000000000000000003"},
 | 
					        "_id": {"$oid":"500000000000000000000003"},
 | 
				
			||||||
        "comment": "",
 | 
					        "comment": "",
 | 
				
			||||||
        "sample_references": [{
 | 
					        "sample_references": [{
 | 
				
			||||||
          "id": {"$oid":"400000000000000000000003"},
 | 
					          "sample_id": {"$oid":"400000000000000000000003"},
 | 
				
			||||||
          "relation": "part to sample"
 | 
					          "relation": "part to sample"
 | 
				
			||||||
        }],
 | 
					        }],
 | 
				
			||||||
        "custom_fields": {
 | 
					        "custom_fields": {
 | 
				
			||||||
@@ -234,60 +270,10 @@
 | 
				
			|||||||
        "__v": 0
 | 
					        "__v": 0
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "conditions": [
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        "_id": {"$oid":"700000000000000000000001"},
 | 
					 | 
				
			||||||
        "sample_id": {"$oid":"400000000000000000000001"},
 | 
					 | 
				
			||||||
        "number": "A1",
 | 
					 | 
				
			||||||
        "parameters": {
 | 
					 | 
				
			||||||
          "material": "copper",
 | 
					 | 
				
			||||||
          "weeks": 3
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "treatment_template": {"$oid":"200000000000000000000001"},
 | 
					 | 
				
			||||||
        "status": 10,
 | 
					 | 
				
			||||||
        "__v": 0
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        "_id": {"$oid":"700000000000000000000002"},
 | 
					 | 
				
			||||||
        "sample_id": {"$oid":"400000000000000000000002"},
 | 
					 | 
				
			||||||
        "number": "A1",
 | 
					 | 
				
			||||||
        "parameters": {
 | 
					 | 
				
			||||||
          "material": "copper",
 | 
					 | 
				
			||||||
          "weeks": 3
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "treatment_template": {"$oid":"200000000000000000000001"},
 | 
					 | 
				
			||||||
        "status": 10,
 | 
					 | 
				
			||||||
        "__v": 0
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        "_id": {"$oid":"700000000000000000000003"},
 | 
					 | 
				
			||||||
        "sample_id": {"$oid":"400000000000000000000004"},
 | 
					 | 
				
			||||||
        "number": "A1",
 | 
					 | 
				
			||||||
        "parameters": {
 | 
					 | 
				
			||||||
          "material": "copper",
 | 
					 | 
				
			||||||
          "weeks": 3
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "treatment_template": {"$oid":"200000000000000000000001"},
 | 
					 | 
				
			||||||
        "status": 10,
 | 
					 | 
				
			||||||
        "__v": 0
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        "_id": {"$oid":"700000000000000000000004"},
 | 
					 | 
				
			||||||
        "sample_id": {"$oid":"400000000000000000000001"},
 | 
					 | 
				
			||||||
        "number": "A2",
 | 
					 | 
				
			||||||
        "parameters": {
 | 
					 | 
				
			||||||
          "material": "hot air",
 | 
					 | 
				
			||||||
          "weeks": 5
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "treatment_template": {"$oid":"200000000000000000000001"},
 | 
					 | 
				
			||||||
        "status": 10,
 | 
					 | 
				
			||||||
        "__v": 0
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    "measurements": [
 | 
					    "measurements": [
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        "_id": {"$oid":"800000000000000000000001"},
 | 
					        "_id": {"$oid":"800000000000000000000001"},
 | 
				
			||||||
        "condition_id": {"$oid":"700000000000000000000001"},
 | 
					        "sample_id": {"$oid":"400000000000000000000001"},
 | 
				
			||||||
        "values": {
 | 
					        "values": {
 | 
				
			||||||
          "dpt": [
 | 
					          "dpt": [
 | 
				
			||||||
            [3997.12558,98.00555],
 | 
					            [3997.12558,98.00555],
 | 
				
			||||||
@@ -301,7 +287,7 @@
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        "_id": {"$oid":"800000000000000000000002"},
 | 
					        "_id": {"$oid":"800000000000000000000002"},
 | 
				
			||||||
        "condition_id": {"$oid":"700000000000000000000002"},
 | 
					        "sample_id": {"$oid":"400000000000000000000002"},
 | 
				
			||||||
        "values": {
 | 
					        "values": {
 | 
				
			||||||
          "weight %": 0.5,
 | 
					          "weight %": 0.5,
 | 
				
			||||||
          "standard deviation": 0.2
 | 
					          "standard deviation": 0.2
 | 
				
			||||||
@@ -312,7 +298,7 @@
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        "_id": {"$oid":"800000000000000000000003"},
 | 
					        "_id": {"$oid":"800000000000000000000003"},
 | 
				
			||||||
        "condition_id": {"$oid":"700000000000000000000003"},
 | 
					        "sample_id": {"$oid":"400000000000000000000003"},
 | 
				
			||||||
        "values": {
 | 
					        "values": {
 | 
				
			||||||
          "val1": 1
 | 
					          "val1": 1
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@@ -321,12 +307,11 @@
 | 
				
			|||||||
        "__v": 0
 | 
					        "__v": 0
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "treatment_templates": [
 | 
					    "condition_templates": [
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        "_id": {"$oid":"200000000000000000000001"},
 | 
					        "_id": {"$oid":"200000000000000000000001"},
 | 
				
			||||||
        "name": "heat treatment",
 | 
					        "name": "heat treatment",
 | 
				
			||||||
        "version": 1,
 | 
					        "version": 1,
 | 
				
			||||||
        "number_prefix": "A",
 | 
					 | 
				
			||||||
        "parameters": [
 | 
					        "parameters": [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            "name": "material",
 | 
					            "name": "material",
 | 
				
			||||||
@@ -351,7 +336,6 @@
 | 
				
			|||||||
        "_id": {"$oid":"200000000000000000000002"},
 | 
					        "_id": {"$oid":"200000000000000000000002"},
 | 
				
			||||||
        "name": "heat treatment 2",
 | 
					        "name": "heat treatment 2",
 | 
				
			||||||
        "version": 2,
 | 
					        "version": 2,
 | 
				
			||||||
        "number_prefix": "B",
 | 
					 | 
				
			||||||
        "parameters": [
 | 
					        "parameters": [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            "name": "material",
 | 
					            "name": "material",
 | 
				
			||||||
@@ -359,6 +343,14 @@
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "__v": 0
 | 
					        "__v": 0
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "_id": {"$oid":"200000000000000000000003"},
 | 
				
			||||||
 | 
					        "name": "raw material",
 | 
				
			||||||
 | 
					        "version": 1,
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "__v": 0
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "measurement_templates": [
 | 
					    "measurement_templates": [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ export default class TestHelper {
 | 
				
			|||||||
    user: {pass: 'Xyz890*)', key: '000000000000000000001001'},
 | 
					    user: {pass: 'Xyz890*)', key: '000000000000000000001001'},
 | 
				
			||||||
    johnnydoe: {pass: 'Xyz890*)', key: '000000000000000000001004'}
 | 
					    johnnydoe: {pass: 'Xyz890*)', key: '000000000000000000001004'}
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static res = {  // default responses
 | 
					  public static res = {  // default responses
 | 
				
			||||||
    400: {status: 'Bad request'},
 | 
					    400: {status: 'Bad request'},
 | 
				
			||||||
    401: {status: 'Unauthorized'},
 | 
					    401: {status: 'Unauthorized'},
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user