<commit_before>/*
 * Copyright (c) 2012 Jose María Chumo Mata. All rights reserved.
 * Use of this source code is governed by a 3-clause BSD license that can be
 * found in the LICENSE file.
 */

/**
 * Store the list of URLs
 */
var url_list = {'this':[], 'others':[]};

/**
 * Join the two URL list into one:
 */
function joinURLLists() {
	var ans = url_list['this'];

	// don't add dupes
	ans = ans.concat(url_list['others'].filter(function(elem, index, array) {
				return ans.indexOf(elem) == -1;
			})
		);

	return ans;
}

/**
 * Translate text in the popup interface
 */
function i18nalize() {
	var objects = document.getElementsByTagName('*');

	for(var i = 0; i < objects.length; i++) {
		// to understand this see HTML5 custom attributes
		if(objects[i].dataset) {
			if(objects[i].dataset.i18nText) {
				objects[i].innerText = chrome.i18n.getMessage(objects[i].dataset.i18nText);
			}
			if(objects[i].dataset.i18nPlaceholder) {
				objects[i].placeholder = chrome.i18n.getMessage(objects[i].dataset.i18nPlaceholder);
			}
			if(objects[i].dataset.i18nTitle) {
				objects[i].title = chrome.i18n.getMessage(objects[i].dataset.i18nTitle);
			}
		}
	}
}

/**
 * Populate the lists containing the URLs from tabs
 */
function URLsFromTabs() {
	chrome.windows.getCurrent(function(currentWindow) {
		chrome.tabs.query({}, function(tabs) {
			tabs.forEach(function(tab) {
				var id = 'this';
				if(tab.windowId != currentWindow.id) {
					id = 'others';
				}

				if(url_list[id].indexOf(tab.url) == -1) {
					url_list[id].push(tab.url);
				}
			});
		});
	});
}

/**
 * Copies URL from clipboard to tabs
 */
function boardToTabs() {
	var box = document.getElementById('urls');

	var message = 'in_ok_txt'

	if(document.getElementById('clipboard').checked) {
		// clear content just in case?
		box.value = '';
		// WARNING: according to https://developer.mozilla.org/en/DOM/HTMLTextAreaElement
		//			the focus method is obselete.
		// TODO: seek another way to focus on the textarea.
		box.focus();
		//box.autofocus = true;
		document.execCommand('paste');
		message = 'in_ok_cb'
	}

	var num = 0;
	box.value.split(/\s/).forEach(function(url) {
		url = url.trim();
		if(url != "") {
			// check it it contains the URL schema
			if(url.search('^[a-zA-Z]+://') == -1) {
				url = "http://www.google.com/#q="+url;
			}
			chrome.tabs.create({'url': url.trim(), 'active': false});
			num++;
		}
	});

	box.value = '';
	document.getElementById('status').innerHTML = chrome.i18n.getMessage(message, num) ;
	//console.log(num);
}

/**
 * Copies URL from tabs to clipboard
 */
function tabsToBoard() {
	var box = document.getElementById('urls');
	var filtered_urls = filterURLs();
	var message = 'out_ok_txt';

	box.value = filtered_urls.join('\n');

	if(document.getElementById('clipboard').checked) {
		// select text
		box.select();
		// cut it
		document.execCommand('cut');
		message = 'out_ok_board';
	}

	document.getElementById('status').innerHTML = chrome.i18n.getMessage(message, filtered_urls.length) ;
	//console.log(filtered_urls.length);
}

/**
 * Filters URLs according to the value of the serch box.
 *
 * Returns an array with the result.
 */
function filterURLs() {
	var patterns = document.getElementById('filter').value.trim(); // TODO

	if(patterns == '') { // no patterns bro!
		if(document.getElementById('allWindows').checked)
			return joinURLLists();
		else
			return url_list['this'];
	}

	patterns = patterns.split(/\s/);

	var len = patterns.length;
	patterns = patterns.filter(function(elem, index, array) {
		return elem.toLowerCase() != ":all:";
	});

	var unfiltered = url_list['this'];
	// TODO choose one
	// if :all: has been removed or the checkbox y checked
	if(len != patterns.length || document.getElementById('allWindows').checked) {
		document.getElementById('allWindows').checked = true; // in case it's not set
		len = patterns.lenght;
		unfiltered = joinURLLists();
	}

	patterns = patterns.filter(function(elem, index, array) {
		return elem.toLowerCase() != ":clipboard:";
	});

	// TODO choose one (boxes or patterns)
	if(!document.getElementById('clipboard').checked) {
		document.getElementById('clipboard').checked = len != patterns.length;
	}

	if(patterns.length == 0)
		return unfiltered;

	var filtered = [];
	patterns.forEach(function(pat, pat_index, pat_array) {
		unfiltered.filter(function(url, url_index, url_array) {
			// keeping only the unmatched this iterates on less items
			if(url.toLowerCase().indexOf(pat.toLowerCase()) != -1 && filtered.indexOf(url) == -1) {
				// do not store url converted to lower case!!!
				// admins may care about case sensitivity. What? I've seen it!
				filtered.push(url);
				return false;
			}
			return true;
		});
	});

	return filtered;
}

/**
 * Add an event listener for the onload event y the HTML.
 */
document.addEventListener('DOMContentLoaded', function() {
	/* Since all this is asyncrhonous is better to do it here.
	 * This way the URL list will be ready if needed.
	 */
	URLsFromTabs();

	i18nalize();
	document.getElementById('open').addEventListener('click', boardToTabs);
	document.getElementById('copy').addEventListener('click', tabsToBoard);
});

<commit_msg>Fix status message<commit_after>/*
 * Copyright (c) 2012 Jose María Chumo Mata. All rights reserved.
 * Use of this source code is governed by a 3-clause BSD license that can be
 * found in the LICENSE file.
 */

/**
 * Store the list of URLs
 */
var url_list = {'this':[], 'others':[]};

/**
 * Join the two URL list into one:
 */
function joinURLLists() {
	var ans = url_list['this'];

	// don't add dupes
	ans = ans.concat(url_list['others'].filter(function(elem, index, array) {
				return ans.indexOf(elem) == -1;
			})
		);

	return ans;
}

/**
 * Translate text in the popup interface
 */
function i18nalize() {
	var objects = document.getElementsByTagName('*');

	for(var i = 0; i < objects.length; i++) {
		// to understand this see HTML5 custom attributes
		if(objects[i].dataset) {
			if(objects[i].dataset.i18nText) {
				objects[i].innerText = chrome.i18n.getMessage(objects[i].dataset.i18nText);
			}
			if(objects[i].dataset.i18nPlaceholder) {
				objects[i].placeholder = chrome.i18n.getMessage(objects[i].dataset.i18nPlaceholder);
			}
			if(objects[i].dataset.i18nTitle) {
				objects[i].title = chrome.i18n.getMessage(objects[i].dataset.i18nTitle);
			}
		}
	}
}

/**
 * Populate the lists containing the URLs from tabs
 */
function URLsFromTabs() {
	chrome.windows.getCurrent(function(currentWindow) {
		chrome.tabs.query({}, function(tabs) {
			tabs.forEach(function(tab) {
				var id = 'this';
				if(tab.windowId != currentWindow.id) {
					id = 'others';
				}

				if(url_list[id].indexOf(tab.url) == -1) {
					url_list[id].push(tab.url);
				}
			});
		});
	});
}

/**
 * Copies URL from clipboard to tabs
 */
function boardToTabs() {
	var box = document.getElementById('urls');

	var message = 'in_ok_txt'

	if(document.getElementById('clipboard').checked) {
		// clear content just in case?
		box.value = '';
		// WARNING: according to https://developer.mozilla.org/en/DOM/HTMLTextAreaElement
		//			the focus method is obselete.
		// TODO: seek another way to focus on the textarea.
		box.focus();
		//box.autofocus = true;
		document.execCommand('paste');
		message = 'in_ok_cb'
	}

	var num = 0;
	box.value.split(/\s/).forEach(function(url) {
		url = url.trim();
		if(url != "") {
			// check it it contains the URL schema
			if(url.search('^[a-zA-Z]+://') == -1) {
				url = "http://www.google.com/#q="+url;
			}
			chrome.tabs.create({'url': url.trim(), 'active': false});
			num++;
		}
	});

	box.value = '';
	document.getElementById('status').innerHTML = chrome.i18n.getMessage(message, num) ;
	//console.log(num);
}

/**
 * Copies URL from tabs to clipboard
 */
function tabsToBoard() {
	var box = document.getElementById('urls');
	var filtered_urls = filterURLs();
	var message = 'out_ok_txt';

	box.value = filtered_urls.join('\n');

	if(document.getElementById('clipboard').checked) {
		// select text
		box.select();
		// cut it
		document.execCommand('cut');
		message = 'out_ok_cb';
	}

	document.getElementById('status').innerHTML = chrome.i18n.getMessage(message, filtered_urls.length) ;
	//console.log(filtered_urls.length);
}

/**
 * Filters URLs according to the value of the serch box.
 *
 * Returns an array with the result.
 */
function filterURLs() {
	var patterns = document.getElementById('filter').value.trim(); // TODO

	if(patterns == '') { // no patterns bro!
		if(document.getElementById('allWindows').checked)
			return joinURLLists();
		else
			return url_list['this'];
	}

	patterns = patterns.split(/\s/);

	var len = patterns.length;
	patterns = patterns.filter(function(elem, index, array) {
		return elem.toLowerCase() != ":all:";
	});

	var unfiltered = url_list['this'];
	// TODO choose one
	// if :all: has been removed or the checkbox y checked
	if(len != patterns.length || document.getElementById('allWindows').checked) {
		document.getElementById('allWindows').checked = true; // in case it's not set
		len = patterns.lenght;
		unfiltered = joinURLLists();
	}

	patterns = patterns.filter(function(elem, index, array) {
		return elem.toLowerCase() != ":clipboard:";
	});

	// TODO choose one (boxes or patterns)
	if(!document.getElementById('clipboard').checked) {
		document.getElementById('clipboard').checked = len != patterns.length;
	}

	if(patterns.length == 0)
		return unfiltered;

	var filtered = [];
	patterns.forEach(function(pat, pat_index, pat_array) {
		unfiltered.filter(function(url, url_index, url_array) {
			// keeping only the unmatched this iterates on less items
			if(url.toLowerCase().indexOf(pat.toLowerCase()) != -1 && filtered.indexOf(url) == -1) {
				// do not store url converted to lower case!!!
				// admins may care about case sensitivity. What? I've seen it!
				filtered.push(url);
				return false;
			}
			return true;
		});
	});

	return filtered;
}

/**
 * Add an event listener for the onload event y the HTML.
 */
document.addEventListener('DOMContentLoaded', function() {
	/* Since all this is asyncrhonous is better to do it here.
	 * This way the URL list will be ready if needed.
	 */
	URLsFromTabs();

	i18nalize();
	document.getElementById('open').addEventListener('click', boardToTabs);
	document.getElementById('copy').addEventListener('click', tabsToBoard);
});

<|endoftext|>


<commit_before>/** @module api */
import * as auth from './auth.js';

/**
 * Fetches a url using a stored access token, signing the user in
 * if no access token exists
 * @param {string} url The url to fetch
 * @return {Promise<Response>} Fetch response object
 */
const authenticatedFetch = async (url) => {
  const accessToken = auth.getAccessToken();
  if (accessToken) {
    const response = await fetch(url, {
      headers: {
        'Authorization': `Bearer ${accessToken}`,
      },
    });

    if (!response.ok) {
      if (response.status == 401) {
        auth.signIn();
      } else {
        throw new Error(await response.text());
      }
    }

    return response;
  } else {
    auth.signIn();
  }
};

