<commit_before>/**
 * Mountains
 */
var colorPalate = 'mountains';

Components.remove({ 'colorPalate': colorPalate });
Pages.remove({ 'colorPalate': colorPalate });

if (Components.find( { 'colorPalate': colorPalate } ).count() === 0) {

    /**
     **************************************************************************
     * Mountains
     **************************************************************************
     */
    var mountains = Components.insert({
        colorPalate: colorPalate,
        link: 'mountains',
        title: 'The Cuyamaca Mountains',
        titleEsp: 'Las montañas Cuyamaca',
        componentNum: '0816',
        order: 7,
        mural: 'mtn-mural.jpg'
    });

    Pages.insert({
        colorPalate: colorPalate,
        componentId: mountains,
        link: 'where-is-it',
        buttonTitle: 'Where is it?',
        espButtonTitle: '¿Dónde está?',
        order: 1,
        bodyCopy: 'The Cuyamaca Mountains, the setting for the scene in front of you, are just 50 miles from the Museum',
        espBodyCopy: 'Las montañas Cuyamaca, el marco de la escena delante de ti, están apenas a 50 millas del  Museo. ',
        backgroundMapClass: 'map-mtn-where',
        bodyImages: [
            {fileName: 'mtn-where.jpg'},
        ],
    });

    //Pages.insert({
        //colorPalate: colorPalate,
        //componentId: mountains,
        //link: 'our-mountains',
        //buttonTitle: 'Our mountains',
        //espButtonTitle: 'Nuestras montañas',
        //order: 2,
        //bodyCopy: 'Southern California has several hundred mountain peaks, including San Gorgonio Mountain, which rises to 11,503 feet.',
        //espBodyCopy: 'El sur de California tiene varios centenares de cumbres, incluyendo la montaña San Gorgonio, que tiene una elevación de 11.503 pies.',
        //backgroundMapClass: 'map-mtn-our',
    //});

    Pages.insert({
        colorPalate: colorPalate,
        componentId: mountains,
        link: 'rare-habitat',
        buttonTitle: 'Rare habitat',
        espButtonTitle: 'Hábitat inusual',
        order: 4,
        bodyCopyLayout: 'horizontal',
        bodyCopy: '
Our highest mountain peaks—the coolest and wettest areas of southern California—are home to some of the last conifer forests in the region.

Move the slider to compare southern California elevations to conifer forest locations.
',
        espBodyCopy: '
Nuestras cumbres más elevadas—las zonas más frescas y húmedas del sur de California—albergan algunos de los últimos bosques de coníferas en la región.

Mueva el control deslizante para comparar las elevaciones del sur de California a coníferas lugares forestales.
',
        backgroundMapClass: 'map-wet-then-now',
        bottomImage: 'mtn-compare-elevation-01-v03.png',
        bottomImageLabel: 'Low elevation',
        topImage: 'mtn-compare-elevation-02-v03.png',
        topImageLabel: 'High elevation',
    });

    var mtnExplore = Pages.insert({
        colorPalate: colorPalate,
        componentId: mountains,
        link: 'explore',
        buttonTitle: 'Explore',
        espButtonTitle: 'Explora',
        order: 3,
        bodyCopy: '',
        espBodyCopy: '',
        backgroundMapClass: 'map-explore',
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'explore',
        componentId: mountains,
        parentId: mtnExplore,
        link: 'palomar',
        buttonTitle: 'Palomar Mountain State Park',
        espButtonTitle: 'Parque Estatal Palomar Mountain',
        buttonImage: 'mtn-explore-palomar.jpg',
        bodyImages: [
            {fileName: 'mtn-explore-palomar-full.jpg'},
        ],
        order: 1,
        bodyCopy: 'Visit Palomar Mountain State Park and enjoy a panoramic view of southern California that stretches from the ocean to the desert.',
        espBodyCopy: 'Visita al Parque Estatal de la Montaña Palomar y disfruta de una vista panorámica del sur de California que abarca desde el océano hasta el desierto.',
        websiteCall: 'For more information, visit:',
        websiteCallEsp: 'Para más información, visita:',
        websiteURL: 'parks.ca.gov',
        backgroundMapClass: 'map-mtn-explore-palomar',
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'explore',
        componentId: mountains,
        parentId: mtnExplore,
        link: 'stonewall',
        buttonTitle: 'Cuyamaca Rancho State Park (Stonewall Peak)',
        espButtonTitle: 'Parque Estatal Cuyamaca Rancho (Pico Stonewall)',
        buttonImage: 'mtn-explore-stonewall.jpg',
        bodyImages: [
            {fileName: 'mtn-explore-stonewall-full.jpg'},
        ],
        order: 2,
        bodyCopy: '
Climb a switchback trail to Stonewall Peak at 5,730 feet and enjoy views of Lake Cuyamaca and nearby peaks.

Join the canyoneers for guided hikes in southern California.
',
        espBodyCopy: '
Sube por un sendero zigzagueante hasta la Cima de Stonewall a 5.730 pies y disfruta de las vistas del lago Cuyamaca y las cumbres cercanas.

Únete a los canyoneers para caminatas guiadas en el sur de California.
',
        websiteCall: 'For more information, visit:',
        websiteCallEsp: 'Para más información, visita:',
        websiteURL: 'sdnat.org/canyoneers',
        backgroundMapClass: 'map-mtn-explore-stonewall',
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'explore',
        componentId: mountains,
        parentId: mtnExplore,
        link: 'volcan',
        buttonTitle: 'Volcan Mountain Wilderness Preserve',
        espButtonTitle: 'Reserva Silvestre Volcan Mountain',
        buttonImage: 'mtn-explore-volcan.jpg',
        bodyImages: [
            {fileName: 'mtn-explore-volcan-full.jpg'},
        ],
        order: 3,
        bodyCopy: '
Hike alongside beds of wildflowers and under the shade of giant oak trees straight to Volcan Peak for a view of the entire preserve.

Join the canyoneers for guided hikes in southern California.
',
        espBodyCopy: '
Camina a lo largo de campos de flores silvestres y bajo la sombra de robles gigantes hasta llegar a Volcan Peak para disfrutar de una vista que abarca toda la reserva.

Únete a los canyoneers para caminatas guiadas en el sur de California.
',
        websiteCall: 'For more information, visit:',
        websiteCallEsp: 'Para más información, visita:',
        websiteURL: 'sdnat.org/canyoneers',
        backgroundMapClass: 'map-mtn-explore-volcan',
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'explore',
        componentId: mountains,
        parentId: mtnExplore,
        link: 'thunder',
        buttonTitle: 'Palomar Mountain State Park (Thunder Spring/Chimney Flats)',
        espButtonTitle: 'Parque Estatal de la Montaña Palomar (Thunder Spring/Chimney Flats)',
        buttonImage: 'mtn-explore-thunder.jpg',
        bodyImages: [
            {fileName: 'mtn-explore-thunder-full.jpg'},
        ],
        order: 4,
        bodyCopy: '
Enjoy a picturesque trail up to Thunder Spring while hiking through oak and conifer forests. It gets a little steep, but the view is worth the climb!

Join the canyoneers for guided hikes in southern California.
',
        espBodyCopy: '
Disfruta de un sendero pintoresco hasta alcanzar Thunder Spring mientras caminas por los bosques de robles y coníferas. Se torna un poco empinado, ¡pero la vista hace que la subida valga la pena!

Únete a los canyoneers para caminatas guiadas en el sur de California.
',
        websiteCall: 'For more information, visit:',
        websiteCallEsp: 'Para más información, visita:',
        websiteURL: 'sdnat.org/canyoneers',
        backgroundMapClass: 'map-mtn-explore-thunder',
    });

    var mtnScienceStories = Pages.insert({
        colorPalate: colorPalate,
        link: 'science',
        componentId: mountains,
        buttonTitle: 'Science spotlight',
        espButtonTitle: 'Enfoque científico',
        order: 5,
        bodyCopy: '',
        espBodyCopy: '',
        backgroundMapClass: 'map-stories',
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'science',
        link: 'lori-hargrove',
        componentId: mountains,
        parentId: mtnScienceStories,
        buttonTitle: 'Lori Hargrove
Ecologist',
        espButtonTitle: 'Ecologista',
        buttonImage: 'sci-hargrove-lori-button.jpg',
        bodyImages: [
            {fileName: 'sci-hargrove-lori-full.jpg'},
        ],
        order: 1,
        bodyCopy: '"The White-headed Woodpecker is found only in the pine forests of western North America, from Canada south to Cuyamaca Rancho State Park in San Diego County. The Cedar Fire in 2003, which burned 95 percent of the park, destroyed this species\' southernmost habitat."',
        espBodyCopy: '"El carpintero cabeza blanca se encuentra únicamente en los bosques de pinos en el oeste de Norteamérica, desde Canadá hacia el sur en el Parque Estatal Cuyamaca Rancho en el condado de San Diego. El incendio Cedar en 2003, que quemó el noventa y cinco por ciento del parque, destruyó el hábitat más sureño de esta especie".',
        backgroundMapClass: 'sci-mtn-woodpecker',
        backgroundCaption: 'White-headed Woodpecker',
        backgroundCaptionEsp: 'Carpintero cabeza blanca',
        backgroundCaptionLatin: 'Picoides albolarvatus'
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'science',
        link: 'jim-berrian',
        componentId: mountains,
        parentId: mtnScienceStories,
        buttonTitle: 'Jim Berrian
Entomologist',
        espButtonTitle: 'Entomólogo',
        buttonImage: 'sci-berrian-jim-button.jpg',
        bodyImages: [
            {fileName: 'sci-berrian-jim-full.jpg'},
        ],
        order: 2,
        bodyCopy: '"This native Californian spider was discovered only recently, in 2005. It’s smaller than a grain of rice. This discovery is exciting because it shows that we still have new finds to make right here in our local mountains."',
        espBodyCopy: '"Esta araña nativa de California fue descubierta recién en 2005.  Es más pequeña que un grano de arroz.  Este descubrimiento es fascinante porque demuestra que tenemos nuevas cosas para descubrir aquí mismo en nuestras montañas locales".',
        backgroundMapClass: 'sci-can-spider',
        backgroundCaption: 'Spider',
        backgroundCaptionEsp: 'Araña',
        backgroundCaptionLatin: 'Apostenus californicus'
    });

}
<commit_msg>We don't need a label for this compare map<commit_after>/**
 * Mountains
 */
var colorPalate = 'mountains';

Components.remove({ 'colorPalate': colorPalate });
Pages.remove({ 'colorPalate': colorPalate });

