Parcourir la source

Added nearestQuality parameter + test + doc

rigwild il y a 5 ans
Parent
commit
ce2c0fb44c
4 fichiers modifiés avec 137 ajouts et 33 suppressions
  1. 45 11
      server/routes/getImage.js
  2. 32 10
      server/routes/getImageExtracts.js
  3. 28 4
      test/api/getImage.js
  4. 32 8
      test/api/getImageExtracts.js

+ 45 - 11
server/routes/getImage.js

@@ -10,7 +10,7 @@ import { asyncMiddleware, checkSceneName, checkRequiredParameters, getSceneFiles
 const router = express.Router()
 
 /**
- * @api {get} /getImage?sceneName=:sceneName&imageQuality=:imageQuality Get an image from a scene
+ * @api {get} /getImage?sceneName=:sceneName&imageQuality=:imageQuality&nearestQuality=:nearestQuality Get an image from a scene
  * @apiVersion 0.1.0
  * @apiName GetImage
  * @apiGroup API
@@ -19,6 +19,7 @@ const router = express.Router()
  *
  * @apiParam {String} sceneName The selected scene
  * @apiParam {Number|"min"|"max"|"median"} imageQuality The required quality of the image
+ * @apiParam {Boolean} [nearestQuality=false] if selected quality not availabie, select the nearest one
  *
  * @apiExample Usage example
  * curl -i -L -X GET "http://diran.univ-littoral.fr/api/getImage?sceneName=bathroom&imageQuality=200"
@@ -27,7 +28,13 @@ const router = express.Router()
  * @apiSuccessExample {json} Success response example
  * HTTP/1.1 200 OK /api/getImage?sceneName=bathroom&imageQuality=200
  * {
- *   "data": "/api/images/bathroom/bathroom_00200.png"
+ *   "data": {
+ *     "link": "/api/images/bathroom/bathroom_00200.png",
+ *     "fileName": "bathroom_00200.png",
+ *     "sceneName": "bathroom",
+ *     "quality": 200,
+ *     "ext": "png"
+ *   }
  * }
  *
  * @apiError (Error 4xx) 400_[1] Missing parameter(s)
@@ -44,7 +51,8 @@ const router = express.Router()
  *   "message": "Invalid query parameter(s).",
  *   "data": [
  *     "The requested scene name \".//../\" is not valid.",
- *     "The specified quality is not an integer."
+ *     "The specified quality is not an integer.",
+ *     "Impossible to use \"min\", \"max\" or \"median\" with \"nearestQuality\" on."
  *   ]
  * }
  *
@@ -89,25 +97,46 @@ const router = express.Router()
  * Get the link and path to an image
  * @param {string} sceneName the scene to get the image from
  * @param {number|"min"|"max"|"median"} quality the requested quality
+ * @param {boolean} [nearestQuality=false] if selected quality not availabie, select the nearest one
  * @returns {Promise<Image>} the link and path to the image
  */
-export const getImage = async (sceneName, quality) => {
+export const getImage = async (sceneName, quality, nearestQuality = false) => {
+  const throwErrIfTrue = x => {
+    if (x) throw boom.badRequest('Impossible to use "min", "max" or "median" with "nearestQuality" on.')
+  }
   const sceneData = await getSceneFilesData(sceneName)
 
   let imageData = null
   // Search an image with the requested quality in the scene
   if (quality === 'min') {
+    throwErrIfTrue(nearestQuality)
     const toFind = Math.min(...sceneData.map(x => x.quality))
     imageData = sceneData.find(x => x.quality === toFind)
   }
   else if (quality === 'max') {
+    throwErrIfTrue(nearestQuality)
     const toFind = Math.max(...sceneData.map(x => x.quality))
     imageData = sceneData.find(x => x.quality === toFind)
   }
-  else if (quality === 'median')
+  else if (quality === 'median') {
+    throwErrIfTrue(nearestQuality)
     imageData = sceneData.length > 0 ? sceneData[Math.ceil(sceneData.length / 2) - 1] : null
-  else
-    imageData = sceneData.find(x => quality === x.quality)
+  }
+  else {
+    if (nearestQuality && sceneData.length > 0 && !isNaN(parseInt(quality, 10))) {
+      let minGap = Number.MAX_SAFE_INTEGER
+      let minGapImageData = null
+      for (const x of sceneData) {
+        const tempGap = Math.abs(x.quality - quality)
+        if (tempGap < minGap) {
+          minGap = tempGap
+          minGapImageData = x
+        }
+      }
+      imageData = minGapImageData
+    }
+    else imageData = sceneData.find(x => quality === x.quality)
+  }
 
   if (imageData)
     return {
@@ -128,6 +157,7 @@ router.get('/', asyncMiddleware(async (req, res) => {
   checkRequiredParameters(['sceneName', 'imageQuality'], req.query)
 
   const { sceneName, imageQuality } = req.query
+  const nearestQuality = req.query.nearestQuality === 'true'
 
   let errorList = []
 
@@ -142,8 +172,11 @@ router.get('/', asyncMiddleware(async (req, res) => {
   // Check `imageQuality` is an integer or `min`, `max` or `median`
   const qualityInt = parseInt(imageQuality, 10)
   let quality = null
-  if (['min', 'median', 'max'].some(x => x === imageQuality))
-    quality = imageQuality
+  if (['min', 'median', 'max'].some(x => x === imageQuality)) {
+    if (nearestQuality)
+      errorList.push('Impossible to use "min", "max" or "median" with "nearestQuality" on.')
+    else quality = imageQuality
+  }
   else if (!isNaN(qualityInt))
     quality = qualityInt
   else
@@ -153,8 +186,9 @@ router.get('/', asyncMiddleware(async (req, res) => {
   if (errorList.length > 0)
     throw boom.badRequest('Invalid query parameter(s).', errorList)
 
-  const { link } = await getImage(sceneName, quality)
-  res.json({ data: link })
+  const data = await getImage(sceneName, quality, nearestQuality)
+  data.path = undefined
+  res.json({ data })
 }))
 
 export default router

+ 32 - 10
server/routes/getImageExtracts.js

@@ -13,7 +13,7 @@ import { getImage } from './getImage'
 const router = express.Router()
 
 /**
- * @api {get} /getImageExtracts?sceneName=:sceneName&imageQuality=:imageQuality&horizontalExtractCount=:horizontalExtractCount&verticalExtractCount=:verticalExtractCount Get image extracts
+ * @api {get} /getImageExtracts?sceneName=:sceneName&imageQuality=:imageQuality&horizontalExtractCount=:horizontalExtractCount&verticalExtractCount=:verticalExtractCount&nearestQuality=:nearestQuality Get image extracts
  * @apiVersion 0.1.0
  * @apiName GetImageExtracts
  * @apiGroup API
@@ -24,6 +24,7 @@ const router = express.Router()
  * @apiParam {Number|"min"|"max"|"median"} imageQuality The required quality of the image
  * @apiParam {Number} horizontalExtractCount The amount of extracts for the horizontal axis
  * @apiParam {Number} verticalExtractCount The amount of extracts for the vertical axis
+ * @apiParam {Boolean} [nearestQuality=false] if selected quality not availabie, select the nearest one
  *
  * @apiExample Usage example
  * curl -i -L -X GET "http://diran.univ-littoral.fr/api/getImageExtracts?sceneName=bathroom&imageQuality=200&horizontalExtractCount=1&verticalExtractCount=2"
@@ -32,10 +33,19 @@ const router = express.Router()
  * @apiSuccessExample {json} Success response example
  * HTTP/1.1 200 OK /api/getImageExtracts?sceneName=bathroom&imageQuality=200&horizontalExtractCount=1&verticalExtractCount=2
  * {
- *   "data": [
- *     "/api/images/bathroom/extracts/x1_y2/zone00001/bathroom_zone00001_200.png",
- *     "/api/images/bathroom/extracts/x1_y2/zone00002/bathroom_zone00002_200.png"
- *   ]
+ *   "data": {
+ *     extracts: [
+ *       "/api/images/bathroom/extracts/x1_y2/zone00001/bathroom_zone00001_200.png",
+ *       "/api/images/bathroom/extracts/x1_y2/zone00002/bathroom_zone00002_200.png"
+ *     ],
+ *     "info": {
+ *       "link": "/api/images/bathroom/bathroom_00200.png",
+ *       "fileName": "bathroom_00200.png",
+ *       "sceneName": "bathroom",
+ *       "quality": 200,
+ *       "ext": "png"
+ *     }
+ *   }
  * }
  *
  * @apiError (Error 4xx) 400_[1] Missing parameter(s)
@@ -54,7 +64,8 @@ const router = express.Router()
  *     "The requested scene name \".//../\" is not valid.",
  *     "The specified quality is not an integer.",
  *     "The specified number of extract for the horizontal axis is not an integer.",
- *     "The specified number of extract for the vertical axis is not an integer."
+ *     "The specified number of extract for the vertical axis is not an integer.",
+ *     "Impossible to use \"min\", \"max\" or \"median\" with \"nearestQuality\" on."
  *   ]
  * }
  *
@@ -205,6 +216,7 @@ router.get('/', asyncMiddleware(async (req, res) => {
   checkRequiredParameters(['sceneName', 'imageQuality', 'horizontalExtractCount', 'verticalExtractCount'], req.query)
 
   const { sceneName, imageQuality, horizontalExtractCount, verticalExtractCount } = req.query
+  const nearestQuality = req.query.nearestQuality === 'true'
 
   let errorList = []
 
@@ -219,8 +231,11 @@ router.get('/', asyncMiddleware(async (req, res) => {
   // Check `imageQuality` is an integer or `min`, `max` or `median`
   const qualityInt = parseInt(imageQuality, 10)
   let quality = null
-  if (['min', 'median', 'max'].some(x => x === imageQuality))
-    quality = imageQuality
+  if (['min', 'median', 'max'].some(x => x === imageQuality)) {
+    if (nearestQuality)
+      errorList.push('Impossible to use "min", "max" or "median" with "nearestQuality" on.')
+    else quality = imageQuality
+  }
   else if (!isNaN(qualityInt))
     quality = qualityInt
   else
@@ -240,13 +255,20 @@ router.get('/', asyncMiddleware(async (req, res) => {
     throw boom.badRequest('Invalid query parameter(s).', errorList)
 
   // Get the image path and link
-  const image = await getImage(sceneName, quality)
+  const image = await getImage(sceneName, quality, nearestQuality)
 
   // Cut the image
   const extracts = await cutImage(image, horizontalExtractCountInt, verticalExtractCountInt)
 
+  image.path = undefined
+
   // Send an array of links
-  res.json({ data: extracts.map(x => x.link) })
+  res.json({
+    data: {
+      extracts: extracts.map(x => x.link),
+      info: image
+    }
+  })
 }))
 
 export default router

+ 28 - 4
test/api/getImage.js

@@ -30,6 +30,15 @@ test('GET /getImage?sceneName=invalid/../scene&imageQuality=aaaa', async t => {
   t.truthy(res.body.data.find(x => x.includes('The specified quality is not an integer')), json(res.body))
 })
 
+test('GET /getImage?sceneName=bathroom&imageQuality=max&nearestQuality=true', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImage?sceneName=bathroom&imageQuality=max&nearestQuality=true`)
+
+  t.is(res.status, 400, json(res))
+  t.true(res.body.message.includes('Invalid query parameter'), json(res.body))
+  t.truthy(res.body.data.find(x => x.match(/Impossible to use.*min.*max.*median.*with.*nearestQuality/)), json(res.body))
+})
+
 test('GET /getImage?sceneName=unknown-scene-name&imageQuality=10', async t => {
   const res = await request(t.context.server)
     .get(`${apiPrefix}/getImage?sceneName=unknown-scene-name&imageQuality=10`)
@@ -51,7 +60,7 @@ test('GET /getImage?sceneName=bathroom&imageQuality=min', async t => {
     .get(`${apiPrefix}/getImage?sceneName=bathroom&imageQuality=min`)
 
   t.is(res.status, 200, json(res))
-  t.is(res.body.data, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
+  t.is(res.body.data.link, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
 })
 
 test('GET /getImage?sceneName=bathroom&imageQuality=median', async t => {
@@ -59,7 +68,7 @@ test('GET /getImage?sceneName=bathroom&imageQuality=median', async t => {
     .get(`${apiPrefix}/getImage?sceneName=bathroom&imageQuality=median`)
 
   t.is(res.status, 200, json(res))
-  t.is(res.body.data, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
+  t.is(res.body.data.link, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
 })
 
 test('GET /getImage?sceneName=bathroom&imageQuality=max', async t => {
@@ -67,7 +76,15 @@ test('GET /getImage?sceneName=bathroom&imageQuality=max', async t => {
     .get(`${apiPrefix}/getImage?sceneName=bathroom&imageQuality=max`)
 
   t.is(res.status, 200, json(res))
-  t.is(res.body.data, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
+  t.is(res.body.data.link, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
+})
+
+test('GET /getImage?sceneName=bathroom&imageQuality=99999&nearestQuality=true', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImage?sceneName=bathroom&imageQuality=99999&nearestQuality=true`)
+
+  t.is(res.status, 200, json(res))
+  t.is(res.body.data.link, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
 })
 
 test('GET /getImage?sceneName=bathroom&imageQuality=10', async t => {
@@ -75,7 +92,14 @@ test('GET /getImage?sceneName=bathroom&imageQuality=10', async t => {
     .get(`${apiPrefix}/getImage?sceneName=bathroom&imageQuality=10`)
 
   t.is(res.status, 200, json(res))
-  t.is(res.body.data, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
+  t.is(res.body.data.link, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
+  t.deepEqual(res.body.data, {
+    'link': `${imageServedUrl}/bathroom/bathroom_00010.png`,
+    'fileName': 'bathroom_00010.png',
+    'sceneName': 'bathroom',
+    'quality': 10,
+    'ext': 'png'
+  }, json(res.body))
 
   // Check link is accessible and is an image
   const res2 = await request(t.context.server)

+ 32 - 8
test/api/getImageExtracts.js

@@ -37,6 +37,15 @@ test('GET /getImageExtracts?sceneName=/../&imageQuality=a&horizontalExtractCount
   t.truthy(res.body.data.find(x => x.includes('vertical axis is not an integer')), json(res.body))
 })
 
+test('GET /getImageExtracts?sceneName=bathroom&horizontalExtractCount=5&verticalExtractCount=2&imageQuality=max&nearestQuality=true', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&horizontalExtractCount=5&verticalExtractCount=2&imageQuality=max&nearestQuality=true`)
+
+  t.is(res.status, 400, json(res))
+  t.true(res.body.message.includes('Invalid query parameter'), json(res.body))
+  t.truthy(res.body.data.find(x => x.match(/Impossible to use.*min.*max.*median.*with.*nearestQuality/)), json(res.body))
+})
+
 test('GET /getImageExtracts?sceneName=unknown-scene-name&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2', async t => {
   const res = await request(t.context.server)
     .get(`${apiPrefix}/getImageExtracts?sceneName=unknown-scene-name&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2`)
@@ -68,8 +77,8 @@ test('GET /getImageExtracts?sceneName=bathroom&imageQuality=min&horizontalExtrac
     .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&imageQuality=min&horizontalExtractCount=5&verticalExtractCount=2`)
 
   t.is(res.status, 200, json(res))
-  t.true(Array.isArray(res.body.data), json(res.body))
-  t.is(res.body.data[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
+  t.true(Array.isArray(res.body.data.extracts), json(res.body))
+  t.is(res.body.data.extracts[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
 })
 
 test('GET /getImageExtracts?sceneName=bathroom&imageQuality=median&horizontalExtractCount=5&verticalExtractCount=2', async t => {
@@ -77,8 +86,8 @@ test('GET /getImageExtracts?sceneName=bathroom&imageQuality=median&horizontalExt
     .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&imageQuality=median&horizontalExtractCount=5&verticalExtractCount=2`)
 
   t.is(res.status, 200, json(res))
-  t.true(Array.isArray(res.body.data), json(res.body))
-  t.is(res.body.data[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
+  t.true(Array.isArray(res.body.data.extracts), json(res.body))
+  t.is(res.body.data.extracts[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
 })
 
 test('GET /getImageExtracts?sceneName=bathroom&imageQuality=max&horizontalExtractCount=5&verticalExtractCount=2', async t => {
@@ -86,8 +95,16 @@ test('GET /getImageExtracts?sceneName=bathroom&imageQuality=max&horizontalExtrac
     .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&imageQuality=max&horizontalExtractCount=5&verticalExtractCount=2`)
 
   t.is(res.status, 200, json(res))
-  t.true(Array.isArray(res.body.data), json(res.body))
-  t.is(res.body.data[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
+  t.true(Array.isArray(res.body.data.extracts), json(res.body))
+  t.is(res.body.data.extracts[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
+})
+
+test('GET /getImageExtracts?sceneName=bathroom&imageQuality=99999&horizontalExtractCount=5&verticalExtractCount=2&nearestQuality=true', async t => {
+  const res = await request(t.context.server)
+    .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&imageQuality=99999&horizontalExtractCount=5&verticalExtractCount=2&nearestQuality=true`)
+
+  t.is(res.status, 200, json(res))
+  t.is(res.body.data.extracts[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
 })
 
 test.serial('GET /getImageExtracts?sceneName=bathroom&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2', async t => {
@@ -95,8 +112,15 @@ test.serial('GET /getImageExtracts?sceneName=bathroom&imageQuality=10&horizontal
     .get(`${apiPrefix}/getImageExtracts?sceneName=bathroom&imageQuality=10&horizontalExtractCount=5&verticalExtractCount=2`)
 
   t.is(res.status, 200, json(res))
-  t.true(Array.isArray(res.body.data), json(res.body))
-  t.is(res.body.data[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
+  t.true(Array.isArray(res.body.data.extracts), json(res.body))
+  t.is(res.body.data.extracts[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
+  t.deepEqual(res.body.data.info, {
+    link: `${imageServedUrl}/bathroom/bathroom_00010.png`,
+    fileName: 'bathroom_00010.png',
+    sceneName: 'bathroom',
+    quality: 10,
+    ext: 'png'
+  }, json(res.body))
 
   // Check link is accessible and is an image
   const res2 = await request(t.context.server)