// TODO: Add ability to filter by search query, to
//       later implement with navigation views
/**
 * Fetches a list of the user's google cloud projects recursively
 * @param {string=} pageToken Page token to use for the request
 * @param {Array=} projects Projects fetched from a previous iteration
 * @return {Promise<Array<string>>} List of projects available to the user
 */
const fetchProjects = async (pageToken) => {
  const response = await gapi.client.cloudresourcemanager.projects.list({
    pageToken,
  });
  const data = response.result;

  // If next page token is present in the response, fetch again and
  // concat result to the current project list
  if (data.nextPageToken) {
    if (pageToken) {
      return data.projects.concat(await fetchProjects(data.nextPageToken));
    }
    return data.projects.concat(await fetchProjects(data.nextPageToken))
        .map((project) => project.projectId);
  }

  if (pageToken) {
    return data.projects;
  }
  return data.projects.map((project) => project.projectId);
};

/**
 * Fetches a list of the possible locations for a given project
 * @param {string} projectId Project id to search locations for
 * @return {Promise<Array<string>>} List of locations available for project
 */
const fetchLocations = async (projectId) => {
  const data = await gapi.client.healthcare.projects.locations.list({
    name: `projects/${projectId}`,
  });

  // Return a list of location Id's
  return data.result.locations.map((location) => location.locationId);
};

/**
 * Fetches a list of the datasets in a project location
 * @param {string} projectId Project id
 * @param {string} location Location
 * @return {Promise<Array<string>>} List of datasets available
 */
const fetchDatasets = async (projectId, location) => {
  // TODO: Handle page tokens
  const data = await gapi.client.healthcare.projects.locations.datasets.list({
    parent: `projects/${projectId}/locations/${location}`,
  });

  // Return a list of datasets by only using content of string after last '/'
  return data.result.datasets
      .map((dataset) => dataset.name.split('/').slice(-1)[0]);
};

/**
 * Fetches a list of the dicomStores in a dataset
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @return {Promise<Array<string>>} List of dicomStores available
 */
const fetchDicomStores = async (projectId, location, dataset) => {
  // TODO: Handle page tokens
  const data = await gapi.client.healthcare.projects.locations.datasets
      .dicomStores.list({
        parent: `projects/${projectId}/locations/${location}/` +
          `datasets/${dataset}`,
      });

  // Return a list of dicomStores by only using content of string after last '/'
  return data.result.dicomStores.map((dicomStore) =>
    dicomStore.name.split('/').slice(-1)[0]);
};

/**
 * Fetches a list of studies in a dicom store
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @param {string} dicomStore Dicom Store
 * @return {Promise<Array<Object>>} List of studies in the dicom store
 */
const fetchStudies =
    async (projectId, location, dataset, dicomStore) => {
      const data = await gapi.client.healthcare.projects.locations.datasets
          .dicomStores.searchForStudies({
            parent: `projects/${projectId}/locations/${location}/` +
        `datasets/${dataset}/dicomStores/${dicomStore}`,
            dicomWebPath: 'studies',
          });

      return data.result;
    };

/**
 * Fetches a list of series in a study
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @param {string} dicomStore Dicom Store
 * @param {string} studyId Study UID
 * @return {Promise<Array<Object>>} List of series in the study
 */
const fetchSeries =
    async (projectId, location, dataset, dicomStore, studyId) => {
      const data = await gapi.client.healthcare.projects.locations.datasets
          .dicomStores.studies.searchForSeries({
            parent: `projects/${projectId}/locations/${location}/` +
        `datasets/${dataset}/dicomStores/${dicomStore}`,
            dicomWebPath: `studies/${studyId}/series`,
          });

      return data.result;
    };

export {authenticatedFetch, fetchProjects, fetchLocations, fetchDatasets,
  fetchDicomStores, fetchStudies, fetchSeries};
<commit_msg>Fixed study and series fetch formatting<commit_after>/** @module api */
import * as auth from './auth.js';

/**
 * Fetches a url using a stored access token, signing the user in
 * if no access token exists
 * @param {string} url The url to fetch
 * @return {Promise<Response>} Fetch response object
 */
const authenticatedFetch = async (url) => {
  const accessToken = auth.getAccessToken();
  if (accessToken) {
    const response = await fetch(url, {
      headers: {
        'Authorization': `Bearer ${accessToken}`,
      },
    });

    if (!response.ok) {
      if (response.status == 401) {
        auth.signIn();
      } else {
        throw new Error(await response.text());
      }
    }

    return response;
  } else {
    auth.signIn();
  }
};

// TODO: Add ability to filter by search query, to
//       later implement with navigation views
/**
 * Fetches a list of the user's google cloud projects recursively
 * @param {string=} pageToken Page token to use for the request
 * @param {Array=} projects Projects fetched from a previous iteration
 * @return {Promise<Array<string>>} List of projects available to the user
 */
const fetchProjects = async (pageToken) => {
  const response = await gapi.client.cloudresourcemanager.projects.list({
    pageToken,
  });
  const data = response.result;

  // If next page token is present in the response, fetch again and
  // concat result to the current project list
  if (data.nextPageToken) {
    if (pageToken) {
      return data.projects.concat(await fetchProjects(data.nextPageToken));
    }
    return data.projects.concat(await fetchProjects(data.nextPageToken))
        .map((project) => project.projectId);
  }

  if (pageToken) {
    return data.projects;
  }
  return data.projects.map((project) => project.projectId);
};

/**
 * Fetches a list of the possible locations for a given project
 * @param {string} projectId Project id to search locations for
 * @return {Promise<Array<string>>} List of locations available for project
 */
const fetchLocations = async (projectId) => {
  const data = await gapi.client.healthcare.projects.locations.list({
    name: `projects/${projectId}`,
  });

  // Return a list of location Id's
  return data.result.locations.map((location) => location.locationId);
};

/**
 * Fetches a list of the datasets in a project location
 * @param {string} projectId Project id
 * @param {string} location Location
 * @return {Promise<Array<string>>} List of datasets available
 */
const fetchDatasets = async (projectId, location) => {
  // TODO: Handle page tokens
  const data = await gapi.client.healthcare.projects.locations.datasets.list({
    parent: `projects/${projectId}/locations/${location}`,
  });

  // Return a list of datasets by only using content of string after last '/'
  return data.result.datasets
      .map((dataset) => dataset.name.split('/').slice(-1)[0]);
};

/**
 * Fetches a list of the dicomStores in a dataset
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @return {Promise<Array<string>>} List of dicomStores available
 */
const fetchDicomStores = async (projectId, location, dataset) => {
  // TODO: Handle page tokens
  const data = await gapi.client.healthcare.projects.locations.datasets
      .dicomStores.list({
        parent: `projects/${projectId}/locations/${location}/` +
          `datasets/${dataset}`,
      });

  // Return a list of dicomStores by only using content of string after last '/'
  return data.result.dicomStores.map((dicomStore) =>
    dicomStore.name.split('/').slice(-1)[0]);
};

/**
 * Fetches a list of studies in a dicom store
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @param {string} dicomStore Dicom Store
 * @return {Promise<Array<Object>>} List of studies in the dicom store
 */
const fetchStudies =
    async (projectId, location, dataset, dicomStore) => {
  const data = await gapi.client.healthcare.projects.locations.datasets
      .dicomStores.searchForStudies({
        parent: `projects/${projectId}/locations/${location}/` +
    `datasets/${dataset}/dicomStores/${dicomStore}`,
        dicomWebPath: 'studies',
      });

  return data.result;
};

/**
 * Fetches a list of series in a study
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @param {string} dicomStore Dicom Store
 * @param {string} studyId Study UID
 * @return {Promise<Array<Object>>} List of series in the study
 */
const fetchSeries =
    async (projectId, location, dataset, dicomStore, studyId) => {
  const data = await gapi.client.healthcare.projects.locations.datasets
      .dicomStores.studies.searchForSeries({
        parent: `projects/${projectId}/locations/${location}/` +
    `datasets/${dataset}/dicomStores/${dicomStore}`,
        dicomWebPath: `studies/${studyId}/series`,
      });

  return data.result;
};

export {authenticatedFetch, fetchProjects, fetchLocations, fetchDatasets,
  fetchDicomStores, fetchStudies, fetchSeries};
<|endoftext|>


<commit_before>
const browser = require('./browser');
const logging = require('./logging');

module.exports = async function(req, res) {
  let input = req.body;

  if (!input.url && !input.content) {
    res.status(400);
    res.json({error: 'url or content required'});
    return;
  }

  if (input.url) {
    logging.log('====== URL request');
    logging.log(`====== ${input.url}`);
  } else {
    logging.log('====== Content request');
    logging.log(`====== Length: ${input.content.length}`);
  }

  print(res, input)
    .then(pdf => {
      res.type('pdf');
      res.send(pdf);
    })
    .catch(err => {
      logging.error('PDF generation failed:', err);

      res.status(500);
      res.json({error: err});
    });
};

async function print(res, input) {
  let page = await browser.instance.newPage();
  page.on('request', request => {
    if (request.url().indexOf('data:') === 0) {
      logging.log(`-- data request, length: ${request.url().length}`);
    } else {
      logging.log(`-- request: ${request.url()}`);
    }
  });

  page.on('requestfailed', request => {
    logging.error(`-- request failed: ${request.url()}`);

    if (!headersSent) {
      throw new Error(`request failed: ${request.url()}`);
    }
  });

  logging.log('[*] Navigate to page');
  if (input.url) {
    await page.goto(input.url, {
      timeout: 25000,
      waitUntil: ['networkidle0', 'load'],
    });
  } else {
    await page.goto(`data:text/html,${input.content}`, {
      timeout: 25000,
      waitUntil: ['networkidle0', 'load'],
    });
  }

  logging.log('[*] Generate PDF');
  return await page.pdf({
    printBackground: true,
    format: 'A4',
    displayHeaderFooter: input.header || input.footer,
    headerTemplate: input.header,
    footerTemplate: input.footer,
    margin: {
      top: input.headerHeight,
      bottom: input.footerHeight,
    },
  });
}