if (Components.find( { 'colorPalate': colorPalate } ).count() === 0) {

    /**
     **************************************************************************
     * Mountains
     **************************************************************************
     */
    var mountains = Components.insert({
        colorPalate: colorPalate,
        link: 'mountains',
        title: 'The Cuyamaca Mountains',
        titleEsp: 'Las montañas Cuyamaca',
        componentNum: '0816',
        order: 7,
        mural: 'mtn-mural.jpg'
    });

    Pages.insert({
        colorPalate: colorPalate,
        componentId: mountains,
        link: 'where-is-it',
        buttonTitle: 'Where is it?',
        espButtonTitle: '¿Dónde está?',
        order: 1,
        bodyCopy: 'The Cuyamaca Mountains, the setting for the scene in front of you, are just 50 miles from the Museum',
        espBodyCopy: 'Las montañas Cuyamaca, el marco de la escena delante de ti, están apenas a 50 millas del  Museo. ',
        backgroundMapClass: 'map-mtn-where',
        bodyImages: [
            {fileName: 'mtn-where.jpg'},
        ],
    });

    //Pages.insert({
        //colorPalate: colorPalate,
        //componentId: mountains,
        //link: 'our-mountains',
        //buttonTitle: 'Our mountains',
        //espButtonTitle: 'Nuestras montañas',
        //order: 2,
        //bodyCopy: 'Southern California has several hundred mountain peaks, including San Gorgonio Mountain, which rises to 11,503 feet.',
        //espBodyCopy: 'El sur de California tiene varios centenares de cumbres, incluyendo la montaña San Gorgonio, que tiene una elevación de 11.503 pies.',
        //backgroundMapClass: 'map-mtn-our',
    //});

    Pages.insert({
        colorPalate: colorPalate,
        componentId: mountains,
        link: 'rare-habitat',
        buttonTitle: 'Rare habitat',
        espButtonTitle: 'Hábitat inusual',
        order: 3,
        bodyCopyLayout: 'horizontal',
        bodyCopy: '
Our highest mountain peaks—the coolest and wettest areas of southern California—are home to some of the last conifer forests in the region.

Move the slider to compare southern California elevations to conifer forest locations.
',
        espBodyCopy: '
Nuestras cumbres más elevadas—las zonas más frescas y húmedas del sur de California—albergan algunos de los últimos bosques de coníferas en la región.

Mueva el control deslizante para comparar las elevaciones del sur de California a coníferas lugares forestales.
',
        backgroundMapClass: 'map-wet-then-now',
        bottomImage: 'mtn-compare-elevation-01-v03.png',
        bottomImageLabel: '',
        topImage: 'mtn-compare-elevation-02-v03.png',
        topImageLabel: '',
    });

    var mtnExplore = Pages.insert({
        colorPalate: colorPalate,
        componentId: mountains,
        link: 'explore',
        buttonTitle: 'Explore',
        espButtonTitle: 'Explora',
        order: 4,
        bodyCopy: '',
        espBodyCopy: '',
        backgroundMapClass: 'map-explore',
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'explore',
        componentId: mountains,
        parentId: mtnExplore,
        link: 'palomar',
        buttonTitle: 'Palomar Mountain State Park',
        espButtonTitle: 'Parque Estatal Palomar Mountain',
        buttonImage: 'mtn-explore-palomar.jpg',
        bodyImages: [
            {fileName: 'mtn-explore-palomar-full.jpg'},
        ],
        order: 1,
        bodyCopy: 'Visit Palomar Mountain State Park and enjoy a panoramic view of southern California that stretches from the ocean to the desert.',
        espBodyCopy: 'Visita al Parque Estatal de la Montaña Palomar y disfruta de una vista panorámica del sur de California que abarca desde el océano hasta el desierto.',
        websiteCall: 'For more information, visit:',
        websiteCallEsp: 'Para más información, visita:',
        websiteURL: 'parks.ca.gov',
        backgroundMapClass: 'map-mtn-explore-palomar',
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'explore',
        componentId: mountains,
        parentId: mtnExplore,
        link: 'stonewall',
        buttonTitle: 'Cuyamaca Rancho State Park (Stonewall Peak)',
        espButtonTitle: 'Parque Estatal Cuyamaca Rancho (Pico Stonewall)',
        buttonImage: 'mtn-explore-stonewall.jpg',
        bodyImages: [
            {fileName: 'mtn-explore-stonewall-full.jpg'},
        ],
        order: 2,
        bodyCopy: '
Climb a switchback trail to Stonewall Peak at 5,730 feet and enjoy views of Lake Cuyamaca and nearby peaks.

Join the canyoneers for guided hikes in southern California.
',
        espBodyCopy: '
Sube por un sendero zigzagueante hasta la Cima de Stonewall a 5.730 pies y disfruta de las vistas del lago Cuyamaca y las cumbres cercanas.

Únete a los canyoneers para caminatas guiadas en el sur de California.
',
        websiteCall: 'For more information, visit:',
        websiteCallEsp: 'Para más información, visita:',
        websiteURL: 'sdnat.org/canyoneers',
        backgroundMapClass: 'map-mtn-explore-stonewall',
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'explore',
        componentId: mountains,
        parentId: mtnExplore,
        link: 'volcan',
        buttonTitle: 'Volcan Mountain Wilderness Preserve',
        espButtonTitle: 'Reserva Silvestre Volcan Mountain',
        buttonImage: 'mtn-explore-volcan.jpg',
        bodyImages: [
            {fileName: 'mtn-explore-volcan-full.jpg'},
        ],
        order: 3,
        bodyCopy: '
Hike alongside beds of wildflowers and under the shade of giant oak trees straight to Volcan Peak for a view of the entire preserve.

Join the canyoneers for guided hikes in southern California.
',
        espBodyCopy: '
Camina a lo largo de campos de flores silvestres y bajo la sombra de robles gigantes hasta llegar a Volcan Peak para disfrutar de una vista que abarca toda la reserva.

Únete a los canyoneers para caminatas guiadas en el sur de California.
',
        websiteCall: 'For more information, visit:',
        websiteCallEsp: 'Para más información, visita:',
        websiteURL: 'sdnat.org/canyoneers',
        backgroundMapClass: 'map-mtn-explore-volcan',
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'explore',
        componentId: mountains,
        parentId: mtnExplore,
        link: 'thunder',
        buttonTitle: 'Palomar Mountain State Park (Thunder Spring/Chimney Flats)',
        espButtonTitle: 'Parque Estatal de la Montaña Palomar (Thunder Spring/Chimney Flats)',
        buttonImage: 'mtn-explore-thunder.jpg',
        bodyImages: [
            {fileName: 'mtn-explore-thunder-full.jpg'},
        ],
        order: 4,
        bodyCopy: '
Enjoy a picturesque trail up to Thunder Spring while hiking through oak and conifer forests. It gets a little steep, but the view is worth the climb!

Join the canyoneers for guided hikes in southern California.
',
        espBodyCopy: '
Disfruta de un sendero pintoresco hasta alcanzar Thunder Spring mientras caminas por los bosques de robles y coníferas. Se torna un poco empinado, ¡pero la vista hace que la subida valga la pena!

Únete a los canyoneers para caminatas guiadas en el sur de California.
',
        websiteCall: 'For more information, visit:',
        websiteCallEsp: 'Para más información, visita:',
        websiteURL: 'sdnat.org/canyoneers',
        backgroundMapClass: 'map-mtn-explore-thunder',
    });

    var mtnScienceStories = Pages.insert({
        colorPalate: colorPalate,
        link: 'science',
        componentId: mountains,
        buttonTitle: 'Science spotlight',
        espButtonTitle: 'Enfoque científico',
        order: 5,
        bodyCopy: '',
        espBodyCopy: '',
        backgroundMapClass: 'map-stories',
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'science',
        link: 'lori-hargrove',
        componentId: mountains,
        parentId: mtnScienceStories,
        buttonTitle: 'Lori Hargrove
Ecologist',
        espButtonTitle: 'Ecologista',
        buttonImage: 'sci-hargrove-lori-button.jpg',
        bodyImages: [
            {fileName: 'sci-hargrove-lori-full.jpg'},
        ],
        order: 1,
        bodyCopy: '"The White-headed Woodpecker is found only in the pine forests of western North America, from Canada south to Cuyamaca Rancho State Park in San Diego County. The Cedar Fire in 2003, which burned 95 percent of the park, destroyed this species\' southernmost habitat."',
        espBodyCopy: '"El carpintero cabeza blanca se encuentra únicamente en los bosques de pinos en el oeste de Norteamérica, desde Canadá hacia el sur en el Parque Estatal Cuyamaca Rancho en el condado de San Diego. El incendio Cedar en 2003, que quemó el noventa y cinco por ciento del parque, destruyó el hábitat más sureño de esta especie".',
        backgroundMapClass: 'sci-mtn-woodpecker',
        backgroundCaption: 'White-headed Woodpecker',
        backgroundCaptionEsp: 'Carpintero cabeza blanca',
        backgroundCaptionLatin: 'Picoides albolarvatus'
    });

    Pages.insert({
        colorPalate: colorPalate,
        subPageType: 'science',
        link: 'jim-berrian',
        componentId: mountains,
        parentId: mtnScienceStories,
        buttonTitle: 'Jim Berrian
Entomologist',
        espButtonTitle: 'Entomólogo',
        buttonImage: 'sci-berrian-jim-button.jpg',
        bodyImages: [
            {fileName: 'sci-berrian-jim-full.jpg'},
        ],
        order: 2,
        bodyCopy: '"This native Californian spider was discovered only recently, in 2005. It’s smaller than a grain of rice. This discovery is exciting because it shows that we still have new finds to make right here in our local mountains."',
        espBodyCopy: '"Esta araña nativa de California fue descubierta recién en 2005.  Es más pequeña que un grano de arroz.  Este descubrimiento es fascinante porque demuestra que tenemos nuevas cosas para descubrir aquí mismo en nuestras montañas locales".',
        backgroundMapClass: 'sci-can-spider',
        backgroundCaption: 'Spider',
        backgroundCaptionEsp: 'Araña',
        backgroundCaptionLatin: 'Apostenus californicus'
    });

}
<|endoftext|>


<commit_before>'use strict'

const coll = 'contracts'
const { cached, execute, hashData, insert, mapResult, parseResponse, retrieve, validate } = require('../../../lib/contract')