<commit_msg>Fix a memory leak and a double check with an undefined variable (#18)<commit_after>
const browser = require('./browser');
const logging = require('./logging');

module.exports = async function(req, res) {
  let input = req.body;

  if (!input.url && !input.content) {
    res.status(400);
    res.json({error: 'url or content required'});
    return;
  }

  if (input.url) {
    logging.log('====== URL request');
    logging.log(`====== ${input.url}`);
  } else {
    logging.log('====== Content request');
    logging.log(`====== Length: ${input.content.length}`);
  }

  print(res, input)
    .then(pdf => {
      res.type('pdf');
      res.send(pdf);
    })
    .catch(err => {
      logging.error('PDF generation failed:', err);

      res.status(500);
      res.json({error: err});
    });
};

async function print(res, input) {
  let page = await browser.instance.newPage();
  page.on('request', request => {
    if (request.url().indexOf('data:') === 0) {
      logging.log(`-- data request, length: ${request.url().length}`);
    } else {
      logging.log(`-- request: ${request.url()}`);
    }
  });

  page.on('requestfailed', request => {
    logging.error(`-- request failed: ${request.url()}`);

    throw new Error(`request failed: ${request.url()}`);
  });

  logging.log('[*] Navigate to page');
  if (input.url) {
    await page.goto(input.url, {
      timeout: 25000,
      waitUntil: ['networkidle0', 'load'],
    });
  } else {
    await page.goto(`data:text/html,${input.content}`, {
      timeout: 25000,
      waitUntil: ['networkidle0', 'load'],
    });
  }

  logging.log('[*] Generate PDF');
  let pdf = await page.pdf({
    printBackground: true,
    format: 'A4',
    displayHeaderFooter: input.header || input.footer,
    headerTemplate: input.header,
    footerTemplate: input.footer,
    margin: {
      top: input.headerHeight,
      bottom: input.footerHeight,
    },
  });

  await page.close();

  return pdf;
}

<|endoftext|>


<commit_before>"use strict";

var registry = require('./registry.js');
var Mailer = require('./mailer.js');
var Promise = require('promise');
var Session = require('./session.js');

var Registry = registry.Registry;
var base64url = registry.base64url;
var bufferFromBase64url = registry.bufferFromBase64url;

var proofError = 'We could not prove email ownership';
var emailRateLimitError = 'Too many requests for email proof';
var invalidEmailError = 'The email address is clearly invalid';
var confirmError = 'We could not confirm email ownership';
var accountError = 'We could not fetch an account';
var logoutError = 'We could not log you out';

// options:
// - mailer: object, see mailer.js
// - db: path to storage point as a string, or constructor, see db.js.
// - emailRateLimit: true by default.
// cb: function(err)
function Api(options, cb) {
  this.registry = new Registry(options.db);
  this.mailer = new Mailer(options.mailer);
  this.registry.setup(cb);
  this.emailRateLimit = (options.emailRateLimit === false)? false: true;
  this.lastProofRequest = Object.create(null);
}

Api.prototype = {
  // cb(error, cookieToken, session)
  login: function(cb) {
    this.registry.login(function(err, secret, session) {
      if (err != null) { return cb(err); }
      var cookieToken = encodeToken(session.id, secret);
      cb(null, cookieToken, session);
    });
  },

  emailRateLimit: true,
  // Map from email to timestamp of last email proof request.
  lastProofRequest: Object.create(null),

  // options:
  // - token (the cookieToken as a string)
  // - email
  // - subject: function(name)
  // - textMessage: function(emailToken, confirmUrl)
  //   (Use emailToken in your confirmation URL. confirmUrl is optional and
  //   meant to be function(emailToken).)
  //   to be the start of an URL including scheme + domain + port.)
  // - htmlMessage: function(emailToken, confirmUrl)
  //   (Use emailToken in your confirmation URL. confirmUrl is optional and
  //   meant to be function(emailToken).)
  // - name (optional): used in the default subject.
  // - confirmUrl (optional): function(emailToken).
  // cb: function(err, emailToken)
  proveEmail: function(options, cb) {
    var cookieToken = options.token;
    var email = options.email;
    var subject = options.subject || defaultSubject;
    var textMessage = options.textMessage || defaultTextMessage;
    var htmlMessage = options.htmlMessage || defaultHtmlMessage;
    var self = this;

    var emailDomain = email.slice(email.lastIndexOf('@'));
    if (emailDomain[0] !== '@') {
      cb(Error(invalidEmailError));
      return;
    }
    var now = Session.currentTime();
    var delay = 0;
    if (this.emailRateLimit) {
      if (this.lastProofRequest[emailDomain] !== undefined) {
        // delay in milliseconds.
        var delay = (1 / (this.lastProofRequest[emailDomain] / 1000) * 1000);
        if (delay > 1000 * 10) {
          // We'd delay for more than 10 seconds.
          cb(Error(emailRateLimitError));
          return;
        }
      }
      this.lastProofRequest[emailDomain] = now;
    }

    setTimeout(function() {
      self.registry.proof(email, function(err, emailSecret, emailSession) {
        if (err != null) { return cb(err); }
        if (emailSecret == null) { return cb(Error(proofError)); }
        var emailToken = encodeToken(emailSession.id, emailSecret);
        self.mailer.send({
          to: email,
          subject: subject(options.name),
          text: textMessage(emailToken, options.confirmUrl),
          html: htmlMessage(emailToken, options.confirmUrl),
        }, function(err) { cb(err, emailToken); });
      });
    }, delay);
  },

  // cb: function(err, cookieToken, session, oldSession)
  // The returned token is undefined if the confirmation failed.
  // The sessions can be undefined.
  confirmEmail: function(cookieToken, emailToken, cb) {
    try {
      var elements = decodeToken(emailToken);
    } catch(e) { return cb(Error(confirmError)); }
    var emailId = elements.id;
    var emailSecret = elements.token;
    if (emailId == null || emailSecret == null) {
      return cb(Error(confirmError));
    }

    var self = this;
    self.registry.auth(emailId, emailSecret, function(err, confirmed, session) {
      if (err != null || !confirmed) {
        if (session !== undefined) {
          // Refresh the session.
          self.registry.load(session.id, function(err, session) {
            if (err != null) { return cb(err); }
            cb(null, undefined, session, session);
          });
        } else { cb(null); }
        return;
      }

      if (cookieToken === undefined) {
        // We received a confirmation from an unknown device.
        self.login(function(err, newToken, newSession) {
          if (err != null) { return cb(err); }
          self.registry.confirmEmailProved(newSession.id, session.email,
          function(err, newSession) {
            if (err != null) { return cb(err); }
            // Burn the emailSession.
            // Whether burning the emailSession succeeded is irrelevant, as we only
            // do it for the memory and cleanliness.
            self.registry.logout(emailId, function(err) {
              cb(null, newToken, newSession, session);
            });
          });
        });

      } else {
        var elements = decodeToken(cookieToken);
        var id = elements.id;
        self.registry.confirmEmailProved(id, session.email,
        function(err, newSession) {
          if (err != null) { return cb(err); }
          // Burn the emailSession.
          self.registry.logout(emailId, function(err) {
            cb(null, cookieToken, newSession, session);
          });
        });
      }
    });
  },

  // cb: function(err, authenticated, session)
  // If authentication failed, session might be undefined.
  authenticate: function(cookieToken, cb) {
    // Any type of invalidity translates to a refusal to authenticate.
    if (!cookieToken) {
      return cb(null, false);
    }
    try {
      var elements = decodeToken(cookieToken);
    } catch(e) { return cb(null, false); }
    var id = elements.id;
    var token = elements.token;
    if (id == null || token == null) {
      return cb(null, false);
    }

    this.registry.auth(id, token, cb);
  },

  // id: base64url session identifier
  // cb(error, session)
  session: function(id, cb) {
    this.registry.load(id, cb);
  },

  // email: account identifier
  // cb: function(err, account)
  // An account is an object with the following fields (not to be confused with
  // registry.js' Account):
  // - email
  // - sessions: list of Session objects.
  account: function(email, cb) {
    var self = this;
    if (email == null) { return cb(Error(accountError)); }
    self.registry.loadAccount(email, function(err, account) {
      if (err != null) { return cb(err); }
      var sessionLoaders = [];
      account.sessionIds.forEach(function(sessionId) {
        sessionLoaders.push(new Promise(function(resolve, reject) {
          self.registry.load(sessionId, function(err, session) {
            if (err != null) { return reject(err); }
            resolve(session);
          });
        }));
      });
      Promise.all(sessionLoaders).then(function(sessions) {
        cb(null, {
          email: email,
          sessions: sessions,
        });
      }).catch(function(err) {
        cb(err);
      });
    });
  },

  // cb: function(error)
  // Remove the session associated with the cookieToken.
  logout: function(cookieToken, cb) {
    if (!cookieToken) {
      return cb(null);
    }
    try {
      var elements = decodeToken(cookieToken);
    } catch(e) { return cb(Error(logoutError)); }
    var id = elements.id;
    var token = elements.token;
    if (id == null || token == null) {
      return cb(Error(logoutError));
    }

    this.registry.logout(id, cb);
  },

  // cb: function(error)
  // Remove the session associated with the sessionId.
  deleteSession: function(sessionId, cb) {
    this.registry.logout(sessionId, cb);
  },

  // cb: function(error)
  // Delete all sessions and information associated to an email.
  deleteAccount: function(email, cb) {
    this.registry.rmAccount(email, cb);
  },
};

// Primitives

// Return {id: base64url, token: base64, version: int}
function decodeToken(base64) {
  var elements = base64.split('.');
  var version = +elements[0];
  var id = elements[1];
  var tokenBase64url = elements[2];
  return {
    id: id,
    token: tokenBase64url.replace(/\-/g, '+').replace(/_/g, '/'),
    version: version,
  };
}

// id: base64url, secret: Buffer, version: int.
// The link token is
// <version>.<id as base64url>.<base64url of the secret>
function encodeToken(id, secret, version) {
  version = version || 1;
  return version + '.' + id + '.' + base64url(secret);
}

function defaultSubject(name) {
  return '[' + name + '] Identity verification'
}

function defaultConfirmUrl(emailToken) {
  return 'https://127.0.0.1/login?token=' + emailToken;
}

function defaultTextMessage(emailToken, confirmUrl) {
  confirmUrl = confirmUrl || defaultConfirmUrl;
  return 'Hi!\n\n' +
    'You can confirm that you own this email address by clicking ' +
    'on this link:\n\n' + confirmUrl(emailToken) + '\n\n' +
    'Please point your browser to that URL and you will be good to go!\n\n' +
    'Cheers!';
}

function defaultHtmlMessage(emailToken, confirmUrl) {
  confirmUrl = confirmUrl || defaultConfirmUrl;
  var link = escapeHtml(confirmUrl(emailToken));
  return '
Hi!
\n\n' +
    '
You can confirm that you own this email address by clicking ' +
    '' + 'here.
' +
    '
Cheers!
';
}

function escapeHtml(text) {
  return text.replace(/\&/g, '&').replace(/</g, '<').replace(/>/g, '>');
}

module.exports = Api;
module.exports.encodeToken = encodeToken;
module.exports.decodeToken = decodeToken;
<commit_msg>Email emission rate limit, interval-based<commit_after>"use strict";

var registry = require('./registry.js');
var Mailer = require('./mailer.js');
var Promise = require('promise');
var Session = require('./session.js');

var Registry = registry.Registry;
var base64url = registry.base64url;
var bufferFromBase64url = registry.bufferFromBase64url;

var proofError = 'We could not prove email ownership';
var emailRateLimitError = 'Too many requests for email proof';
var invalidEmailError = 'The email address is clearly invalid';
var confirmError = 'We could not confirm email ownership';
var accountError = 'We could not fetch an account';
var logoutError = 'We could not log you out';

// options:
// - mailer: object, see mailer.js
// - db: path to storage point as a string, or constructor, see db.js.
// - emailRateLimit: true by default.
// cb: function(err)
function Api(options, cb) {
  this.registry = new Registry(options.db);
  this.mailer = new Mailer(options.mailer);
  this.registry.setup(cb);
  this.emailRateLimit = (options.emailRateLimit === false)? false: true;
  this.nextProofRequest = Object.create(null);
}

Api.prototype = {
  // cb(error, cookieToken, session)
  login: function(cb) {
    this.registry.login(function(err, secret, session) {
      if (err != null) { return cb(err); }
      var cookieToken = encodeToken(session.id, secret);
      cb(null, cookieToken, session);
    });
  },

  emailRateLimit: true,
  // Map from email to minimum timestamp of next email proof request.
  nextProofRequest: Object.create(null),

  // options:
  // - token (the cookieToken as a string)
  // - email
  // - subject: function(name)
  // - textMessage: function(emailToken, confirmUrl)
  //   (Use emailToken in your confirmation URL. confirmUrl is optional and
  //   meant to be function(emailToken).)
  //   to be the start of an URL including scheme + domain + port.)
  // - htmlMessage: function(emailToken, confirmUrl)
  //   (Use emailToken in your confirmation URL. confirmUrl is optional and
  //   meant to be function(emailToken).)
  // - name (optional): used in the default subject.
  // - confirmUrl (optional): function(emailToken).
  // cb: function(err, emailToken)
  proveEmail: function(options, cb) {
    var cookieToken = options.token;
    var email = options.email;
    var subject = options.subject || defaultSubject;
    var textMessage = options.textMessage || defaultTextMessage;
    var htmlMessage = options.htmlMessage || defaultHtmlMessage;
    var self = this;

    var emailDomain = email.slice(email.lastIndexOf('@'));
    if (emailDomain[0] !== '@') {
      cb(Error(invalidEmailError));
      return;
    }
    var now = Session.currentTime();

    // Ensure we keep a 500ms delay between each email to a mail server.
    var mailEmissionLapse = 500;
    // delay in milliseconds.
    var delay = 0;
    if (this.emailRateLimit) {
      if (this.nextProofRequest[emailDomain] !== undefined) {
        if (now < this.nextProofRequest[emailDomain] + mailEmissionLapse) {
          this.nextProofRequest[emailDomain] += mailEmissionLapse;
          delay = this.nextProofRequest[emailDomain];
        }
        if (delay > 1000 * 60 * 5) {
          // We'd delay for more than 5 minutes.
          cb(Error(emailRateLimitError));
          return;
        }
      } else {
        this.nextProofRequest[emailDomain] = now + mailEmissionLapse;
      }
    }

    setTimeout(function() {
      self.registry.proof(email, function(err, emailSecret, emailSession) {
        if (err != null) { return cb(err); }
        if (emailSecret == null) { return cb(Error(proofError)); }
        var emailToken = encodeToken(emailSession.id, emailSecret);
        self.mailer.send({
          to: email,
          subject: subject(options.name),
          text: textMessage(emailToken, options.confirmUrl),
          html: htmlMessage(emailToken, options.confirmUrl),
        }, function(err) { cb(err, emailToken); });
      });
    }, delay);
  },

  // cb: function(err, cookieToken, session, oldSession)
  // The returned token is undefined if the confirmation failed.
  // The sessions can be undefined.
  confirmEmail: function(cookieToken, emailToken, cb) {
    try {
      var elements = decodeToken(emailToken);
    } catch(e) { return cb(Error(confirmError)); }
    var emailId = elements.id;
    var emailSecret = elements.token;
    if (emailId == null || emailSecret == null) {
      return cb(Error(confirmError));
    }

    var self = this;
    self.registry.auth(emailId, emailSecret, function(err, confirmed, session) {
      if (err != null || !confirmed) {
        if (session !== undefined) {
          // Refresh the session.
          self.registry.load(session.id, function(err, session) {
            if (err != null) { return cb(err); }
            cb(null, undefined, session, session);
          });
        } else { cb(null); }
        return;
      }

      if (cookieToken === undefined) {
        // We received a confirmation from an unknown device.
        self.login(function(err, newToken, newSession) {
          if (err != null) { return cb(err); }
          self.registry.confirmEmailProved(newSession.id, session.email,
          function(err, newSession) {
            if (err != null) { return cb(err); }
            // Burn the emailSession.
            // Whether burning the emailSession succeeded is irrelevant, as we only
            // do it for the memory and cleanliness.
            self.registry.logout(emailId, function(err) {
              cb(null, newToken, newSession, session);
            });
          });
        });

      } else {
        var elements = decodeToken(cookieToken);
        var id = elements.id;
        self.registry.confirmEmailProved(id, session.email,
        function(err, newSession) {
          if (err != null) { return cb(err); }
          // Burn the emailSession.
          self.registry.logout(emailId, function(err) {
            cb(null, cookieToken, newSession, session);
          });
        });
      }
    });
  },

  // cb: function(err, authenticated, session)
  // If authentication failed, session might be undefined.
  authenticate: function(cookieToken, cb) {
    // Any type of invalidity translates to a refusal to authenticate.
    if (!cookieToken) {
      return cb(null, false);
    }
    try {
      var elements = decodeToken(cookieToken);
    } catch(e) { return cb(null, false); }
    var id = elements.id;
    var token = elements.token;
    if (id == null || token == null) {
      return cb(null, false);
    }

    this.registry.auth(id, token, cb);
  },

  // id: base64url session identifier
  // cb(error, session)
  session: function(id, cb) {
    this.registry.load(id, cb);
  },

  // email: account identifier
  // cb: function(err, account)
  // An account is an object with the following fields (not to be confused with
  // registry.js' Account):
  // - email
  // - sessions: list of Session objects.
  account: function(email, cb) {
    var self = this;
    if (email == null) { return cb(Error(accountError)); }
    self.registry.loadAccount(email, function(err, account) {
      if (err != null) { return cb(err); }
      var sessionLoaders = [];
      account.sessionIds.forEach(function(sessionId) {
        sessionLoaders.push(new Promise(function(resolve, reject) {
          self.registry.load(sessionId, function(err, session) {
            if (err != null) { return reject(err); }
            resolve(session);
          });
        }));
      });
      Promise.all(sessionLoaders).then(function(sessions) {
        cb(null, {
          email: email,
          sessions: sessions,
        });
      }).catch(function(err) {
        cb(err);
      });
    });
  },

  // cb: function(error)
  // Remove the session associated with the cookieToken.
  logout: function(cookieToken, cb) {
    if (!cookieToken) {
      return cb(null);
    }
    try {
      var elements = decodeToken(cookieToken);
    } catch(e) { return cb(Error(logoutError)); }
    var id = elements.id;
    var token = elements.token;
    if (id == null || token == null) {
      return cb(Error(logoutError));
    }

    this.registry.logout(id, cb);
  },

  // cb: function(error)
  // Remove the session associated with the sessionId.
  deleteSession: function(sessionId, cb) {
    this.registry.logout(sessionId, cb);
  },

  // cb: function(error)
  // Delete all sessions and information associated to an email.
  deleteAccount: function(email, cb) {
    this.registry.rmAccount(email, cb);
  },
};

// Primitives

// Return {id: base64url, token: base64, version: int}
function decodeToken(base64) {
  var elements = base64.split('.');
  var version = +elements[0];
  var id = elements[1];
  var tokenBase64url = elements[2];
  return {
    id: id,
    token: tokenBase64url.replace(/\-/g, '+').replace(/_/g, '/'),
    version: version,
  };
}

// id: base64url, secret: Buffer, version: int.
// The link token is
// <version>.<id as base64url>.<base64url of the secret>
function encodeToken(id, secret, version) {
  version = version || 1;
  return version + '.' + id + '.' + base64url(secret);
}

function defaultSubject(name) {
  return '[' + name + '] Identity verification'
}

function defaultConfirmUrl(emailToken) {
  return 'https://127.0.0.1/login?token=' + emailToken;
}

function defaultTextMessage(emailToken, confirmUrl) {
  confirmUrl = confirmUrl || defaultConfirmUrl;
  return 'Hi!\n\n' +
    'You can confirm that you own this email address by clicking ' +
    'on this link:\n\n' + confirmUrl(emailToken) + '\n\n' +
    'Please point your browser to that URL and you will be good to go!\n\n' +
    'Cheers!';
}

function defaultHtmlMessage(emailToken, confirmUrl) {
  confirmUrl = confirmUrl || defaultConfirmUrl;
  var link = escapeHtml(confirmUrl(emailToken));
  return '
Hi!
\n\n' +
    '
You can confirm that you own this email address by clicking ' +
    '' + 'here.
' +
    '
Cheers!
';
}

function escapeHtml(text) {
  return text.replace(/\&/g, '&').replace(/</g, '<').replace(/>/g, '>');
}

module.exports = Api;
module.exports.encodeToken = encodeToken;
module.exports.decodeToken = decodeToken;
<|endoftext|>


<commit_before>'use strict';

require('isomorphic-fetch');
const credentials = require('./credentials');

const models = require('./models');
const channels = require('./channels');
const config = require('./config');

const fetch_ = (path, options) => {
  const url = config.host + path;
  options.headers = options.headers || {};
  return Promise.resolve()
    .then(() => credentials.getToken())
    .then(token => {
      options.headers.Authorization = 'Bearer ' + token;
      return fetch(url, options);
    })
    .then(response => {
      return response.json();
    })
    .catch(error => {
      throw new Error(error);
    });
};

const createQueryString = obj => {
  var parts = ['?'];
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
      parts.push(encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]));
    }
  }
  return parts.join('&');
};

const get = (path, params) => {
  if (params) path += createQueryString(params);
  return fetch_(path, { method: 'get' });
};

const getCurrentDevice = () => {
  return Promise.resolve()
    .then(() => {
      return channels.system.request({ name: 'getCurrentDevice' });
    })
    .then(device => {
      return new models.Device({ device: device, current: true });
    });
};

const getCurrentExperience = () => {
  return Promise.resolve()
    .then(() => {
      return channels.system.request({ name: 'getCurrentExperience' });
    })
    .then(experience => {
      return new models.Experience({ experience: experience });
    });
};

const getDevice = uuid => {
  return Promise.resolve()
    .then(() => {
      if (!uuid) throw new Error('uuidRequired');
      return get('/api/devices/' + uuid);
    })
    .then(device => {
      return new models.Device({ device: device });
    });
};

const getDevices = params => {
  return Promise.resolve()
    .then(() => {
      return get('/api/devices', params);
    })
    .then(query => {
      const devices = [];
      query.results.forEach(device => {
        devices.push(new models.Device({ device: device }));
      });
      return devices;
    });
};

const getExperience = uuid => {
  return Promise.resolve()
    .then(() => {
      if (!uuid) throw new Error('uuidRequired');
      return get('/api/experiences/' + uuid);
    })
    .then(experience => {
      return new models.Experience({ experience: experience });
    });
};

const getExperiences = params => {
  return Promise.resolve()
    .then(() => {
      return get('/api/experiences', params);
    })
    .then(query => {
      const experiences = [];
      query.results.forEach(experience => {
        experiences.push(new models.Experience({ experience: experience }));
      });
      return experiences;
    });
};

const getLocation = uuid => {
  return Promise.resolve()
    .then(() => {
      if (!uuid) throw new Error('uuidRequired');
      return get('/api/locations/' + uuid);
    })
    .then(location => {
      return new models.Location({ location: location });
    });
};

const getLocations = params => {
  return Promise.resolve()
    .then(() => {
      return get('/api/locations', params);
    })
    .then(query => {
      const locations = [];
      query.results.forEach(location => {
        locations.push(new models.Location({ location: location }));
      });
      return locations;
    });
};

const getZone = uuid => {
  return Promise.resolve()
    .then(() => {
      if (!uuid) throw new Error('uuidRequired');
      return get('/api/zones/' + uuid);
    })
    .then(zone => {
      return new models.Zone({ zone: zone });
    });
};

const getZones = params => {
  return Promise.resolve()
    .then(() => {
      return get('/api/zones', params);
    })
    .then(query => {
      const zones = [];
      query.results.forEach(zone => {
        zones.push(new models.Zone({ zone: zone }));
      });
      return zones;
    });
};

const identifyDevice = deviceUuid => {
  return channels.system.request({ name: 'identify' }, { deviceUuid: deviceUuid });
};

const getContentNode = uuid => {
  return Promise.resolve()
    .then(() => {
      if (!uuid) throw new Error('uuidRequired');
      return get('/api/content/' + uuid + '/children');
    })
    .then(content => {
      return new models.ContentNode({ content: content });
    });
};