const jackal = (req, res, next) => {
  const contracts = req.body

  if (contracts.length === undefined || contracts.length === 0) {
    res.status(400).send({ message: 'No contracts received' })

    return next()
  }

  const json = JSON.stringify(contracts)
  const hash = hashData(json)

  if (!cached(coll, hash)) {
    const validations = contracts.map(validate)

    if (!validations.every(v => v.valid)) {
      res.status(400).send({
        message: "One or more contracts are invalid",
        validations: validations.map((v, i) => {
          const contract = contracts[i]

          return {
            contract: `${contract.name} <- ${contract.consumer}`,
            valid: v.valid,
            errors: v.errors
          }
        })
      })

      return next()
    }

    const parsedContracts = contracts.map(contract => {
      const parsedResponse = parseResponse(contract.response)

      return {
        name: contract.name,
        consumer: contract.consumer,
        request: contract.request,
        response: parsedResponse.valid ? parsedResponse.response : parsedResponse.error
      }
    })

    const invalidContract = parsedContracts.find(pc => isCompilationError(pc.response))
    if (invalidContract) {
      res.status(400).send(invalidContract.response)

      return next()
    }

    if (!insert(coll, hash, parsedContracts)) {
      res.status(500).send({ message: 'Cache failed on contracts insertion' })

      return next()
    }
  }

  const cacheObject = retrieve(coll, hash)

  if (cacheObject.hash !== hash) {
    res.status(500).send({ message: 'Cache failed on contracts retrieval' })

    return next()
  }

  execute(cacheObject.contracts, (err, results) => {
    res.status(201).send(results.map(mapResult))

    return next()
  })
}

module.exports = jackal

const isCompilationError = value => {
  const name = value.name && value.name === 'DSL Error'
  const msg = value.message && value.message === 'Response could not be compiled. Please see the DSL documentation: https://github.com/findmypast-oss/jackal/blob/master/dsl.md'

  return name && msg
}
<commit_msg>Fixing error in middleware<commit_after>'use strict'

const coll = 'contracts'
const { cached, execute, hashData, insert, mapResult, parseResponse, retrieve, validate } = require('../../../lib/contract')

const jackal = (req, res, next) => {
  const contracts = req.body

  if (contracts.length === undefined || contracts.length === 0) {
    res.status(400).send({ message: 'No contracts received' })

    return next()
  }

  const json = JSON.stringify(contracts)
  const hash = hashData(json)

  if (!cached(coll, hash)) {
    const validations = contracts.map(validate)

    if (!validations.every(v => v.valid)) {
      res.status(400).send({
        message: "One or more contracts are invalid",
        validations: validations.map((v, i) => {
          const contract = contracts[i]

          return {
            contract: `${contract.name} <- ${contract.consumer}`,
            valid: v.valid,
            errors: v.errors
          }
        })
      })

      return next()
    }

    const parsedContracts = contracts.map(contract => {
      const parsedResponse = parseResponse(contract.response)

      return {
        name: contract.name,
        consumer: contract.consumer,
        request: contract.request,
        response: parsedResponse.valid ? parsedResponse.response : parsedResponse.error
      }
    })

    const invalidContract = parsedContracts.find(pc => isCompilationError(pc.response.body))
    if (invalidContract) {
      res.status(400).send(invalidContract.response)

      return next()
    }

    if (!insert(coll, hash, parsedContracts)) {
      res.status(500).send({ message: 'Cache failed on contracts insertion' })

      return next()
    }
  }

  const cacheObject = retrieve(coll, hash)

  if (cacheObject.hash !== hash) {
    res.status(500).send({ message: 'Cache failed on contracts retrieval' })

    return next()
  }

  execute(cacheObject.contracts, (err, results) => {
    res.status(201).send(results.map(mapResult))

    return next()
  })
}

module.exports = jackal

const isCompilationError = value => {
  const name = value.name && value.name === 'DSL Error'
  const msg = value.message && value.message === 'Response could not be compiled. Please see the DSL documentation: https://github.com/findmypast-oss/jackal/blob/master/dsl.md'

  return name && msg
}
<|endoftext|>


<commit_before>/*
MIT License

Copyright (c) 2017 Erik De Rijcke

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

'use strict'

const DisplayResource = require('./DisplayResource')
const DisplayRequests = require('./DisplayRequests')
const SyncCallbackResource = require('./SyncCallbackResource')
const Fixed = require('./Fixed')

/**
 * Represents a client connection.
 * @implements DisplayRequests
 */