const getContentNodes = params => {
  return Promise.reject();
};

const getData = (key, group) => {
  return Promise.resolve()
    .then(() => {
      return get('/api/data/' + group + '/' + key);
    })
    .then(data => {
      return new models.Data({ data: data });
    });
};

const findData = params => {
  return Promise.resolve()
    .then(() => {
      return get('/api/data', params);
    })
    .then(query => {
      const results = [];
      query.results.forEach(data => {
	results.push(new models.Data({ data: data }));
      });
      return results;
    });
};

module.exports.identifyDevice = identifyDevice;

module.exports.getContentNode = getContentNode;

module.exports.getCurrentDevice = getCurrentDevice;
module.exports.getCurrentExperience = getCurrentExperience;

module.exports.getExperience = getExperience;
module.exports.getExperiences = getExperiences;

module.exports.getDevice = getDevice;
module.exports.getDevices = getDevices;

module.exports.getLocation = getLocation;
module.exports.getLocations = getLocations;

module.exports.getZone = getZone;
module.exports.getZones = getZones;

module.exports.getData = getData;
module.exports.findData = findData;

module.exports.get = get;
module.exports.fetch = fetch_;
<commit_msg>Remove tabbed whitespace.<commit_after>'use strict';

require('isomorphic-fetch');
const credentials = require('./credentials');

const models = require('./models');
const channels = require('./channels');
const config = require('./config');

const fetch_ = (path, options) => {
  const url = config.host + path;
  options.headers = options.headers || {};
  return Promise.resolve()
    .then(() => credentials.getToken())
    .then(token => {
      options.headers.Authorization = 'Bearer ' + token;
      return fetch(url, options);
    })
    .then(response => {
      return response.json();
    })
    .catch(error => {
      throw new Error(error);
    });
};

const createQueryString = obj => {
  var parts = ['?'];
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
      parts.push(encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]));
    }
  }
  return parts.join('&');
};

const get = (path, params) => {
  if (params) path += createQueryString(params);
  return fetch_(path, { method: 'get' });
};

const getCurrentDevice = () => {
  return Promise.resolve()
    .then(() => {
      return channels.system.request({ name: 'getCurrentDevice' });
    })
    .then(device => {
      return new models.Device({ device: device, current: true });
    });
};

const getCurrentExperience = () => {
  return Promise.resolve()
    .then(() => {
      return channels.system.request({ name: 'getCurrentExperience' });
    })
    .then(experience => {
      return new models.Experience({ experience: experience });
    });
};

const getDevice = uuid => {
  return Promise.resolve()
    .then(() => {
      if (!uuid) throw new Error('uuidRequired');
      return get('/api/devices/' + uuid);
    })
    .then(device => {
      return new models.Device({ device: device });
    });
};

const getDevices = params => {
  return Promise.resolve()
    .then(() => {
      return get('/api/devices', params);
    })
    .then(query => {
      const devices = [];
      query.results.forEach(device => {
        devices.push(new models.Device({ device: device }));
      });
      return devices;
    });
};

const getExperience = uuid => {
  return Promise.resolve()
    .then(() => {
      if (!uuid) throw new Error('uuidRequired');
      return get('/api/experiences/' + uuid);
    })
    .then(experience => {
      return new models.Experience({ experience: experience });
    });
};

const getExperiences = params => {
  return Promise.resolve()
    .then(() => {
      return get('/api/experiences', params);
    })
    .then(query => {
      const experiences = [];
      query.results.forEach(experience => {
        experiences.push(new models.Experience({ experience: experience }));
      });
      return experiences;
    });
};

const getLocation = uuid => {
  return Promise.resolve()
    .then(() => {
      if (!uuid) throw new Error('uuidRequired');
      return get('/api/locations/' + uuid);
    })
    .then(location => {
      return new models.Location({ location: location });
    });
};

const getLocations = params => {
  return Promise.resolve()
    .then(() => {
      return get('/api/locations', params);
    })
    .then(query => {
      const locations = [];
      query.results.forEach(location => {
        locations.push(new models.Location({ location: location }));
      });
      return locations;
    });
};

const getZone = uuid => {
  return Promise.resolve()
    .then(() => {
      if (!uuid) throw new Error('uuidRequired');
      return get('/api/zones/' + uuid);
    })
    .then(zone => {
      return new models.Zone({ zone: zone });
    });
};

const getZones = params => {
  return Promise.resolve()
    .then(() => {
      return get('/api/zones', params);
    })
    .then(query => {
      const zones = [];
      query.results.forEach(zone => {
        zones.push(new models.Zone({ zone: zone }));
      });
      return zones;
    });
};

const identifyDevice = deviceUuid => {
  return channels.system.request({ name: 'identify' }, { deviceUuid: deviceUuid });
};

const getContentNode = uuid => {
  return Promise.resolve()
    .then(() => {
      if (!uuid) throw new Error('uuidRequired');
      return get('/api/content/' + uuid + '/children');
    })
    .then(content => {
      return new models.ContentNode({ content: content });
    });
};

const getContentNodes = params => {
  return Promise.reject();
};

const getData = (key, group) => {
  return Promise.resolve()
    .then(() => {
      return get('/api/data/' + group + '/' + key);
    })
    .then(data => {
      return new models.Data({ data: data });
    });
};

const findData = params => {
  return Promise.resolve()
    .then(() => {
      return get('/api/data', params);
    })
    .then(query => {
      const results = [];
      query.results.forEach(data => {
        results.push(new models.Data({ data: data }));
      });
      return results;
    });
};

module.exports.identifyDevice = identifyDevice;

module.exports.getContentNode = getContentNode;

module.exports.getCurrentDevice = getCurrentDevice;
module.exports.getCurrentExperience = getCurrentExperience;

module.exports.getExperience = getExperience;
module.exports.getExperiences = getExperiences;

module.exports.getDevice = getDevice;
module.exports.getDevices = getDevices;

module.exports.getLocation = getLocation;
module.exports.getLocations = getLocations;

module.exports.getZone = getZone;
module.exports.getZones = getZones;

module.exports.getData = getData;
module.exports.findData = findData;

module.exports.get = get;
module.exports.fetch = fetch_;
<|endoftext|>


<commit_before>/**
 * @fileoverview About this file
 */

/**
 * @type {Object}
 * @const
 */
var SugarCrm = {};

/**
 * Represents SugarCRM name value list pair.
 * @constructor
 */
SugarCrm.NameValue = function() {};

/**
 * @type {string}
 */
SugarCrm.NameValue.prototype.name;

/**
 * @type {string}
 */
SugarCrm.NameValue.prototype.value;

/**
 * Represents SugarCRM name value list entry.
 * @constructor
 */
SugarCrm.NameValueEntry = function() {};

/**
 * @type {string}
 */
SugarCrm.NameValueEntry.prototype.id;

/**
 * @type {string}
 */
SugarCrm.NameValueEntry.prototype.module_name;

/**
 * @type {Object.<SugarCrm.NameValue>}
 */
SugarCrm.NameValueEntry.prototype.name_value_list;

/**
 * Represents SugarCRM entry.
 * @constructor
 */
SugarCrm.Record = function() {};

/**
 * @type {string}
 */
SugarCrm.Record.prototype.id;

/**
 * @type {string}
 */
SugarCrm.Record.prototype.date_modified;

/**
 * Represents SugarCRM ModuleField.
 * @constructor
 */
SugarCrm.ModuleField = function() {};

/**
 * @type {boolean}
 */
SugarCrm.ModuleField.prototype.calculated;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.group;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.id_name;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.label;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.len;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.name;

/**
 * @type {!Object.<SugarCrm.NameValue>}
 */
SugarCrm.ModuleField.prototype.options;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.related_module;

/**
 * @type {number}
 */
SugarCrm.ModuleField.prototype.required;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.type;

/**
 * Represents SugarCRM ModuleInfo.
 * @constructor
 */
SugarCrm.ModuleInfo = function() {};

/**
 * @type {string}
 */
SugarCrm.ModuleInfo.prototype.module_name;

/**
 * @type {string}
 */
SugarCrm.ModuleInfo.prototype.table_name;

/**
 * @type {!Object.<SugarCrm.ModuleField>}
 */
SugarCrm.ModuleInfo.prototype.module_fields;

/**
 * Represents query format in channel.
 * @constructor
 */
SugarCrm.Query = function() {};

/**
 * @type {string} module name.
 */
SugarCrm.Query.prototype.store;

/**
 * @type {string} field name, use 'id' for primary key.
 */
SugarCrm.Query.prototype.index;

/**
 * @type {string} id.
 */
SugarCrm.Query.prototype.key;

/**
 * @type {{lower: string, upper: string}} id range.
 */
SugarCrm.Query.prototype.keyRange;

/**
 * Represents query result format in channel.
 * @constructor
 * @extends {SugarCrm.Query}
 */
SugarCrm.QueryResult = function() {};

/**
 * @type {Array.<SugarCrm.Record>} result of query.
 */
SugarCrm.Query.prototype.result;
<commit_msg>sugar setup<commit_after>/**
 * @fileoverview About this file
 */

/**
 * @type {Object}
 * @const
 */
var SugarCrm = {};

/**
 * Represents SugarCRM name value list pair.
 * @constructor
 */
SugarCrm.NameValue = function() {};

/**
 * @type {string}
 */
SugarCrm.NameValue.prototype.name;

/**
 * @type {string}
 */
SugarCrm.NameValue.prototype.value;

/**
 * Represents SugarCRM name value list entry.
 * @constructor
 */
SugarCrm.NameValueEntry = function() {};

/**
 * @type {string}
 */
SugarCrm.NameValueEntry.prototype.id;

/**
 * @type {string}
 */
SugarCrm.NameValueEntry.prototype.module_name;

/**
 * @type {Object.<SugarCrm.NameValue>}
 */
SugarCrm.NameValueEntry.prototype.name_value_list;

/**
 * Represents SugarCRM entry.
 * @constructor
 */
SugarCrm.Record = function() {};

/**
 * @type {string}
 */
SugarCrm.Record.prototype.id;

/**
 * @type {string}
 */
SugarCrm.Record.prototype.date_modified;

/**
 * Represents SugarCRM ModuleField.
 * @constructor
 */
SugarCrm.ModuleField = function() {};

/**
 * @type {boolean}
 */
SugarCrm.ModuleField.prototype.calculated;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.group;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.id_name;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.label;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.len;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.name;

/**
 * @type {!Object.<SugarCrm.NameValue>}
 */
SugarCrm.ModuleField.prototype.options;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.related_module;

/**
 * @type {number}
 */
SugarCrm.ModuleField.prototype.required;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.type;

/**
 * Represents SugarCRM ModuleInfo.
 * @constructor
 */
SugarCrm.ModuleInfo = function() {};

/**
 * @type {string}
 */
SugarCrm.ModuleInfo.prototype.module_name;

/**
 * @type {string}
 */
SugarCrm.ModuleInfo.prototype.table_name;

/**
 * @type {!Object.<SugarCrm.ModuleField>}
 */
SugarCrm.ModuleInfo.prototype.module_fields;

/**
 * Represents query format in channel.
 * @constructor
 */
SugarCrm.Query = function() {};

/**
 * @type {string} module name.
 */
SugarCrm.Query.prototype.store;

/**
 * @type {string} field name, use 'id' for primary key.
 */
SugarCrm.Query.prototype.index;

/**
 * @type {string} id.
 */
SugarCrm.Query.prototype.key;

/**
 * @type {{lower: string, upper: string}} id range.
 */
SugarCrm.Query.prototype.keyRange;

/**
 * Represents query result format in channel.
 * @constructor
 * @extends {SugarCrm.Query}
 */
SugarCrm.QueryResult = function() {};

/**
 * @type {Array.<SugarCrm.Record>} result of query.
 */
SugarCrm.Query.prototype.result;

/**
 * @typedef {{
 *   baseUrl: (string|undefined),
 *   domain: (string?|undefined),
 *   userName: (string?|undefined),
 *   isLogin: (boolean|undefined),
 *   flavor: (string|undefined),
 *   version: (string|undefined),
 *   uptime: (string|undefined)
 * }}
 */
SugarCrm.ServerInfo;

<|endoftext|>


<commit_before>/*
 *   Copyright 2014-2016 Guy Bedford (http://guybedford.com)
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
require('core-js/es6/string');

var path = require('path');
var nodeSemver = require('semver');
var ui = require('./ui');
var config = require('./config');
var registry = require('./registry');
var PackageName = require('./package-name');
var fs = require('graceful-fs');
var mkdirp = require('mkdirp');
var rimraf = require('rimraf');
var asp = require('bluebird').Promise.promisify;
var System = require('systemjs');
var Loader = require('../api').Loader;
var HOME = require('./common').HOME;
var Promise = require('bluebird');
var getCanonicalName = require('systemjs-builder/lib/utils').getCanonicalName;
var extend = require('./common').extend;

var core = module.exports;

// we always download the latest semver compatible version
var systemVersion = require('../package.json').dependencies.systemjs;

if (systemVersion.match(/^systemjs\/systemjs\#/))
  systemVersion = systemVersion.substr(systemVersion.indexOf('#') + 1);

exports.run = function(moduleName, view, production) {
  return config.load(false, true)
  .then(function() {
    System.config(config.getLoaderConfig());
    if (production)
      System.config({ production: true });
    return System.import(moduleName)
    .then(function(m) {
      if (view)
        console.log(m);
    });
  })
  .catch(function(e) {
    ui.log('err', e.stack || e);
  });
};

// check and download module loader files
exports.checkDlLoader = function() {
  return config.load()
  .then(function() {
    return asp(fs.readFile)(path.resolve(config.pjson.packages, '.loaderversions'));
  })
  .catch(function(err) {
    if (err.code === 'ENOENT')
      return '';
    throw err;
  })
  .then(function(cacheVersions) {
    if (cacheVersions.toString() !== systemVersion)
      return exports.dlLoader();

    // even if version file is fresh, still check files exist
    return asp(fs.readdir)(config.pjson.packages)
    .catch(function(err) {
      if (err.code === 'ENOENT')
        return [];
      throw err;
    })
    .then(function(files) {
      if (files.indexOf('system.js') === -1)
        return exports.dlLoader();
    });
  });
};

// mini registry API usage implementation
var loaderFilesCacheDir = path.join(HOME, '.jspm', 'loader-files');

function dl(name, repo, version) {
  var pkg = new PackageName(repo);
  var endpoint = registry.load(pkg.registry);
  var vMatch, vMatchLookup;
  var dlDir = path.resolve(loaderFilesCacheDir, name);

  return endpoint.lookup(pkg.package)
  .then(function(lookup) {
    if (!(nodeSemver.validRange(version)))
      vMatch = version;
    else
      vMatch = Object.keys(lookup.versions)
      .filter(nodeSemver.valid)
      .sort(nodeSemver.compare).reverse()
      .filter(function(v) {
        return nodeSemver.satisfies(v, version);
      })[0];

    vMatchLookup = lookup.versions[vMatch];

    return asp(fs.readFile)(path.resolve(dlDir, '.hash'))
    .then(function(_hash) {
      return _hash.toString() === vMatchLookup.hash;
    }, function (e) {
      if (e.code === 'ENOENT')
        return;
      throw e;
    });
  })
  .then(function(cached) {
    if (cached)
      return;

    return endpoint.download(pkg.package, vMatch, vMatchLookup.hash, vMatchLookup.meta, dlDir)
    .then(function() {
      return fs.writeFile(path.resolve(dlDir, '.hash'), vMatchLookup.hash);
    });
  })
  .then(function() {
    return vMatch;
  });
}

// file copy implementation
function cp(file, name, transform) {
  return asp(fs.readFile)(path.resolve(loaderFilesCacheDir, file)).then(function(source) {
    if (transform)
      source = transform(source.toString());
    ui.log('info', '  `' + name + '`');
    return asp(fs.writeFile)(path.resolve(config.pjson.packages, name), source);
  });
}

exports.dlLoader = function(unminified, edge, latest) {
  ui.log('info', 'Looking up loader files...');
  var min = unminified ? '.src' : '';

  var using = {};

  return config.load()
  .then(function() {
    return asp(mkdirp)(config.pjson.packages);
  })
  .then(function() {
    // delete old versions
    return asp(fs.readdir)(config.pjson.packages);
  })
  .then(function(files) {
    return Promise.all(files.filter(function(file) {
      return file.match(/^(system-csp|system-csp-production|system|es6-module-loader|system-polyfills)/);
    }).map(function(file) {
      return asp(fs.unlink)(path.resolve(config.pjson.packages, file));
    }));
  })
  .then(function() {
    return dl('systemjs', 'github:systemjs/systemjs', !edge ? (!latest ? systemVersion : '^' + systemVersion) : 'master')
    .then(function(version) {
      using.system = version;
      return Promise.all([
        cp('systemjs/dist/system' + min + '.js', 'system.js'),
        unminified || cp('systemjs/dist/system.src.js', 'system.src.js'),
        unminified || cp('systemjs/dist/system.js.map', 'system.js.map'),
        // cp('systemjs/dist/system-csp-production' + min + '.js', 'system-csp-production.js'),
        // unminified || cp('systemjs/dist/system-csp-production.src.js', 'system-csp-production.src.js'),
        // unminified || cp('systemjs/dist/system-csp-production.js.map', 'system-csp-production.js.map'),
        cp('systemjs/dist/system-polyfills' + min + '.js', 'system-polyfills.js'),
        unminified || cp('systemjs/dist/system-polyfills.src.js', 'system-polyfills.src.js'),
        unminified || cp('systemjs/dist/system-polyfills.js.map', 'system-polyfills.js.map')
      ]);
    });
  })
  .then(function() {
    ui.log('info', '\nUsing loader versions:');
    ui.log('info', '  `systemjs@' + using.system + '`');

    return asp(fs.writeFile)(path.resolve(config.pjson.packages, '.loaderversions'), systemVersion);
  })
  .then(function() {
    ui.log('ok', 'Loader files downloaded successfully');
  }, function(err) {
    ui.log('err', err);
    ui.log('err', 'Error downloading loader files.');
    throw err;
  });
};

exports.normalize = function(moduleName, parentName, canonicalize, env) {
  var loader = new Loader();

  // set a custom environment for normalization
  var envModule = extend(extend({}, loader.get('@system-env')), env);
  loader.set('@system-env', loader.newModule(envModule));

  // ensure the parent package is loaded first
  return loader.normalize(parentName)
  .then(function() {
    return loader.normalize(moduleName, parentName && loader.normalizeSync(parentName));
  })
  .then(function(normalized) {
    if (canonicalize)
      return getCanonicalName(loader, normalized);
    return normalized;
  });
};

exports.init = function init(basePath) {
  if (basePath)
    process.env.jspmConfigPath = path.resolve(basePath, 'package.json');
  var relBase = path.relative(process.cwd(), path.dirname(process.env.jspmConfigPath || ''));
  if (relBase !== '')
    ui.log('info', 'Initializing package at `' + relBase + '/`\nUse %jspm init .% to intialize into the current folder.');
  return config.load(true)
  .then(config.save)
  .then(function() {
    ui.log('info', '');
    ui.log('ok', 'package.json at %' + path.relative(process.cwd(), config.pjsonPath) + '%\n' +
        'Config at %' + path.relative(process.cwd(), config.pjson.configFile) + '%' +
        (config.loader.devFile ? ', %' + path.relative(process.cwd(), config.pjson.configFileDev) + '%' : '') +
        (config.loader.browserFile ? ', %' + path.relative(process.cwd(), config.pjson.configFileBrowser) + '%' : '') +
        (config.loader.nodeFile ? ', %' + path.relative(process.cwd(), config.pjson.configFileNode) + '%' : ''));
  })
  .then(function() {
    return core.checkDlLoader();
  })
  .catch(function(err) {
    ui.log('err', err && err.stack || err);
  });
};

exports.cacheClear = function() {
  var jspmDir = path.resolve(HOME, '.jspm'),
      packagesCacheDir = path.join(jspmDir, 'packages'),
      loaderCacheDir = path.join(jspmDir, 'loader-files'),
      files, filesLength, fileName, i;

  // Clear loader files
  if (fs.existsSync(loaderCacheDir))
    rimraf.sync(loaderCacheDir);
  ui.log('ok', 'Loader file cache cleared.');

  // Clear packages cache folder
  if (fs.existsSync(packagesCacheDir))
    rimraf.sync(packagesCacheDir);
  ui.log('ok', 'Package cache cleared.');

  // Clear registry cache folders
  files = fs.readdirSync(jspmDir);
  filesLength = files.length;
  for (i = 0; i < filesLength; i++) {
    fileName = files[i];
    if (fileName.endsWith('-cache')) {
      rimraf.sync(path.join(jspmDir, fileName));
      ui.log('ok', '%' + fileName.substr(0, fileName.length - '-cache'.length) + '% cache cleared.');
    }
  }

  ui.log('warn', 'All caches cleared.');
  ui.log('info', 'Please post an issue if you suspect the cache isn\'t invalidating properly.');
  ui.log('info', '%jspm install -f% is equivalent to running a cache clear for that specific package tree.');
};
<commit_msg>jspm normalize fix<commit_after>/*
 *   Copyright 2014-2016 Guy Bedford (http://guybedford.com)
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
require('core-js/es6/string');

var path = require('path');
var nodeSemver = require('semver');
var ui = require('./ui');
var config = require('./config');
var registry = require('./registry');
var PackageName = require('./package-name');
var fs = require('graceful-fs');
var mkdirp = require('mkdirp');
var rimraf = require('rimraf');
var asp = require('bluebird').Promise.promisify;
var System = require('systemjs');
var Loader = require('../api').Loader;
var HOME = require('./common').HOME;
var Promise = require('bluebird');
var getCanonicalName = require('systemjs-builder/lib/utils').getCanonicalName;
var extend = require('./common').extend;

var core = module.exports;

// we always download the latest semver compatible version
var systemVersion = require('../package.json').dependencies.systemjs;

if (systemVersion.match(/^systemjs\/systemjs\#/))
  systemVersion = systemVersion.substr(systemVersion.indexOf('#') + 1);

exports.run = function(moduleName, view, production) {
  return config.load(false, true)
  .then(function() {
    System.config(config.getLoaderConfig());
    if (production)
      System.config({ production: true });
    return System.import(moduleName)
    .then(function(m) {
      if (view)
        console.log(m);
    });
  })
  .catch(function(e) {
    ui.log('err', e.stack || e);
  });
};

// check and download module loader files
exports.checkDlLoader = function() {
  return config.load()
  .then(function() {
    return asp(fs.readFile)(path.resolve(config.pjson.packages, '.loaderversions'));
  })
  .catch(function(err) {
    if (err.code === 'ENOENT')
      return '';
    throw err;
  })
  .then(function(cacheVersions) {
    if (cacheVersions.toString() !== systemVersion)
      return exports.dlLoader();

    // even if version file is fresh, still check files exist
    return asp(fs.readdir)(config.pjson.packages)
    .catch(function(err) {
      if (err.code === 'ENOENT')
        return [];
      throw err;
    })
    .then(function(files) {
      if (files.indexOf('system.js') === -1)
        return exports.dlLoader();
    });
  });
};

// mini registry API usage implementation
var loaderFilesCacheDir = path.join(HOME, '.jspm', 'loader-files');

function dl(name, repo, version) {
  var pkg = new PackageName(repo);
  var endpoint = registry.load(pkg.registry);
  var vMatch, vMatchLookup;
  var dlDir = path.resolve(loaderFilesCacheDir, name);

  return endpoint.lookup(pkg.package)
  .then(function(lookup) {
    if (!(nodeSemver.validRange(version)))
      vMatch = version;
    else
      vMatch = Object.keys(lookup.versions)
      .filter(nodeSemver.valid)
      .sort(nodeSemver.compare).reverse()
      .filter(function(v) {
        return nodeSemver.satisfies(v, version);
      })[0];

    vMatchLookup = lookup.versions[vMatch];

    return asp(fs.readFile)(path.resolve(dlDir, '.hash'))
    .then(function(_hash) {
      return _hash.toString() === vMatchLookup.hash;
    }, function (e) {
      if (e.code === 'ENOENT')
        return;
      throw e;
    });
  })
  .then(function(cached) {
    if (cached)
      return;

    return endpoint.download(pkg.package, vMatch, vMatchLookup.hash, vMatchLookup.meta, dlDir)
    .then(function() {
      return fs.writeFile(path.resolve(dlDir, '.hash'), vMatchLookup.hash);
    });
  })
  .then(function() {
    return vMatch;
  });
}

// file copy implementation
function cp(file, name, transform) {
  return asp(fs.readFile)(path.resolve(loaderFilesCacheDir, file)).then(function(source) {
    if (transform)
      source = transform(source.toString());
    ui.log('info', '  `' + name + '`');
    return asp(fs.writeFile)(path.resolve(config.pjson.packages, name), source);
  });
}

exports.dlLoader = function(unminified, edge, latest) {
  ui.log('info', 'Looking up loader files...');
  var min = unminified ? '.src' : '';

  var using = {};

  return config.load()
  .then(function() {
    return asp(mkdirp)(config.pjson.packages);
  })
  .then(function() {
    // delete old versions
    return asp(fs.readdir)(config.pjson.packages);
  })
  .then(function(files) {
    return Promise.all(files.filter(function(file) {
      return file.match(/^(system-csp|system-csp-production|system|es6-module-loader|system-polyfills)/);
    }).map(function(file) {
      return asp(fs.unlink)(path.resolve(config.pjson.packages, file));
    }));
  })
  .then(function() {
    return dl('systemjs', 'github:systemjs/systemjs', !edge ? (!latest ? systemVersion : '^' + systemVersion) : 'master')
    .then(function(version) {
      using.system = version;
      return Promise.all([
        cp('systemjs/dist/system' + min + '.js', 'system.js'),
        unminified || cp('systemjs/dist/system.src.js', 'system.src.js'),
        unminified || cp('systemjs/dist/system.js.map', 'system.js.map'),
        // cp('systemjs/dist/system-csp-production' + min + '.js', 'system-csp-production.js'),
        // unminified || cp('systemjs/dist/system-csp-production.src.js', 'system-csp-production.src.js'),
        // unminified || cp('systemjs/dist/system-csp-production.js.map', 'system-csp-production.js.map'),
        cp('systemjs/dist/system-polyfills' + min + '.js', 'system-polyfills.js'),
        unminified || cp('systemjs/dist/system-polyfills.src.js', 'system-polyfills.src.js'),
        unminified || cp('systemjs/dist/system-polyfills.js.map', 'system-polyfills.js.map')
      ]);
    });
  })
  .then(function() {
    ui.log('info', '\nUsing loader versions:');
    ui.log('info', '  `systemjs@' + using.system + '`');

    return asp(fs.writeFile)(path.resolve(config.pjson.packages, '.loaderversions'), systemVersion);
  })
  .then(function() {
    ui.log('ok', 'Loader files downloaded successfully');
  }, function(err) {
    ui.log('err', err);
    ui.log('err', 'Error downloading loader files.');
    throw err;
  });
};

exports.normalize = function(moduleName, parentName, canonicalize, env) {
  var loader = new Loader();

  // set a custom environment for normalization
  var envModule = extend(extend({}, loader.get('@system-env')), env);
  loader.set('@system-env', loader.newModule(envModule));

  // ensure the parent package is loaded first
  return (parentName && loader.normalize(parentName) || Promise.resolve())
  .then(function() {
    return loader.normalize(moduleName, parentName && loader.normalizeSync(parentName));
  })
  .then(function(normalized) {
    if (canonicalize)
      return getCanonicalName(loader, normalized);
    return normalized;
  });
};

exports.init = function init(basePath) {
  if (basePath)
    process.env.jspmConfigPath = path.resolve(basePath, 'package.json');
  var relBase = path.relative(process.cwd(), path.dirname(process.env.jspmConfigPath || ''));
  if (relBase !== '')
    ui.log('info', 'Initializing package at `' + relBase + '/`\nUse %jspm init .% to intialize into the current folder.');
  return config.load(true)
  .then(config.save)
  .then(function() {
    ui.log('info', '');
    ui.log('ok', 'package.json at %' + path.relative(process.cwd(), config.pjsonPath) + '%\n' +
        'Config at %' + path.relative(process.cwd(), config.pjson.configFile) + '%' +
        (config.loader.devFile ? ', %' + path.relative(process.cwd(), config.pjson.configFileDev) + '%' : '') +
        (config.loader.browserFile ? ', %' + path.relative(process.cwd(), config.pjson.configFileBrowser) + '%' : '') +
        (config.loader.nodeFile ? ', %' + path.relative(process.cwd(), config.pjson.configFileNode) + '%' : ''));
  })
  .then(function() {
    return core.checkDlLoader();
  })
  .catch(function(err) {
    ui.log('err', err && err.stack || err);
  });
};

exports.cacheClear = function() {
  var jspmDir = path.resolve(HOME, '.jspm'),
      packagesCacheDir = path.join(jspmDir, 'packages'),
      loaderCacheDir = path.join(jspmDir, 'loader-files'),
      files, filesLength, fileName, i;

  // Clear loader files
  if (fs.existsSync(loaderCacheDir))
    rimraf.sync(loaderCacheDir);
  ui.log('ok', 'Loader file cache cleared.');

  // Clear packages cache folder
  if (fs.existsSync(packagesCacheDir))
    rimraf.sync(packagesCacheDir);
  ui.log('ok', 'Package cache cleared.');

  // Clear registry cache folders
  files = fs.readdirSync(jspmDir);
  filesLength = files.length;
  for (i = 0; i < filesLength; i++) {
    fileName = files[i];
    if (fileName.endsWith('-cache')) {
      rimraf.sync(path.join(jspmDir, fileName));
      ui.log('ok', '%' + fileName.substr(0, fileName.length - '-cache'.length) + '% cache cleared.');
    }
  }

  ui.log('warn', 'All caches cleared.');
  ui.log('info', 'Please post an issue if you suspect the cache isn\'t invalidating properly.');
  ui.log('info', '%jspm install -f% is equivalent to running a cache clear for that specific package tree.');
};
<|endoftext|>


<commit_before>'use strict'

const bodyParser = require('koa-bodyparser');
const route = require('koa-route');

module.exports = (config, app) => {

  // TODO: Remove relative path when questionnaire-core is published to npmjs.org
  const core = require('../../questionnaire-core/src/interface.js')(config.mongodbUrl);

  // API METHODS

  async function getQuestionnaires(ctx) {
    console.log('GET /questionnaires');
    /*
    var responseWithJSON = function(data){
      response.end(JSON.stringify(data));
    };

    response.writeHead(200, {"Content-Type": "application/json"});
    var responseWithJSON = function(data){
      response.end(JSON.stringify(data));
    };
    core.findRestaurants(responseWithJSON);
  */
    let responseFromGetQuestionnaires = await core.getQuestionnaires();
    ctx.body = responseFromGetQuestionnaires;
  }

  async function putQuestionnaire(ctx){
    console.log('PUT /questionnaire');

    console.log('DEBUG PRINT PAYLOAD REMOVE ME: ');
    console.log(ctx.request.body);

    let responseFromPutQuestionnaire = await core.putQuestionnaire();
    ctx.body = responseFromPutQuestionnaire;
  }

  // ROUTES

  app.use(bodyParser());
  app.use(route.get('/questionnaires', getQuestionnaires));
  app.use(route.put('/questionnaire', putQuestionnaire));
}<commit_msg>Remove console.logs<commit_after>'use strict'