class Client extends DisplayRequests {
  /**
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @param {number}consumption
   * @private
   */
  static _checkMessageSize (message, consumption) {
    if (message.consumed + consumption > message.size) {
      throw new Error(`Request too short.`)
    } else {
      message.consumed += consumption
    }
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @returns {number}
   */
  u (message) { // unsigned integer {number}
    Client._checkMessageSize(message, 4)
    return message.buffer[message.bufferOffset++]
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @returns {number}
   */
  i (message) { // integer {number}
    Client._checkMessageSize(message, 4)
    const arg = new Int32Array(message.buffer.buffer, message.buffer.byteOffset + (message.bufferOffset * Uint32Array.BYTES_PER_ELEMENT), 1)[0]
    message.bufferOffset += 1
    return arg
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @returns {number}
   */
  f (message) { // float {number}
    Client._checkMessageSize(message, 4)
    const arg = new Int32Array(message.buffer.buffer, message.buffer.byteOffset + (message.bufferOffset * Uint32Array.BYTES_PER_ELEMENT), 1)[0]
    message.bufferOffset += 1
    return new Fixed(arg >> 0)
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @param {Boolean} optional
   * @returns {Resource}
   */
  o (message, optional) {
    Client._checkMessageSize(message, 4)
    const arg = message.buffer[message.bufferOffset++]
    if (optional && arg === 0) {
      return null
    } else {
      return this._resources[arg]
    }
  }

  /**
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @returns {number}
   */
  n (message) {
    Client._checkMessageSize(message, 4)
    return message.buffer[message.bufferOffset++]
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @param {Boolean} optional
   * @returns {String}
   */
  s (message, optional) { // {String}
    Client._checkMessageSize(message, 4)
    const stringSize = message.buffer[message.bufferOffset++]
    if (optional && stringSize === 0) {
      return null
    } else {
      const alignedSize = ((stringSize + 3) & ~3)
      Client._checkMessageSize(message, alignedSize)
      // size -1 to eliminate null byte
      const byteArray = new Uint8Array(message.buffer.buffer, message.buffer.byteOffset + (message.bufferOffset * Uint32Array.BYTES_PER_ELEMENT), stringSize - 1)
      message.bufferOffset += alignedSize
      return String.fromCharCode(...byteArray)
    }
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @param {Boolean} optional
   * @returns {ArrayBuffer}
   */
  a (message, optional) {
    Client._checkMessageSize(message, 4)
    const arraySize = message.buffer[message.bufferOffset++]
    if (optional && arraySize === 0) {
      return null
    } else {
      const alignedSize = ((arraySize + 3) & ~3)
      Client._checkMessageSize(message, alignedSize)
      const arg = message.buffer.buffer.slice(message.buffer.byteOffset + (message.bufferOffset * Uint32Array.BYTES_PER_ELEMENT), message.buffer.byteOffset + (message.bufferOffset * Uint32Array.BYTES_PER_ELEMENT) + arraySize)
      message.bufferOffset += alignedSize
      return arg
    }
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @returns {number}
   */
  h (message) { // file descriptor {number}
    if (message.fds.length > message.fdsOffset) {
      return message.fds[message.fdsOffset++]
    } else {
      throw new Error('Not enough file descriptors in message object.')
    }
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @param {string} argsSignature
   * @returns {Array<*>}
   */
  unmarshallArgs (message, argsSignature) {
    const argsSigLength = argsSignature.length
    const args = []
    let optional = false
    for (let i = 0; i < argsSigLength; i++) {
      let signatureChar = argsSignature[i]
      optional = signatureChar === '?'

      if (optional) {
        signatureChar = argsSignature[++i]
      }

      args.push(this[signatureChar](message, optional))
    }
    return args
  }

  /**
   * This doesn't actually send the message, but queues it so it can be send on flush.
   * @param {{buffer: ArrayBuffer, fds: Array<number>}}wireMsg a single wire message event.
   */
  onSend (wireMsg) {
    if (!this._display) {
      // client destroyed
      return
    }
    this._outMessages.push(wireMsg)
  }

  /**
   * Empty the queue of wire messages and send them to the other end.
   */
  flush () {
    if (!this._display) {
      // client destroyed
      return
    }
    if (this._outMessages.length === 0) {
      return
    }

    this.onFlush(this._outMessages)
    this._outMessages = []
  }

  /**
   * Callback when this connection wishes to send data to the other end. This callback can be used to send the given
   * array buffers using any transport mechanism.
   * @param {Array<{buffer: ArrayBuffer, fds: Array<number>}>}wireMessages
   */
  onFlush (wireMessages) {}

  /**
   * Handle a received message from a client.
   * @param {{buffer: Uint32Array, fds: Uint32Array}} incomingWireMessages
   * @return {Promise<void>}
   * @throws Error If an illegal client request is received ie. bad length or missing file descriptor.
   */
  async message (incomingWireMessages) {
    if (!this._display) {
      // client destroyed
      return
    }

    this._inMessages.push(incomingWireMessages)
    if (this._inMessages.length > 1) {
      // more than one message in queue means the message loop is in await, don't concurrently process the new
      // message, instead return early and let the resume-from-await pick up the newly queued message.
      return
    }

    while (this._inMessages.length) {
      const wireMessages = /** @type {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} */this._inMessages[0]
      wireMessages.bufferOffset = 0
      wireMessages.consumed = 0
      wireMessages.size = 0
      while (wireMessages.bufferOffset < wireMessages.buffer.length) {
        const id = wireMessages.buffer[wireMessages.bufferOffset]
        const sizeOpcode = wireMessages.buffer[wireMessages.bufferOffset + 1]
        wireMessages.size = sizeOpcode >>> 16
        const opcode = sizeOpcode & 0x0000FFFF

        if (wireMessages.size > wireMessages.buffer.byteLength) {
          throw new Error('Request buffer too small')
        }

        const resource = this._resources[id]
        if (resource) {
          wireMessages.bufferOffset += 2
          wireMessages.consumed = 8
          await resource[opcode](wireMessages)
          if (!this._display) {
            // client destroyed
            return
          }
        } else {
          throw new Error(`invalid object ${id}`)
        }
      }
      this._inMessages.shift()
    }

    // TODO in wayland-server the wait-for-message & flush order is reversed. Find out why.
    this.flush()
  }

  close () {
    if (!this._display) {
      // client destroyed
      return
    }

    Object.values(this._resources).forEach((resource) => {
      resource.destroy()
    })

    this._outMessages = null
    this._inMessages = null
    this.displayResource = null
    this._resources = null
    this._display = null
    this._destroyedResolver()
  }

  /**
   *
   * @returns {Promise<void>}
   */
  onClose () {
    return this._destroyPromise
  }

  /**
   *
   * @param {Resource} resource
   */
  registerResource (resource) {
    if (!this._display) {
      // client destroyed
      return
    }
    this._resources[resource.id] = resource
  }

  /**
   *
   * @param {Resource} resource
   */
  unregisterResource (resource) {
    if (!this._display) {
      // client destroyed
      return
    }
    delete this._resources[resource.id]
    this.displayResource.deleteId(resource.id)
  }

  /**
   *
   * @param {number} id
   * @param {number} opcode
   * @param {number} size
   * @param {Array<{value: *, type: string, size: number, optional: boolean, _marshallArg: function({buffer: ArrayBuffer, fds: Array<number>, bufferOffset: number}):void}>} argsArray
   * @private
   */
  __marshallMsg (id, opcode, size, argsArray) {
    /**
     * @type {{buffer: ArrayBuffer, fds: Array<number>, bufferOffset: number}}
     */
    const wireMsg = {
      buffer: new ArrayBuffer(size),
      fds: [],
      bufferOffset: 0
    }

    // write actual wire message
    const bufu32 = new Uint32Array(wireMsg.buffer)
    const bufu16 = new Uint16Array(wireMsg.buffer)
    bufu32[0] = id
    bufu16[2] = opcode
    bufu16[3] = size
    wireMsg.bufferOffset = 8

    argsArray.forEach((arg) => {
      arg._marshallArg(wireMsg) // write actual argument value to buffer
    })

    this.onSend(wireMsg)
  }

  /**
   * @param {number} id
   * @param {number} opcode
   * @param {Array<{value: *, type: string, size: number, optional: boolean, _marshallArg: function({buffer: ArrayBuffer, fds: Array<number>, bufferOffset: number}):void}>} argsArray
   * @return {number}
   */
  marshallConstructor (id, opcode, argsArray) {
    // get next server id
    const objectId = this._display.nextId
    this._display.nextId++

    // determine required wire message length
    let size = 4 + 2 + 2 // id+size+opcode
    argsArray.forEach(function (arg) {
      if (arg.type === 'n') {
        arg.value = objectId
      }

      size += arg.size // add size of the actual argument values
    })

    this.__marshallMsg(id, opcode, size, argsArray)

    return objectId
  }

  /**
   *
   * @param {number} id
   * @param {number} opcode
   * @param {Array<{value: *, type: string, size: number, optional: boolean, _marshallArg: function({buffer: ArrayBuffer, fds: Array<number>, bufferOffset: number}):void}>} argsArray
   */
  marshall (id, opcode, argsArray) {
    // determine required wire message length
    let size = 4 + 2 + 2  // id+size+opcode
    argsArray.forEach(function (arg) {
      size += arg.size // add size of the actual argument values
    })

    this.__marshallMsg(id, opcode, size, argsArray)
  }

  /**
   * @param {DisplayResource}resource
   * @param {number}id
   * @override
   */
  sync (resource, id) {
    const syncCallbackResource = new SyncCallbackResource(resource.client, id, 1)
    syncCallbackResource.done(++this._syncEventSerial)
    syncCallbackResource.destroy()
  }

  /**
   * @param {DisplayResource}resource
   * @param {number}id
   * @override
   */
  getRegistry (resource, id) {
    this._display.registry.publishGlobals(this._display.registry.createRegistryResource(this, id))
  }

  /**
   *
   * @param {Display} display
   */
  constructor (display) {
    super()
    /**
     * @type {Object.<number, Resource>}
     * @private
     */
    this._resources = {}
    /**
     * @type {Display}
     * @private
     */
    this._display = display
    /**
     * @type {number}
     * @private
     */
    this._syncEventSerial = 0
    /**
     * @type {function():void}
     * @private
     */
    this._destroyedResolver = null
    /**
     * @type {Promise<void>}
     * @private
     */
    this._destroyPromise = new Promise((resolve) => {
      this._destroyedResolver = resolve
    })
    /**
     * @type {DisplayResource}
     */
    this.displayResource = new DisplayResource(this, 1, 0)
    this.displayResource.implementation = this
    /**
     * @type {Array<{buffer: ArrayBuffer, fds: Array<number>}>}
     * @private
     */
    this._outMessages = []
    /**
     * @type {Array<{buffer: Uint32Array, fds: Uint32Array}>}
     * @private
     */
    this._inMessages = []
  }
}

module.exports = Client<commit_msg>properly increase buffer offset in case of string argument<commit_after>/*
MIT License

Copyright (c) 2017 Erik De Rijcke

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

'use strict'

const DisplayResource = require('./DisplayResource')
const DisplayRequests = require('./DisplayRequests')
const SyncCallbackResource = require('./SyncCallbackResource')
const Fixed = require('./Fixed')

/**
 * Represents a client connection.
 * @implements DisplayRequests
 */
class Client extends DisplayRequests {
  /**
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @param {number}consumption
   * @private
   */
  static _checkMessageSize (message, consumption) {
    if (message.consumed + consumption > message.size) {
      throw new Error(`Request too short.`)
    } else {
      message.consumed += consumption
    }
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @returns {number}
   */
  u (message) { // unsigned integer {number}
    Client._checkMessageSize(message, 4)
    return message.buffer[message.bufferOffset++]
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @returns {number}
   */
  i (message) { // integer {number}
    Client._checkMessageSize(message, 4)
    const arg = new Int32Array(message.buffer.buffer, message.buffer.byteOffset + (message.bufferOffset * Uint32Array.BYTES_PER_ELEMENT), 1)[0]
    message.bufferOffset += 1
    return arg
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @returns {number}
   */
  f (message) { // float {number}
    Client._checkMessageSize(message, 4)
    const arg = new Int32Array(message.buffer.buffer, message.buffer.byteOffset + (message.bufferOffset * Uint32Array.BYTES_PER_ELEMENT), 1)[0]
    message.bufferOffset += 1
    return new Fixed(arg >> 0)
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @param {Boolean} optional
   * @returns {Resource}
   */
  o (message, optional) {
    Client._checkMessageSize(message, 4)
    const arg = message.buffer[message.bufferOffset++]
    if (optional && arg === 0) {
      return null
    } else {
      return this._resources[arg]
    }
  }

  /**
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @returns {number}
   */
  n (message) {
    Client._checkMessageSize(message, 4)
    return message.buffer[message.bufferOffset++]
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @param {Boolean} optional
   * @returns {String}
   */
  s (message, optional) { // {String}
    Client._checkMessageSize(message, 4)
    const stringSize = message.buffer[message.bufferOffset++]
    if (optional && stringSize === 0) {
      return null
    } else {
      const alignedSize = ((stringSize + 3) & ~3)
      Client._checkMessageSize(message, alignedSize)
      // size -1 to eliminate null byte
      const byteArray = new Uint8Array(message.buffer.buffer, message.buffer.byteOffset + (message.bufferOffset * Uint32Array.BYTES_PER_ELEMENT), stringSize - 1)
      message.bufferOffset += (alignedSize / 4)
      return String.fromCharCode(...byteArray)
    }
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @param {Boolean} optional
   * @returns {ArrayBuffer}
   */
  a (message, optional) {
    Client._checkMessageSize(message, 4)
    const arraySize = message.buffer[message.bufferOffset++]
    if (optional && arraySize === 0) {
      return null
    } else {
      const alignedSize = ((arraySize + 3) & ~3)
      Client._checkMessageSize(message, alignedSize)
      const arg = message.buffer.buffer.slice(message.buffer.byteOffset + (message.bufferOffset * Uint32Array.BYTES_PER_ELEMENT), message.buffer.byteOffset + (message.bufferOffset * Uint32Array.BYTES_PER_ELEMENT) + arraySize)
      message.bufferOffset += alignedSize
      return arg
    }
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @returns {number}
   */
  h (message) { // file descriptor {number}
    if (message.fds.length > message.fdsOffset) {
      return message.fds[message.fdsOffset++]
    } else {
      throw new Error('Not enough file descriptors in message object.')
    }
  }

  /**
   *
   * @param {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} message
   * @param {string} argsSignature
   * @returns {Array<*>}
   */
  unmarshallArgs (message, argsSignature) {
    const argsSigLength = argsSignature.length
    const args = []
    let optional = false
    for (let i = 0; i < argsSigLength; i++) {
      let signatureChar = argsSignature[i]
      optional = signatureChar === '?'

      if (optional) {
        signatureChar = argsSignature[++i]
      }

      args.push(this[signatureChar](message, optional))
    }
    return args
  }

  /**
   * This doesn't actually send the message, but queues it so it can be send on flush.
   * @param {{buffer: ArrayBuffer, fds: Array<number>}}wireMsg a single wire message event.
   */
  onSend (wireMsg) {
    if (!this._display) {
      // client destroyed
      return
    }
    this._outMessages.push(wireMsg)
  }

  /**
   * Empty the queue of wire messages and send them to the other end.
   */
  flush () {
    if (!this._display) {
      // client destroyed
      return
    }
    if (this._outMessages.length === 0) {
      return
    }

    this.onFlush(this._outMessages)
    this._outMessages = []
  }

  /**
   * Callback when this connection wishes to send data to the other end. This callback can be used to send the given
   * array buffers using any transport mechanism.
   * @param {Array<{buffer: ArrayBuffer, fds: Array<number>}>}wireMessages
   */
  onFlush (wireMessages) {}

  /**
   * Handle a received message from a client.
   * @param {{buffer: Uint32Array, fds: Uint32Array}} incomingWireMessages
   * @return {Promise<void>}
   * @throws Error If an illegal client request is received ie. bad length or missing file descriptor.
   */
  async message (incomingWireMessages) {
    if (!this._display) {
      // client destroyed
      return
    }

    this._inMessages.push(incomingWireMessages)
    if (this._inMessages.length > 1) {
      // more than one message in queue means the message loop is in await, don't concurrently process the new
      // message, instead return early and let the resume-from-await pick up the newly queued message.
      return
    }

    while (this._inMessages.length) {
      const wireMessages = /** @type {{buffer: Uint32Array, fds: Uint32Array, bufferOffset: number, fdsOffset:number, consumed: number, size: number}} */this._inMessages[0]
      wireMessages.bufferOffset = 0
      wireMessages.consumed = 0
      wireMessages.size = 0
      while (wireMessages.bufferOffset < wireMessages.buffer.length) {
        const id = wireMessages.buffer[wireMessages.bufferOffset]
        const sizeOpcode = wireMessages.buffer[wireMessages.bufferOffset + 1]
        wireMessages.size = sizeOpcode >>> 16
        const opcode = sizeOpcode & 0x0000FFFF

        if (wireMessages.size > wireMessages.buffer.byteLength) {
          throw new Error('Request buffer too small')
        }

        const resource = this._resources[id]
        if (resource) {
          wireMessages.bufferOffset += 2
          wireMessages.consumed = 8
          await resource[opcode](wireMessages)
          if (!this._display) {
            // client destroyed
            return
          }
        } else {
          throw new Error(`invalid object ${id}`)
        }
      }
      this._inMessages.shift()
    }

    this.flush()
  }

  close () {
    if (!this._display) {
      // client destroyed
      return
    }

    Object.values(this._resources).forEach((resource) => {
      resource.destroy()
    })

    this._outMessages = null
    this._inMessages = null
    this.displayResource = null
    this._resources = null
    this._display = null
    this._destroyedResolver()
  }

  /**
   *
   * @returns {Promise<void>}
   */
  onClose () {
    return this._destroyPromise
  }

  /**
   *
   * @param {Resource} resource
   */
  registerResource (resource) {
    if (!this._display) {
      // client destroyed
      return
    }
    this._resources[resource.id] = resource
  }

  /**
   *
   * @param {Resource} resource
   */
  unregisterResource (resource) {
    if (!this._display) {
      // client destroyed
      return
    }
    delete this._resources[resource.id]
    this.displayResource.deleteId(resource.id)
  }

  /**
   *
   * @param {number} id
   * @param {number} opcode
   * @param {number} size
   * @param {Array<{value: *, type: string, size: number, optional: boolean, _marshallArg: function({buffer: ArrayBuffer, fds: Array<number>, bufferOffset: number}):void}>} argsArray
   * @private
   */
  __marshallMsg (id, opcode, size, argsArray) {
    /**
     * @type {{buffer: ArrayBuffer, fds: Array<number>, bufferOffset: number}}
     */
    const wireMsg = {
      buffer: new ArrayBuffer(size),
      fds: [],
      bufferOffset: 0
    }

    // write actual wire message
    const bufu32 = new Uint32Array(wireMsg.buffer)
    const bufu16 = new Uint16Array(wireMsg.buffer)
    bufu32[0] = id
    bufu16[2] = opcode
    bufu16[3] = size
    wireMsg.bufferOffset = 8

    argsArray.forEach((arg) => {
      arg._marshallArg(wireMsg) // write actual argument value to buffer
    })

    this.onSend(wireMsg)
  }

  /**
   * @param {number} id
   * @param {number} opcode
   * @param {Array<{value: *, type: string, size: number, optional: boolean, _marshallArg: function({buffer: ArrayBuffer, fds: Array<number>, bufferOffset: number}):void}>} argsArray
   * @return {number}
   */
  marshallConstructor (id, opcode, argsArray) {
    // get next server id
    const objectId = this._display.nextId
    this._display.nextId++

    // determine required wire message length
    let size = 4 + 2 + 2 // id+size+opcode
    argsArray.forEach(function (arg) {
      if (arg.type === 'n') {
        arg.value = objectId
      }

      size += arg.size // add size of the actual argument values
    })

    this.__marshallMsg(id, opcode, size, argsArray)

    return objectId
  }

  /**
   *
   * @param {number} id
   * @param {number} opcode
   * @param {Array<{value: *, type: string, size: number, optional: boolean, _marshallArg: function({buffer: ArrayBuffer, fds: Array<number>, bufferOffset: number}):void}>} argsArray
   */
  marshall (id, opcode, argsArray) {
    // determine required wire message length
    let size = 4 + 2 + 2  // id+size+opcode
    argsArray.forEach(function (arg) {
      size += arg.size // add size of the actual argument values
    })

    this.__marshallMsg(id, opcode, size, argsArray)
  }

  /**
   * @param {DisplayResource}resource
   * @param {number}id
   * @override
   */
  sync (resource, id) {
    const syncCallbackResource = new SyncCallbackResource(resource.client, id, 1)
    syncCallbackResource.done(++this._syncEventSerial)
    syncCallbackResource.destroy()
  }

  /**
   * @param {DisplayResource}resource
   * @param {number}id
   * @override
   */
  getRegistry (resource, id) {
    this._display.registry.publishGlobals(this._display.registry.createRegistryResource(this, id))
  }

  /**
   *
   * @param {Display} display
   */
  constructor (display) {
    super()
    /**
     * @type {Object.<number, Resource>}
     * @private
     */
    this._resources = {}
    /**
     * @type {Display}
     * @private
     */
    this._display = display
    /**
     * @type {number}
     * @private
     */
    this._syncEventSerial = 0
    /**
     * @type {function():void}
     * @private
     */
    this._destroyedResolver = null
    /**
     * @type {Promise<void>}
     * @private
     */
    this._destroyPromise = new Promise((resolve) => {
      this._destroyedResolver = resolve
    })
    /**
     * @type {DisplayResource}
     */
    this.displayResource = new DisplayResource(this, 1, 0)
    this.displayResource.implementation = this
    /**
     * @type {Array<{buffer: ArrayBuffer, fds: Array<number>}>}
     * @private
     */
    this._outMessages = []
    /**
     * @type {Array<{buffer: Uint32Array, fds: Uint32Array}>}
     * @private
     */
    this._inMessages = []
  }
}

module.exports = Client<|endoftext|>


<commit_before>'use strict'

let _ = require('lodash')
let zomato = require('../middleware/zomato')
let helpers = require('../helpers')

module.exports = (config, serviceConfig) => {
  let zo = zomato(config)

  let processMenu = (obj, options, next) => {
    let dailyMenus = obj.daily_menus
    let out = []
    for (let i = 0; i < dailyMenus.length; i++) {
      out.push({
        date: zo.parseDate(dailyMenus[i].start_date),
        items: _.map(dailyMenus[i].daily_menu.dishes, zo.mapItem)
      })
    }

    next(null, out)
  }

  return {
    middleware: zo.middleware(serviceConfig.id, helpers.createProcessMenu(processMenu))
  }
}
<commit_msg>Filter Bad Flash menu mess<commit_after>'use strict'

let _ = require('lodash')
let zomato = require('../middleware/zomato')
let helpers = require('../helpers')

module.exports = (config, serviceConfig) => {
  let zo = zomato(config)

  let noHeaders = (menu) => !menu.item.match(/Polední nabídka|v menu|Domácí limonáda|0,3l|Informace o alergenech/gi)

  let processMenu = (obj, options, next) => {
    let dailyMenus = obj.daily_menus
    let out = []
    for (let i = 0; i < dailyMenus.length; i++) {
      out.push({
        date: zo.parseDate(dailyMenus[i].start_date),
        items: _.filter(_.map(dailyMenus[i].daily_menu.dishes, zo.mapItem), noHeaders)
      })
    }

    next(null, out)
  }

  return {
    middleware: zo.middleware(serviceConfig.id, helpers.createProcessMenu(processMenu))
  }
}
<|endoftext|>


<commit_before>var fs     = require('fs')
  , events = require('events')
  , buffer = require('buffer')
  , http   = require('http')
  , url    = require('url')
  , path   = require('path')
  , mime   = require('mime')
  , util   = require('./node-static/util');

// Current version
var version = [0, 7, 7];

var Server = function (root, options) {
    if (root && (typeof(root) === 'object')) { options = root; root = null }

    // resolve() doesn't normalize (to lowercase) drive letters on Windows
    this.root    = path.normalize(path.resolve(root || '.'));
    this.options = options || {};
    this.cache   = 3600;

    this.defaultHeaders  = {};
    this.options.headers = this.options.headers || {};

    this.options.indexFile = this.options.indexFile || "index.html";

    if ('cache' in this.options) {
        if (typeof(this.options.cache) === 'number') {
            this.cache = this.options.cache;
        } else if (! this.options.cache) {
            this.cache = false;
        }
    }

    if ('serverInfo' in this.options) {
        this.serverInfo = this.options.serverInfo.toString();
    } else {
        this.serverInfo = 'node-static/' + version.join('.');
    }

    this.defaultHeaders['server'] = this.serverInfo;

    if (this.cache !== false) {
        this.defaultHeaders['cache-control'] = 'max-age=' + this.cache;
    }

    for (var k in this.defaultHeaders) {
        this.options.headers[k] = this.options.headers[k] ||
                                  this.defaultHeaders[k];
    }
};

Server.prototype.serveDir = function (pathname, req, res, finish) {
    var htmlIndex = path.join(pathname, this.options.indexFile),
        that = this;

    fs.stat(htmlIndex, function (e, stat) {
        if (!e) {
            var status = 200;
            var headers = {};
            var originalPathname = decodeURI(url.parse(req.url).pathname);
            if (originalPathname.length && originalPathname.charAt(originalPathname.length - 1) !== '/') {
                return finish(301, { 'Location': originalPathname + '/' });
            } else {
                that.respond(null, status, headers, [htmlIndex], stat, req, res, finish);
            }
        } else {
            // Stream a directory of files as a single file.
            fs.readFile(path.join(pathname, 'index.json'), function (e, contents) {
                if (e) { return finish(404, {}) }
                var index = JSON.parse(contents);
                streamFiles(index.files);
            });
        }
    });
    function streamFiles(files) {
        util.mstat(pathname, files, function (e, stat) {
            if (e) { return finish(404, {}) }
            that.respond(pathname, 200, {}, files, stat, req, res, finish);
        });
    }
};

Server.prototype.serveFile = function (pathname, status, headers, req, res) {
    var that = this;
    var promise = new(events.EventEmitter);

    pathname = this.resolve(pathname);

    fs.stat(pathname, function (e, stat) {
        if (e) {
            return promise.emit('error', e);
        }
        that.respond(null, status, headers, [pathname], stat, req, res, function (status, headers) {
            that.finish(status, headers, req, res, promise);
        });
    });
    return promise;
};

Server.prototype.finish = function (status, headers, req, res, promise, callback) {
    var result = {
        status:  status,
        headers: headers,
        message: http.STATUS_CODES[status]
    };

    headers['server'] = this.serverInfo;

    if (!status || status >= 400) {
        if (callback) {
            callback(result);
        } else {
            if (promise.listeners('error').length > 0) {
                promise.emit('error', result);
            }
            else {
              res.writeHead(status, headers);
              res.end();
            }
        }
    } else {
        // Don't end the request here, if we're streaming;
        // it's taken care of in `prototype.stream`.
        if (status !== 200 || req.method !== 'GET') {
            res.writeHead(status, headers);
            res.end();
        }
        callback && callback(null, result);
        promise.emit('success', result);
    }
};

Server.prototype.servePath = function (pathname, status, headers, req, res, finish) {
    var that = this,
        promise = new(events.EventEmitter);

    pathname = this.resolve(pathname);

    // Make sure we're not trying to access a
    // file outside of the root.
    if (pathname.indexOf(that.root) === 0) {
        fs.stat(pathname, function (e, stat) {
            if (e) {
                finish(404, {});
            } else if (stat.isFile()) {      // Stream a single file.
                that.respond(null, status, headers, [pathname], stat, req, res, finish);
            } else if (stat.isDirectory()) { // Stream a directory of files.
                that.serveDir(pathname, req, res, finish);
            } else {
                finish(400, {});
            }
        });
    } else {
        // Forbidden
        finish(403, {});
    }
    return promise;
};

Server.prototype.resolve = function (pathname) {
    return path.resolve(path.join(this.root, pathname));
};

Server.prototype.serve = function (req, res, callback) {
    var that    = this,
        promise = new(events.EventEmitter),
        pathname;

    var finish = function (status, headers) {
        that.finish(status, headers, req, res, promise, callback);
    };

    try {
        pathname = decodeURI(url.parse(req.url).pathname);
    }
    catch(e) {
        return process.nextTick(function() {
            return finish(400, {});
        });
    }

    process.nextTick(function () {
        that.servePath(pathname, 200, {}, req, res, finish).on('success', function (result) {
            promise.emit('success', result);
        }).on('error', function (err) {
            promise.emit('error');
        });
    });
    if (! callback) { return promise }
};

/* Check if we should consider sending a gzip version of the file based on the
 * file content type and client's Accept-Encoding header value.
 */
Server.prototype.gzipOk = function(req, contentType) {
    var enable = this.options.gzip;
    if(enable &&
        (typeof enable === 'boolean' ||
            (contentType && (enable instanceof RegExp) && enable.test(contentType)))) {
        var acceptEncoding = req.headers['accept-encoding'];
        return acceptEncoding && acceptEncoding.indexOf("gzip") >= 0;
    }
    return false;
}

/* Send a gzipped version of the file if the options and the client indicate gzip is enabled and
 * we find a .gz file mathing the static resource requested.
 */
Server.prototype.respondGzip = function(pathname, status, contentType, _headers, files, stat, req, res, finish) {
    var that = this;
    if(files.length == 1 && this.gzipOk(req, contentType)) {
        var gzFile = files[0] + ".gz";
        fs.stat(gzFile, function(e, gzStat) {
            if(!e && gzStat.isFile()) {
                //console.log('Serving', gzFile, 'to gzip-capable client instead of', files[0], 'new size is', gzStat.size, 'uncompressed size', stat.size);
                var vary = _headers['Vary'];
                _headers['Vary'] = (vary && vary != 'Accept-Encoding'?vary+', ':'')+'Accept-Encoding';
                _headers['Content-Encoding'] = 'gzip';
                stat.size = gzStat.size;
                files = [gzFile];
            } else {
                //console.log('gzip file not found or error finding it', gzFile, String(e), stat.isFile());
            }
            that.respondNoGzip(pathname, status, contentType, _headers, files, stat, req, res, finish);
        });
    } else {
        // Client doesn't want gzip or we're sending multiple files
        that.respondNoGzip(pathname, status, contentType, _headers, files, stat, req, res, finish);
    }
}

Server.prototype.parseByteRange = function(req, stat) {
    var byteRange = {
      from: 0,
      to: 0,
      valid: false
    }

    var rangeHeader = req.headers['range'];
    var flavor = 'bytes=';

    if (rangeHeader) {
        if (rangeHeader.indexOf(flavor) == 0 && rangeHeader.indexOf(',') == -1) {
            /* Parse */
            rangeHeader = rangeHeader.substr(flavor.length).split('-');
            byteRange.from = parseInt(rangeHeader[0]);
            byteRange.to = parseInt(rangeHeader[1]);

            /* Replace empty fields of differential requests by absolute values */
            if (isNaN(byteRange.from) && !isNaN(byteRange.to)) {
                byteRange.from = stat.size - byteRange.to;
                byteRange.to = stat.size ? stat.size - 1 : 0;
            } else if (!isNaN(byteRange.from) && isNaN(byteRange.to)) {
                byteRange.to = stat.size ? stat.size - 1 : 0;
            }

            /* General byte range validation */
            if (!isNaN(byteRange.from) && !!byteRange.to && 0 <= byteRange.from < byteRange.to) {
                byteRange.valid = true;
            } else {
                console.warn("Request contains invalid range header: ", rangeHeader);
            }
        } else {
            console.warn("Request contains unsupported range header: ", rangeHeader);
        }
    }
    return byteRange;
}

Server.prototype.respondNoGzip = function (pathname, status, contentType, _headers, files, stat, req, res, finish) {
    var mtime           = Date.parse(stat.mtime),
        key             = pathname || files[0],
        headers         = {},
        clientETag      = req.headers['if-none-match'],
        clientMTime     = Date.parse(req.headers['if-modified-since']),
        startByte       = 0,
        length          = stat.size,
        byteRange       = this.parseByteRange(req, stat);

    /* Handle byte ranges */
    if (files.length == 1 && byteRange.valid) {
        if (byteRange.to < length) {

            // Note: HTTP Range param is inclusive
            startByte = byteRange.from;
            length = byteRange.to - byteRange.from + 1;
            status = 206;

            // Set Content-Range response header (we advertise initial resource size on server here (stat.size))
            headers['Content-Range'] = 'bytes ' + byteRange.from + '-' + byteRange.to + '/' + stat.size;

        } else {
            byteRange.valid = false;
            console.warn("Range request exceeds file boundaries, goes until byte no", byteRange.to, "against file size of", length, "bytes");
        }
    }

    /* In any case, check for unhandled byte range headers */
    if (!byteRange.valid && req.headers['range']) {
        console.error(new Error("Range request present but invalid, might serve whole file instead"));
    }

    // Copy default headers
    for (var k in this.options.headers) {  headers[k] = this.options.headers[k] }
    // Copy custom headers
    for (var k in _headers) { headers[k] = _headers[k] }

    headers['Etag']          = JSON.stringify([stat.ino, stat.size, mtime].join('-'));
    headers['Date']          = new(Date)().toUTCString();
    headers['Last-Modified'] = new(Date)(stat.mtime).toUTCString();
    headers['Content-Type']   = contentType;
    headers['Content-Length'] = length;

    for (var k in _headers) { headers[k] = _headers[k] }

    // Conditional GET
    // If the "If-Modified-Since" or "If-None-Match" headers
    // match the conditions, send a 304 Not Modified.
    if ((clientMTime  || clientETag) &&
        (!clientETag  || clientETag === headers['Etag']) &&
        (!clientMTime || clientMTime >= mtime)) {
        // 304 response should not contain entity headers
        ['Content-Encoding',
         'Content-Language',
         'Content-Length',
         'Content-Location',
         'Content-MD5',
         'Content-Range',
         'Content-Type',
         'Expires',
         'Last-Modified'].forEach(function(entityHeader) {
            delete headers[entityHeader];
        });
        finish(304, headers);
    } else {

        res.writeHead(status, headers);

        this.stream(key, files, new(buffer.Buffer)(length), startByte, res, function (e, buffer) {
            if (e) { return finish(500, {}) }
            finish(status, headers);
        });
    }
};

Server.prototype.respond = function (pathname, status, _headers, files, stat, req, res, finish) {
    var contentType = _headers['Content-Type'] ||
                      mime.lookup(files[0]) ||
                      'application/octet-stream';

    if(this.options.gzip) {
        this.respondGzip(pathname, status, contentType, _headers, files, stat, req, res, finish);
    } else {
        this.respondNoGzip(pathname, status, contentType, _headers, files, stat, req, res, finish);
    }
}

Server.prototype.stream = function (pathname, files, buffer, startByte, res, callback) {

    (function streamFile(files, offset) {
        var file = files.shift();

        if (file) {
            file = path.resolve(file) === path.normalize(file)  ? file : path.join(pathname || '.', file);

            // Stream the file to the client
            fs.createReadStream(file, {
                flags: 'r',
                mode: 0666,
                start: startByte,
                end: startByte + (buffer.length ? buffer.length - 1 : 0)
            }).on('data', function (chunk) {
                // Bounds check the incoming chunk and offset, as copying
                // a buffer from an invalid offset will throw an error and crash
                if (chunk.length && offset < buffer.length && offset >= 0) {
                    chunk.copy(buffer, offset);
                    offset += chunk.length;
                }
            }).on('close', function () {
                streamFile(files, offset);
            }).on('error', function (err) {
                callback(err);
                console.error(err);
            }).pipe(res, { end: false });
        } else {
            res.end();
            callback(null, buffer, offset);
        }
    })(files.slice(0), 0);
};

// Exports
exports.Server       = Server;
exports.version      = version;
exports.mime         = mime;

<commit_msg>Support `charset` option<commit_after>var fs     = require('fs')
  , events = require('events')
  , buffer = require('buffer')
  , http   = require('http')
  , url    = require('url')
  , path   = require('path')
  , mime   = require('mime')
  , util   = require('./node-static/util');

// Current version
var version = [0, 7, 7];

var Server = function (root, options) {
    if (root && (typeof(root) === 'object')) { options = root; root = null }

    // resolve() doesn't normalize (to lowercase) drive letters on Windows
    this.root    = path.normalize(path.resolve(root || '.'));
    this.options = options || {};
    this.cache   = 3600;

    this.defaultHeaders  = {};
    this.options.headers = this.options.headers || {};

    this.options.indexFile = this.options.indexFile || "index.html";

    if ('cache' in this.options) {
        if (typeof(this.options.cache) === 'number') {
            this.cache = this.options.cache;
        } else if (! this.options.cache) {
            this.cache = false;
        }
    }

    if ('serverInfo' in this.options) {
        this.serverInfo = this.options.serverInfo.toString();
    } else {
        this.serverInfo = 'node-static/' + version.join('.');
    }

    this.defaultHeaders['server'] = this.serverInfo;

    if (this.cache !== false) {
        this.defaultHeaders['cache-control'] = 'max-age=' + this.cache;
    }

    for (var k in this.defaultHeaders) {
        this.options.headers[k] = this.options.headers[k] ||
                                  this.defaultHeaders[k];
    }
};

Server.prototype.serveDir = function (pathname, req, res, finish) {
    var htmlIndex = path.join(pathname, this.options.indexFile),
        that = this;

    fs.stat(htmlIndex, function (e, stat) {
        if (!e) {
            var status = 200;
            var headers = {};
            var originalPathname = decodeURI(url.parse(req.url).pathname);
            if (originalPathname.length && originalPathname.charAt(originalPathname.length - 1) !== '/') {
                return finish(301, { 'Location': originalPathname + '/' });
            } else {
                that.respond(null, status, headers, [htmlIndex], stat, req, res, finish);
            }
        } else {
            // Stream a directory of files as a single file.
            fs.readFile(path.join(pathname, 'index.json'), function (e, contents) {
                if (e) { return finish(404, {}) }
                var index = JSON.parse(contents);
                streamFiles(index.files);
            });
        }
    });
    function streamFiles(files) {
        util.mstat(pathname, files, function (e, stat) {
            if (e) { return finish(404, {}) }
            that.respond(pathname, 200, {}, files, stat, req, res, finish);
        });
    }
};

Server.prototype.serveFile = function (pathname, status, headers, req, res) {
    var that = this;
    var promise = new(events.EventEmitter);

    pathname = this.resolve(pathname);

    fs.stat(pathname, function (e, stat) {
        if (e) {
            return promise.emit('error', e);
        }
        that.respond(null, status, headers, [pathname], stat, req, res, function (status, headers) {
            that.finish(status, headers, req, res, promise);
        });
    });
    return promise;
};

Server.prototype.finish = function (status, headers, req, res, promise, callback) {
    var result = {
        status:  status,
        headers: headers,
        message: http.STATUS_CODES[status]
    };

    headers['server'] = this.serverInfo;

    if (!status || status >= 400) {
        if (callback) {
            callback(result);
        } else {
            if (promise.listeners('error').length > 0) {
                promise.emit('error', result);
            }
            else {
              res.writeHead(status, headers);
              res.end();
            }
        }
    } else {
        // Don't end the request here, if we're streaming;
        // it's taken care of in `prototype.stream`.
        if (status !== 200 || req.method !== 'GET') {
            res.writeHead(status, headers);
            res.end();
        }
        callback && callback(null, result);
        promise.emit('success', result);
    }
};

Server.prototype.servePath = function (pathname, status, headers, req, res, finish) {
    var that = this,
        promise = new(events.EventEmitter);

    pathname = this.resolve(pathname);

    // Make sure we're not trying to access a
    // file outside of the root.
    if (pathname.indexOf(that.root) === 0) {
        fs.stat(pathname, function (e, stat) {
            if (e) {
                finish(404, {});
            } else if (stat.isFile()) {      // Stream a single file.
                that.respond(null, status, headers, [pathname], stat, req, res, finish);
            } else if (stat.isDirectory()) { // Stream a directory of files.
                that.serveDir(pathname, req, res, finish);
            } else {
                finish(400, {});
            }
        });
    } else {
        // Forbidden
        finish(403, {});
    }
    return promise;
};

Server.prototype.resolve = function (pathname) {
    return path.resolve(path.join(this.root, pathname));
};

Server.prototype.serve = function (req, res, callback) {
    var that    = this,
        promise = new(events.EventEmitter),
        pathname;

    var finish = function (status, headers) {
        that.finish(status, headers, req, res, promise, callback);
    };

    try {
        pathname = decodeURI(url.parse(req.url).pathname);
    }
    catch(e) {
        return process.nextTick(function() {
            return finish(400, {});
        });
    }

    process.nextTick(function () {
        that.servePath(pathname, 200, {}, req, res, finish).on('success', function (result) {
            promise.emit('success', result);
        }).on('error', function (err) {
            promise.emit('error');
        });
    });
    if (! callback) { return promise }
};

/* Check if we should consider sending a gzip version of the file based on the
 * file content type and client's Accept-Encoding header value.
 */
Server.prototype.gzipOk = function(req, contentType) {
    var enable = this.options.gzip;
    if(enable &&
        (typeof enable === 'boolean' ||
            (contentType && (enable instanceof RegExp) && enable.test(contentType)))) {
        var acceptEncoding = req.headers['accept-encoding'];
        return acceptEncoding && acceptEncoding.indexOf("gzip") >= 0;
    }
    return false;
}

/* Send a gzipped version of the file if the options and the client indicate gzip is enabled and
 * we find a .gz file mathing the static resource requested.
 */
Server.prototype.respondGzip = function(pathname, status, contentType, _headers, files, stat, req, res, finish) {
    var that = this;
    if(files.length == 1 && this.gzipOk(req, contentType)) {
        var gzFile = files[0] + ".gz";
        fs.stat(gzFile, function(e, gzStat) {
            if(!e && gzStat.isFile()) {
                //console.log('Serving', gzFile, 'to gzip-capable client instead of', files[0], 'new size is', gzStat.size, 'uncompressed size', stat.size);
                var vary = _headers['Vary'];
                _headers['Vary'] = (vary && vary != 'Accept-Encoding'?vary+', ':'')+'Accept-Encoding';
                _headers['Content-Encoding'] = 'gzip';
                stat.size = gzStat.size;
                files = [gzFile];
            } else {
                //console.log('gzip file not found or error finding it', gzFile, String(e), stat.isFile());
            }
            that.respondNoGzip(pathname, status, contentType, _headers, files, stat, req, res, finish);
        });
    } else {
        // Client doesn't want gzip or we're sending multiple files
        that.respondNoGzip(pathname, status, contentType, _headers, files, stat, req, res, finish);
    }
}

Server.prototype.parseByteRange = function(req, stat) {
    var byteRange = {
      from: 0,
      to: 0,
      valid: false
    }

    var rangeHeader = req.headers['range'];
    var flavor = 'bytes=';

    if (rangeHeader) {
        if (rangeHeader.indexOf(flavor) == 0 && rangeHeader.indexOf(',') == -1) {
            /* Parse */
            rangeHeader = rangeHeader.substr(flavor.length).split('-');
            byteRange.from = parseInt(rangeHeader[0]);
            byteRange.to = parseInt(rangeHeader[1]);

            /* Replace empty fields of differential requests by absolute values */
            if (isNaN(byteRange.from) && !isNaN(byteRange.to)) {
                byteRange.from = stat.size - byteRange.to;
                byteRange.to = stat.size ? stat.size - 1 : 0;
            } else if (!isNaN(byteRange.from) && isNaN(byteRange.to)) {
                byteRange.to = stat.size ? stat.size - 1 : 0;
            }

            /* General byte range validation */
            if (!isNaN(byteRange.from) && !!byteRange.to && 0 <= byteRange.from < byteRange.to) {
                byteRange.valid = true;
            } else {
                console.warn("Request contains invalid range header: ", rangeHeader);
            }
        } else {
            console.warn("Request contains unsupported range header: ", rangeHeader);
        }
    }
    return byteRange;
}

Server.prototype.respondNoGzip = function (pathname, status, contentType, _headers, files, stat, req, res, finish) {
    var mtime           = Date.parse(stat.mtime),
        key             = pathname || files[0],
        headers         = {},
        clientETag      = req.headers['if-none-match'],
        clientMTime     = Date.parse(req.headers['if-modified-since']),
        startByte       = 0,
        length          = stat.size,
        byteRange       = this.parseByteRange(req, stat);

    /* Handle byte ranges */
    if (files.length == 1 && byteRange.valid) {
        if (byteRange.to < length) {

            // Note: HTTP Range param is inclusive
            startByte = byteRange.from;
            length = byteRange.to - byteRange.from + 1;
            status = 206;

            // Set Content-Range response header (we advertise initial resource size on server here (stat.size))
            headers['Content-Range'] = 'bytes ' + byteRange.from + '-' + byteRange.to + '/' + stat.size;

        } else {
            byteRange.valid = false;
            console.warn("Range request exceeds file boundaries, goes until byte no", byteRange.to, "against file size of", length, "bytes");
        }
    }

    /* In any case, check for unhandled byte range headers */
    if (!byteRange.valid && req.headers['range']) {
        console.error(new Error("Range request present but invalid, might serve whole file instead"));
    }

    // Copy default headers
    for (var k in this.options.headers) {  headers[k] = this.options.headers[k] }
    // Copy custom headers
    for (var k in _headers) { headers[k] = _headers[k] }

    headers['Etag']          = JSON.stringify([stat.ino, stat.size, mtime].join('-'));
    headers['Date']          = new(Date)().toUTCString();
    headers['Last-Modified'] = new(Date)(stat.mtime).toUTCString();
    headers['Content-Type']   = contentType;
    headers['Content-Length'] = length;

    for (var k in _headers) { headers[k] = _headers[k] }

    // Conditional GET
    // If the "If-Modified-Since" or "If-None-Match" headers
    // match the conditions, send a 304 Not Modified.
    if ((clientMTime  || clientETag) &&
        (!clientETag  || clientETag === headers['Etag']) &&
        (!clientMTime || clientMTime >= mtime)) {
        // 304 response should not contain entity headers
        ['Content-Encoding',
         'Content-Language',
         'Content-Length',
         'Content-Location',
         'Content-MD5',
         'Content-Range',
         'Content-Type',
         'Expires',
         'Last-Modified'].forEach(function(entityHeader) {
            delete headers[entityHeader];
        });
        finish(304, headers);
    } else {

        res.writeHead(status, headers);

        this.stream(key, files, new(buffer.Buffer)(length), startByte, res, function (e, buffer) {
            if (e) { return finish(500, {}) }
            finish(status, headers);
        });
    }
};

Server.prototype.respond = function (pathname, status, _headers, files, stat, req, res, finish) {
    var contentType = _headers['Content-Type'] ||
                      mime.lookup(files[0]) + (this.options.charset ? '; charset=' + this.options.charset : '') ||
                      'application/octet-stream';

    if(this.options.gzip) {
        this.respondGzip(pathname, status, contentType, _headers, files, stat, req, res, finish);
    } else {
        this.respondNoGzip(pathname, status, contentType, _headers, files, stat, req, res, finish);
    }
}

Server.prototype.stream = function (pathname, files, buffer, startByte, res, callback) {

    (function streamFile(files, offset) {
        var file = files.shift();

        if (file) {
            file = path.resolve(file) === path.normalize(file)  ? file : path.join(pathname || '.', file);

            // Stream the file to the client
            fs.createReadStream(file, {
                flags: 'r',
                mode: 0666,
                start: startByte,
                end: startByte + (buffer.length ? buffer.length - 1 : 0)
            }).on('data', function (chunk) {
                // Bounds check the incoming chunk and offset, as copying
                // a buffer from an invalid offset will throw an error and crash
                if (chunk.length && offset < buffer.length && offset >= 0) {
                    chunk.copy(buffer, offset);
                    offset += chunk.length;
                }
            }).on('close', function () {
                streamFile(files, offset);
            }).on('error', function (err) {
                callback(err);
                console.error(err);
            }).pipe(res, { end: false });
        } else {
            res.end();
            callback(null, buffer, offset);
        }
    })(files.slice(0), 0);
};

// Exports
exports.Server       = Server;
exports.version      = version;
exports.mime         = mime;

<|endoftext|>


<commit_before>var Bvalidator = require('../../models/alpha_report/bvalidator.js');
var revalidator = require('revalidator');
var RawFeedService = require('./raw_feed');
var actionEmitter = require('../action_emitter.js');

module.exports = function(models, io, logger) {
	var me = this;

	var validationModel = models.alphaReportValidation;

	var services = {
		alphaReportService: me,
		rawFeedService: new RawFeedService(models, io, logger)
	};
	var bvalidator = new Bvalidator(services, logger);

	/**
	 *	Returns a list of all the alpha reports
	 */
	me.list = function(req, callback){
		paramHandler.handleDefaultParams(req, function(params){
			if (params !== null){
				var sortObject = {};
				sortObject[params.sortKey] = params.sort;

				var config = {
					createdDate : {
						$gte: params.start,
						$lte: params.end
					}
				};

				models.alphaReport.find(config).skip(params.offset).sort(sortObject).limit(params.count).execFind(function(error, response){
					callback(error, response, config);
				});
			} else {
				models.alphaReport.find({}, function(error, response){
					callback(error, response, {});
				});
			}
		});
	};

	me.getIndexes = function(req, callback){
		var keys = Object.keys(models.alphaReport.schema.paths);
		var indexes = ["_id"];
		for (var i = 0; i < keys.length; i++){
			if (models.alphaReport.schema.paths[keys[i]]._index){
				indexes.push(keys[i].toString());
			}
		}

		callback(indexes);
	};

	me.getTotalCount = function(config, callback){
		models.alphaReport.count(config, callback);
	};

	me.listFields = function(params, field_string, callback) {
		models.alphaReport.find(params, field_string, callback);
	};

	/**
	 * create is a "generic" save method callable from both
	 * request-response methods and parser-type methods for population of AlphaReport data
	 *
	 * saveAlphaReport calls the validateAlphaReport module to ensure that the
	 * data being saved to the database is complete and has integrity.
	 *
	 * saveCallback takes the form of  function(err, valid object, AlphaReport object)
	 */
	me.create = function(data, saveCallback) {
		me.validateAlphaReport(data, function(valid) {
			if (valid.valid) {
				logger.info("Valid AlphaReport");
				var newAlphaReport = new models.alphaReport(data);
				newAlphaReport.save(function(err){
					if(err){
						logger.error('Error saving AlphaReport ', err);
					} else {
						actionEmitter.saveAlphaReportEvent({data: newAlphaReport});
					}
					saveCallback(err, valid, newAlphaReport);

				});
			}
			else {
				saveCallback(undefined, valid, data);
			}
		});
	};

	/**
	 * validateAlphaReport validates an AlphaReport object against the AlphaReport semantic rules
	 * and the business rules associated with an AlphaReport
	 *
	 * validateAlphaReport calls the JSON validation module  revalidator and
	 * calls the business validation module bvalidator for the AlphaReport object

	 * data is the object being validated
	 *
	 * valCallback takes the form of  function(valid structure)
	 */
	me.validateAlphaReport = function(data, valCallback) {
		// is the JSON semantically valid for the location object?
		var valid = revalidator.validate(data, validationModel);
		if (valid.valid) {
			// does the location object comply with business validation logic
			bvalidator.validate(data, function(valid) {
				valCallback(valid);
			});
		}
		else {

			valCallback(valid);
		}
	};

	/**
	 * Returns the alpha report object with id specified in URL
	 */
	me.get = function(id, callback){
		me.findWhere({_id: id}, callback);
	};

	/**
	 * readAlphaReportByProperty is a generic read method to return all of
	 * documents that have a property value that matches.
	 */
	me.findWhere = function(config, callback){
		models.alphaReport.find(config, callback);
	};

	me.update = function(id, data, updCallback) {
		me.get(id, function(err, docs){
			if(err) {
				logger.info("Error getting AlphaReport "+err);
				updCallback(err, null, data);
			} else if(docs) {
				docs = docs[0];//There will only be one alpha report from the get
				for(var e in data){
					if(e !== '_id') {
						docs[e] = data[e];
					}
				}
				docs.updatedDate = new Date();
				me.validateAlphaReport(docs, function(valid){
					if (valid.valid) {
						docs.save(function(err){
							if(err){
								updCallback(err, valid, data);
							} else {
								updCallback(err, valid, docs);
							}
						});
					} else {
						valid.valid = false;
						valid.errors = {expected: id, message: "Updated Alpha Report information not valid"};
						updCallback(err, valid, data);
					}
				});
			} else {
					var errorMSG = new Error("Could not find Alpha Report to Update");
					updCallback(errorMSG, null, data);
			}
		});
	};

	me.del = function(config, callback){
		models.alphaReport.remove(config, callback);
	};
};<commit_msg>and forgotten require paramHandler<commit_after>var Bvalidator = require('../../models/alpha_report/bvalidator.js');
var revalidator = require('revalidator');
var RawFeedService = require('./raw_feed');
var actionEmitter = require('../action_emitter.js');
var paramHandler = require('../list_default_handler.js');

module.exports = function(models, io, logger) {
	var me = this;

	var validationModel = models.alphaReportValidation;

	var services = {
		alphaReportService: me,
		rawFeedService: new RawFeedService(models, io, logger)
	};
	var bvalidator = new Bvalidator(services, logger);

	/**
	 *	Returns a list of all the alpha reports
	 */
	me.list = function(req, callback){
		paramHandler.handleDefaultParams(req, function(params){
			if (params !== null){
				var sortObject = {};
				sortObject[params.sortKey] = params.sort;

				var config = {
					createdDate : {
						$gte: params.start,
						$lte: params.end
					}
				};

				models.alphaReport.find(config).skip(params.offset).sort(sortObject).limit(params.count).execFind(function(error, response){
					callback(error, response, config);
				});
			} else {
				models.alphaReport.find({}, function(error, response){
					callback(error, response, {});
				});
			}
		});
	};

	me.getIndexes = function(req, callback){
		var keys = Object.keys(models.alphaReport.schema.paths);
		var indexes = ["_id"];
		for (var i = 0; i < keys.length; i++){
			if (models.alphaReport.schema.paths[keys[i]]._index){
				indexes.push(keys[i].toString());
			}
		}

		callback(indexes);
	};

	me.getTotalCount = function(config, callback){
		models.alphaReport.count(config, callback);
	};

	me.listFields = function(params, field_string, callback) {
		models.alphaReport.find(params, field_string, callback);
	};

	/**
	 * create is a "generic" save method callable from both
	 * request-response methods and parser-type methods for population of AlphaReport data
	 *
	 * saveAlphaReport calls the validateAlphaReport module to ensure that the
	 * data being saved to the database is complete and has integrity.
	 *
	 * saveCallback takes the form of  function(err, valid object, AlphaReport object)
	 */
	me.create = function(data, saveCallback) {
		me.validateAlphaReport(data, function(valid) {
			if (valid.valid) {
				logger.info("Valid AlphaReport");
				var newAlphaReport = new models.alphaReport(data);
				newAlphaReport.save(function(err){
					if(err){
						logger.error('Error saving AlphaReport ', err);
					} else {
						actionEmitter.saveAlphaReportEvent({data: newAlphaReport});
					}
					saveCallback(err, valid, newAlphaReport);

				});
			}
			else {
				saveCallback(undefined, valid, data);
			}
		});
	};

	/**
	 * validateAlphaReport validates an AlphaReport object against the AlphaReport semantic rules
	 * and the business rules associated with an AlphaReport
	 *
	 * validateAlphaReport calls the JSON validation module  revalidator and
	 * calls the business validation module bvalidator for the AlphaReport object

	 * data is the object being validated
	 *
	 * valCallback takes the form of  function(valid structure)
	 */
	me.validateAlphaReport = function(data, valCallback) {
		// is the JSON semantically valid for the location object?
		var valid = revalidator.validate(data, validationModel);
		if (valid.valid) {
			// does the location object comply with business validation logic
			bvalidator.validate(data, function(valid) {
				valCallback(valid);
			});
		}
		else {

			valCallback(valid);
		}
	};

	/**
	 * Returns the alpha report object with id specified in URL
	 */
	me.get = function(id, callback){
		me.findWhere({_id: id}, callback);
	};

	/**
	 * readAlphaReportByProperty is a generic read method to return all of
	 * documents that have a property value that matches.
	 */
	me.findWhere = function(config, callback){
		models.alphaReport.find(config, callback);
	};

	me.update = function(id, data, updCallback) {
		me.get(id, function(err, docs){
			if(err) {
				logger.info("Error getting AlphaReport "+err);
				updCallback(err, null, data);
			} else if(docs) {
				docs = docs[0];//There will only be one alpha report from the get
				for(var e in data){
					if(e !== '_id') {
						docs[e] = data[e];
					}
				}
				docs.updatedDate = new Date();
				me.validateAlphaReport(docs, function(valid){
					if (valid.valid) {
						docs.save(function(err){
							if(err){
								updCallback(err, valid, data);
							} else {
								updCallback(err, valid, docs);
							}
						});
					} else {
						valid.valid = false;
						valid.errors = {expected: id, message: "Updated Alpha Report information not valid"};
						updCallback(err, valid, data);
					}
				});
			} else {
					var errorMSG = new Error("Could not find Alpha Report to Update");
					updCallback(errorMSG, null, data);
			}
		});
	};

	me.del = function(config, callback){
		models.alphaReport.remove(config, callback);
	};
};<|endoftext|>