const bodyParser = require('koa-bodyparser');
const route = require('koa-route');

module.exports = (config, app) => {

  // TODO: Remove relative path when questionnaire-core is published to npmjs.org
  const core = require('../../questionnaire-core/src/interface.js')(config.mongodbUrl);

  // API METHODS

  async function getQuestionnaires(ctx) {
    console.log('GET /questionnaires');
    /*
    var responseWithJSON = function(data){
      response.end(JSON.stringify(data));
    };

    response.writeHead(200, {"Content-Type": "application/json"});
    var responseWithJSON = function(data){
      response.end(JSON.stringify(data));
    };
    core.findRestaurants(responseWithJSON);
  */
    let responseFromGetQuestionnaires = await core.getQuestionnaires();
    ctx.body = responseFromGetQuestionnaires;
  }

  async function putQuestionnaire(ctx){
    console.log('PUT /questionnaire');

    let payload = ctx.request.body;

    let responseFromPutQuestionnaire = await core.putQuestionnaire(payload);
    ctx.body = responseFromPutQuestionnaire;
  }

  // ROUTES

  app.use(bodyParser());
  app.use(route.get('/questionnaires', getQuestionnaires));
  app.use(route.put('/questionnaire', putQuestionnaire));
}<|endoftext|>


<commit_before>'use strict';

//====================================================================

var assign = require('lodash.assign');
var Bluebird = require('bluebird');
var debug = require('debug')('xo:api');
var forEach = require('lodash.foreach');
var isArray = require('lodash.isarray');
var isFunction = require('lodash.isfunction');
var isObject = require('lodash.isobject');
var isString = require('lodash.isstring');
var keys = require('lodash.keys');
var pick = require('lodash.pick');
var requireTree = require('require-tree');
var schemaInspector = require('schema-inspector');

var apiErrors = require('./api-errors');
var coroutine = require('./fibers-utils').$coroutine;
var InvalidParameters = require('./api-errors').InvalidParameters;
var MethodNotFound = require('./api-errors').MethodNotFound;
var Unauthorized = require('./api-errors').Unauthorized;
var wait = require('./fibers-utils').$wait;
var wrap = require('./utils').wrap;

//====================================================================

function $deprecated(fn)
{
	return function (session, req) {
		console.warn(req.method +' is deprecated!');

		return fn.apply(this, arguments);
	};
}

//====================================================================

// TODO: Helper functions that could be written:
// - checkParams(req.params, param1, ..., paramN)

var helpers = {};

helpers.checkPermission = function (permission)
{
	// TODO: Handle token permission.

	var userId = this.session.get('user_id', undefined);

	if (undefined === userId)
	{
		throw new Unauthorized();
	}

	if (!permission)
	{
		return;
	}

	var user = wait(this.users.first(userId));
	// The user MUST exist at this time.

	if (!user.hasPermission(permission))
	{
		throw new Unauthorized();
	}
};

// Checks and returns parameters.
helpers.getParams = function (schema) {
	var params = this.request.params;

	schema = {
		type: 'object',
		properties: schema,
	};

	var result = schemaInspector.validate(schema, params);

	if (!result.valid)
	{
		throw new InvalidParameters(result.error);
	}

	return params;
};

helpers.getUserPublicProperties = function (user) {
	// Handles both properties and wrapped models.
	var properties = user.properties || user;

	return pick(properties, 'id', 'email', 'permission');
};

helpers.getServerPublicProperties = function (server) {
	// Handles both properties and wrapped models.
	var properties = server.properties || server;

	return pick(properties, 'id', 'host', 'username');
};

// Deprecated!
var errorClasses = {
	ALREADY_AUTHENTICATED: apiErrors.AlreadyAuthenticated,
	INVALID_CREDENTIAL: apiErrors.InvalidCredential,
	INVALID_PARAMS: apiErrors.InvalidParameters,
	NO_SUCH_OBJECT: apiErrors.NoSuchObject,
	NOT_IMPLEMENTED: apiErrors.NotImplementd,
};
helpers.throw = function (errorId, data) {
	throw new (errorClasses[errorId])(data);
};

//====================================================================

function Api(xo)
{
	if ( !(this instanceof Api) )
	{
		return new Api(xo);
	}

	this.xo = xo;
}

var execHelper = coroutine(function (session, request) {
	var ctx = Object.create(this.xo);
	assign(ctx, helpers, {
		session: session,
		request: request,
	});

	var method = this.getMethod(request.method);

	if (!method)
	{
		console.warn('Invalid method: '+ request.method);
		throw new MethodNotFound(request.method);
	}

	if ('permission' in method)
	{
		helpers.checkPermission.call(ctx, method.permission);
	}

	if (method.params)
	{
		helpers.getParams.call(ctx, method.params);
	}

	return method.call(ctx, request.params);
});

Api.prototype.exec = function (session, request) {
	var method = request.method;

	debug('%s(...)', method);

	return Bluebird.try(execHelper, [session, request], this).then(
		function (result) {
			debug('%s(...) → %s', method, typeof result);
			return result;
		},
		function (error) {
			debug('Error: %s(...) → %s', method, error);
			throw error;
		}
	);
};

Api.prototype.getMethod = function (name) {
	var parts = name.split('.');

	var current = Api.fn;
	for (
		var i = 0, n = parts.length;
		(i < n) && (current = current[parts[i]]);
		++i
	)
	{
		/* jshint noempty:false */
	}

	// Method found.
	if (isFunction(current))
	{
		return current;
	}

	// It's a (deprecated) alias.
	if (isString(current))
	{
		return $deprecated(this.getMethod(current));
	}

	// No entry found, looking for a catch-all method.
	current = Api.fn;
	var catchAll;
	for (i = 0; (i < n) && (current = current[parts[i]]); ++i)
	{
		catchAll = current.__catchAll || catchAll;
	}

	return catchAll;
};

module.exports = Api;

//====================================================================

var $register = function (path, fn, params) {
	var component, current;

	if (params)
	{
		fn.params = params;
	}

	if (!isArray(path))
	{
		path = path.split('.');
	}

	current = Api.fn;
	for (var i = 0, n = path.length - 1; i < n; ++i)
	{
		component = path[i];
		current = (current[component] || (current[component] = {}));
	}

	if (isFunction(fn))
	{
		current[path[n]] = fn;
	}
	else if (isObject(fn) && !isArray(fn))
	{
		// If it is not an function but an object, copies its
		// properties.

		component = path[n];
		current = (current[component] || (current[component] = {}));

		assign(current, fn);
	}
	else
	{
		current[path[n]] = wrap(fn);
	}
};

Api.fn = requireTree('./api');

//--------------------------------------------------------------------

$register('system.getVersion', wrap('0.1'));

$register('xo.getAllObjects', function () {
	return this.getObjects();
});

// Returns the list of available methods similar to XML-RPC
// introspection (http://xmlrpc-c.sourceforge.net/introspection.html).
(function () {
	var methods = {};

	(function browse(container, path) {
		var n = path.length;
		forEach(container, function (content, key) {
			path[n] = key;
			if (isFunction(content))
			{
				methods[path.join('.')] = {
					description: content.description,
					params: content.params || {},
					permission: content.permission,
				};
			}
			else
			{
				browse(content, path);
			}
		});
		path.pop();
	})(Api.fn, []);

	$register('system.listMethods', wrap(keys(methods)));
	$register('system.methodSignature', function (params) {
		var method = methods[params.name];

		if (!method)
		{
			this.throw('NO_SUCH_OBJECT');
		}

		// XML-RPC can have multiple signatures per method.
		return [
			// XML-RPC requires the method name.
			assign({name: params.name}, method)
		];
	}, {
		name: {
			description: 'method to describe',
			type: 'string',
		},
	});

	$register('system.getMethodsInfo', wrap(methods));
})();
<commit_msg>API methods never return undefined.<commit_after>'use strict';

//====================================================================

var assign = require('lodash.assign');
var Bluebird = require('bluebird');
var debug = require('debug')('xo:api');
var forEach = require('lodash.foreach');
var isArray = require('lodash.isarray');
var isFunction = require('lodash.isfunction');
var isObject = require('lodash.isobject');
var isString = require('lodash.isstring');
var keys = require('lodash.keys');
var pick = require('lodash.pick');
var requireTree = require('require-tree');
var schemaInspector = require('schema-inspector');

var apiErrors = require('./api-errors');
var coroutine = require('./fibers-utils').$coroutine;
var InvalidParameters = require('./api-errors').InvalidParameters;
var MethodNotFound = require('./api-errors').MethodNotFound;
var Unauthorized = require('./api-errors').Unauthorized;
var wait = require('./fibers-utils').$wait;
var wrap = require('./utils').wrap;

//====================================================================

function $deprecated(fn)
{
	return function (session, req) {
		console.warn(req.method +' is deprecated!');

		return fn.apply(this, arguments);
	};
}

//====================================================================

// TODO: Helper functions that could be written:
// - checkParams(req.params, param1, ..., paramN)

var helpers = {};

helpers.checkPermission = function (permission)
{
	// TODO: Handle token permission.

	var userId = this.session.get('user_id', undefined);

	if (undefined === userId)
	{
		throw new Unauthorized();
	}

	if (!permission)
	{
		return;
	}

	var user = wait(this.users.first(userId));
	// The user MUST exist at this time.

	if (!user.hasPermission(permission))
	{
		throw new Unauthorized();
	}
};

// Checks and returns parameters.
helpers.getParams = function (schema) {
	var params = this.request.params;

	schema = {
		type: 'object',
		properties: schema,
	};

	var result = schemaInspector.validate(schema, params);

	if (!result.valid)
	{
		throw new InvalidParameters(result.error);
	}

	return params;
};

helpers.getUserPublicProperties = function (user) {
	// Handles both properties and wrapped models.
	var properties = user.properties || user;

	return pick(properties, 'id', 'email', 'permission');
};

helpers.getServerPublicProperties = function (server) {
	// Handles both properties and wrapped models.
	var properties = server.properties || server;

	return pick(properties, 'id', 'host', 'username');
};

// Deprecated!
var errorClasses = {
	ALREADY_AUTHENTICATED: apiErrors.AlreadyAuthenticated,
	INVALID_CREDENTIAL: apiErrors.InvalidCredential,
	INVALID_PARAMS: apiErrors.InvalidParameters,
	NO_SUCH_OBJECT: apiErrors.NoSuchObject,
	NOT_IMPLEMENTED: apiErrors.NotImplementd,
};
helpers.throw = function (errorId, data) {
	throw new (errorClasses[errorId])(data);
};

//====================================================================

function Api(xo)
{
	if ( !(this instanceof Api) )
	{
		return new Api(xo);
	}

	this.xo = xo;
}

var execHelper = coroutine(function (session, request) {
	var ctx = Object.create(this.xo);
	assign(ctx, helpers, {
		session: session,
		request: request,
	});

	var method = this.getMethod(request.method);

	if (!method)
	{
		console.warn('Invalid method: '+ request.method);
		throw new MethodNotFound(request.method);
	}

	if ('permission' in method)
	{
		helpers.checkPermission.call(ctx, method.permission);
	}

	if (method.params)
	{
		helpers.getParams.call(ctx, method.params);
	}

	return method.call(ctx, request.params);
});

Api.prototype.exec = function (session, request) {
	var method = request.method;

	debug('%s(...)', method);

	return Bluebird.try(execHelper, [session, request], this).then(
		function (result) {
			// If nothing was returned, consider this operation a success
			// and return true.
			if (result === undefined) {
				result = true;
			}

			debug('%s(...) → %s', method, typeof result);

			return result;
		},
		function (error) {
			debug('Error: %s(...) → %s', method, error);
			throw error;
		}
	);
};

Api.prototype.getMethod = function (name) {
	var parts = name.split('.');

	var current = Api.fn;
	for (
		var i = 0, n = parts.length;
		(i < n) && (current = current[parts[i]]);
		++i
	)
	{
		/* jshint noempty:false */
	}

	// Method found.
	if (isFunction(current))
	{
		return current;
	}

	// It's a (deprecated) alias.
	if (isString(current))
	{
		return $deprecated(this.getMethod(current));
	}

	// No entry found, looking for a catch-all method.
	current = Api.fn;
	var catchAll;
	for (i = 0; (i < n) && (current = current[parts[i]]); ++i)
	{
		catchAll = current.__catchAll || catchAll;
	}

	return catchAll;
};

module.exports = Api;

//====================================================================

var $register = function (path, fn, params) {
	var component, current;

	if (params)
	{
		fn.params = params;
	}

	if (!isArray(path))
	{
		path = path.split('.');
	}

	current = Api.fn;
	for (var i = 0, n = path.length - 1; i < n; ++i)
	{
		component = path[i];
		current = (current[component] || (current[component] = {}));
	}

	if (isFunction(fn))
	{
		current[path[n]] = fn;
	}
	else if (isObject(fn) && !isArray(fn))
	{
		// If it is not an function but an object, copies its
		// properties.

		component = path[n];
		current = (current[component] || (current[component] = {}));

		assign(current, fn);
	}
	else
	{
		current[path[n]] = wrap(fn);
	}
};

Api.fn = requireTree('./api');

//--------------------------------------------------------------------

$register('system.getVersion', wrap('0.1'));

$register('xo.getAllObjects', function () {
	return this.getObjects();
});

// Returns the list of available methods similar to XML-RPC
// introspection (http://xmlrpc-c.sourceforge.net/introspection.html).
(function () {
	var methods = {};

	(function browse(container, path) {
		var n = path.length;
		forEach(container, function (content, key) {
			path[n] = key;
			if (isFunction(content))
			{
				methods[path.join('.')] = {
					description: content.description,
					params: content.params || {},
					permission: content.permission,
				};
			}
			else
			{
				browse(content, path);
			}
		});
		path.pop();
	})(Api.fn, []);

	$register('system.listMethods', wrap(keys(methods)));
	$register('system.methodSignature', function (params) {
		var method = methods[params.name];

		if (!method)
		{
			this.throw('NO_SUCH_OBJECT');
		}

		// XML-RPC can have multiple signatures per method.
		return [
			// XML-RPC requires the method name.
			assign({name: params.name}, method)
		];
	}, {
		name: {
			description: 'method to describe',
			type: 'string',
		},
	});

	$register('system.getMethodsInfo', wrap(methods));
})();
<|endoftext|>