<commit_before>/*
 * grunt-spm
 *
 * Copyright (c) 2012 yun77op
 * Licensed under the MIT license.
 */

var fs = require('fs');
var Path = require('path');
var crypto = require('crypto');

module.exports = function(grunt) {
  "use strict";

  grunt.util = grunt.util || grunt.utils;

  // shortcuts
  var File = grunt.file;
  var util = grunt.util;
  var exec = require('child_process').exec;
  var log = grunt.log;

  var idPattern = /define\s*\((['"])([^\s'"]*)\1/g;
  var cache = {};

  // ==========================================================================
  // PRIVATE HELPER FUNCTIONS
  // ==========================================================================

  function md5(filecontent) {
    return crypto.createHash('md5').update(filecontent).digest("hex");
  }

  function normalizePath(path) {
    return Path.normalize(path).replace(/\\/g, '/');
  }

  function file_hash(path) {
    if (cache[path]) {
      return cache[path];
    }

    var filename_dst;
    var filecontent = File.read(path);
    var extname = Path.extname(path);
    var basename = Path.basename(path, extname);

    filename_dst = basename + '-' + md5(filecontent) + extname;
    cache[path] = filename_dst;

    return filename_dst;
  }

  function getRelativePath(path_src, path_dst, filename) {
    var relative_path = path_relative(path_src, path_dst);

    var tmp = relative_path.split(/[\/\\]/);
    tmp.pop();
    tmp.push(filename);

    path_dst = tmp.join('/');

    return path_dst;
  }

  function path_relative(path_from, path_to) {
    path_from = Path.resolve(path_from);
    path_to = Path.resolve(path_to);
    return Path.relative(path_from, path_to);
  }

  function path_relative_to_dist(path) {
    return normalizePath(path_relative(grunt.config('pkg.dist'), path));
  }

  function processFile(filepath, hashed_file, config) {
    var relative_path = getRelativePath(config.dist, filepath, hashed_file);
    var local_path = Path.join(config.dist, relative_path);

    File.copy(filepath, local_path);

    return relative_path;
  }

  function generateResourceMap(config) {
    var dist = config.dist;
    var spmResourceMap = [];
    var resourceMap = {};

    fs.readdirSync(dist).forEach(function(filename) {

      var filepath = normalizePath(Path.join(dist, filename));

      if (fs.statSync(filepath).isDirectory(filepath) ||
          filename.match(/\-debug/)) return;

      var source = File.read(filepath);
      var result, id;
      var hashed_file = file_hash(filepath);

      var hashed_file_full = Path.join(config.dist, hashed_file);
      resourceMap[path_relative_to_dist(filepath)] = path_relative_to_dist(hashed_file_full);

      while(result = idPattern.exec(source)) {
        id = result[2];
        if (id[0] == '#') id = id.slice(1);
        spmResourceMap.push([id + '.js', processFile(filepath, hashed_file, config)]);
      }

    });

    if (util.kindOf(config.resource_map) === 'object') {
      var i, filepath;
      for (i in config.resource_map) {
        filepath = Path.join(dist, config.resource_map[i]);
        var hashed_file = file_hash(filepath);
        spmResourceMap.push([i + '.js', processFile(filepath, hashed_file, config)]);
      }
    }

    var defaultResourceMapFilename = 'js-resource-map.json';
    var resourceMapPath = config.resource_map_file &&
        grunt.template.process(config.resource_map_file) ||
        Path.join(dist, defaultResourceMapFilename);

    File.write(resourceMapPath, JSON.stringify(spmResourceMap));
    var global_dist = grunt.config('pkg.dist');
    File.write(Path.join(global_dist, 'js-resource-map.json'), JSON.stringify(resourceMap));

    log.ok('File ' + resourceMapPath + ' created.');
  }

  // ==========================================================================
  // TASKS
  // ==========================================================================

  grunt.registerTask("spm", "Build spm modules and generate resource map", function() {
    var config = grunt.config('spm');
    var options = config.options;
    var callback = this.async();

    var spmBuildParams = '';
    var pathOptions = {
      src: true,
      dist: true
    };

    var i;
    for (i in options) {
      if (pathOptions[i]) {
        options[i] = grunt.template.process(options[i]);
      }

      spmBuildParams += ' --' + i + '=' + options[i];
    }

    config.dist = Path.join(config.root, options.dist);

    exec('spm build' + spmBuildParams,  {
      cwd: Path.resolve(config.root)
    }, function(err, stdout, stderr) {
      log.writeln('');
      log.writeln(stdout);
      if (stderr) log.error(stderr);
      if (err) grunt.fail.fatal(err);

      generateResourceMap(config);
      callback();
    });
  });
};
<commit_msg>Fix spm build bug can not recognise dist command line parameter\nreplace dist parameter with to parameter<commit_after>/*
 * grunt-spm
 *
 * Copyright (c) 2012 yun77op
 * Licensed under the MIT license.
 */

var fs = require('fs');
var Path = require('path');
var crypto = require('crypto');

module.exports = function(grunt) {
  "use strict";

  grunt.util = grunt.util || grunt.utils;

  // shortcuts
  var File = grunt.file;
  var util = grunt.util;
  var exec = require('child_process').exec;
  var log = grunt.log;

  var idPattern = /define\s*\((['"])([^\s'"]*)\1/g;
  var cache = {};

  // ==========================================================================
  // PRIVATE HELPER FUNCTIONS
  // ==========================================================================

  function md5(filecontent) {
    return crypto.createHash('md5').update(filecontent).digest("hex");
  }

  function normalizePath(path) {
    return Path.normalize(path).replace(/\\/g, '/');
  }

  function file_hash(path) {
    if (cache[path]) {
      return cache[path];
    }

    var filename_dst;
    var filecontent = File.read(path);
    var extname = Path.extname(path);
    var basename = Path.basename(path, extname);

    filename_dst = basename + '-' + md5(filecontent) + extname;
    cache[path] = filename_dst;

    return filename_dst;
  }

  function getRelativePath(path_src, path_dst, filename) {
    var relative_path = path_relative(path_src, path_dst);

    var tmp = relative_path.split(/[\/\\]/);
    tmp.pop();
    tmp.push(filename);

    path_dst = tmp.join('/');

    return path_dst;
  }

  function path_relative(path_from, path_to) {
    path_from = Path.resolve(path_from);
    path_to = Path.resolve(path_to);
    return Path.relative(path_from, path_to);
  }

  function path_relative_to_dist(path) {
    return normalizePath(path_relative(grunt.config('pkg.dist'), path));
  }

  function processFile(filepath, hashed_file, config) {
    var relative_path = getRelativePath(config.dist, filepath, hashed_file);
    var local_path = Path.join(config.dist, relative_path);

    File.copy(filepath, local_path);

    return relative_path;
  }

  function generateResourceMap(config) {
    var dist = config.dist;
    var spmResourceMap = [];
    var resourceMap = {};

    fs.readdirSync(dist).forEach(function(filename) {

      var filepath = normalizePath(Path.join(dist, filename));

      if (fs.statSync(filepath).isDirectory(filepath) ||
          filename.match(/\-debug/)) return;

      var source = File.read(filepath);
      var result, id;
      var hashed_file = file_hash(filepath);

      var hashed_file_full = Path.join(config.dist, hashed_file);
      resourceMap[path_relative_to_dist(filepath)] = path_relative_to_dist(hashed_file_full);

      while(result = idPattern.exec(source)) {
        id = result[2];
        if (id[0] == '#') id = id.slice(1);
        spmResourceMap.push([id + '.js', processFile(filepath, hashed_file, config)]);
      }

    });

    if (util.kindOf(config.resource_map) === 'object') {
      var i, filepath;
      for (i in config.resource_map) {
        filepath = Path.join(dist, config.resource_map[i]);
        var hashed_file = file_hash(filepath);
        spmResourceMap.push([i + '.js', processFile(filepath, hashed_file, config)]);
      }
    }

    var defaultResourceMapFilename = 'js-resource-map.json';
    var resourceMapPath = config.resource_map_file &&
        grunt.template.process(config.resource_map_file) ||
        Path.join(dist, defaultResourceMapFilename);

    File.write(resourceMapPath, JSON.stringify(spmResourceMap));
    var global_dist = grunt.config('pkg.dist');
    File.write(Path.join(global_dist, 'js-resource-map.json'), JSON.stringify(resourceMap));

    log.ok('File ' + resourceMapPath + ' created.');
  }

  // ==========================================================================
  // TASKS
  // ==========================================================================

  grunt.registerTask("spm-build", "Build spm modules and generate resource map", function() {
    var config = grunt.config('spm-build');
    var options = config.options;
    var callback = this.async();

    var spmBuildStr = '';
    var pathOptions = {
      src: true,
      dist: true
    };

    var i;

    for (i in options) {
      var key = i;

      if (pathOptions[i]) {
        options[i] = grunt.template.process(options[i]);
      }

      // spm bug, can't recognise dist commmand line parameter
      if (i == 'dist') key = 'to';

      spmBuildStr += ' --' + key + '=' + options[i];
    }

    config.dist = Path.join(config.root, options.dist);
    File.mkdir(config.dist);

    exec('spm build' + spmBuildStr,  {
      cwd: Path.resolve(config.root)
    }, function(err, stdout, stderr) {
      log.writeln('');
      log.writeln(stdout);
      if (stderr) log.error(stderr);
      if (err) grunt.fail.fatal(err);

      generateResourceMap(config);
      callback();
    });
  });
};
<|endoftext|>


<commit_before>import stringSimilarity from 'string-similarity';
import { t } from 'ttag';
import gql from 'src/lib/gql';
import {
  createPostbackAction,
  ellipsis,
  createSuggestOtherFactCheckerReply,
  POSTBACK_NO_ARTICLE_FOUND,
  createHighlightContents,
  createTextMessage,
  createArticleSourceReply,
} from './utils';
import ga from 'src/lib/ga';
import detectDialogflowIntent from 'src/lib/detectDialogflowIntent';
import handlePostback from '../handlePostback';

const SIMILARITY_THRESHOLD = 0.95;

export default async function initState(params) {
  let { data, event, userId, replies } = params;
  const state = '__INIT__';

  // Track text message type send by user
  const visitor = ga(userId, state, event.input);
  visitor.event({ ec: 'UserInput', ea: 'MessageType', el: event.message.type });

  // Store user input into context
  data.searchedText = event.input;

  // send input to dialogflow before doing search
  // uses dialogflowResponse as reply only when there's a intent matched and
  // input.length <= 10 or input.length > 10 but intentDetectionConfidence == 1
  const dialogflowResponse = await detectDialogflowIntent(data.searchedText);
  if (
    dialogflowResponse &&
    dialogflowResponse.queryResult &&
    dialogflowResponse.queryResult.intent &&
    (event.input.length <= 10 ||
      dialogflowResponse.queryResult.intentDetectionConfidence == 1)
  ) {
    replies = [
      {
        type: 'text',
        text: dialogflowResponse.queryResult.fulfillmentText,
      },
    ];
    visitor.event({
      ec: 'UserInput',
      ea: 'ChatWithBot',
      el: dialogflowResponse.queryResult.intent.displayName,
    });
    visitor.send();
    return { data, event, userId, replies };
  }

  // Search for articles
  const {
    data: { ListArticles },
  } = await gql`
    query($text: String!) {
      ListArticles(
        filter: { moreLikeThis: { like: $text } }
        orderBy: [{ _score: DESC }]
        first: 4
      ) {
        edges {
          node {
            text
            id
          }
          highlight {
            text
            hyperlinks {
              title
              summary
            }
          }
        }
      }
    }
  `({
    text: event.input,
  });

  const inputSummary = ellipsis(event.input, 12);

  if (ListArticles.edges.length) {
    // Track if find similar Articles in DB.
    visitor.event({ ec: 'UserInput', ea: 'ArticleSearch', el: 'ArticleFound' });

    // Track which Article is searched. And set tracking event as non-interactionHit.
    ListArticles.edges.forEach(edge => {
      visitor.event({
        ec: 'Article',
        ea: 'Search',
        el: edge.node.id,
        ni: true,
      });
    });

    const edgesSortedWithSimilarity = ListArticles.edges
      .map(edge => {
        edge.similarity = stringSimilarity.compareTwoStrings(
          // Remove spaces so that we count word's similarities only
          //
          edge.node.text.replace(/\s/g, ''),
          event.input.replace(/\s/g, '')
        );
        return edge;
      })
      .sort((edge1, edge2) => edge2.similarity - edge1.similarity)
      .slice(0, 9) /* flex carousel has at most 10 bubbles */;

    const hasIdenticalDocs =
      edgesSortedWithSimilarity[0].similarity >= SIMILARITY_THRESHOLD;

    if (edgesSortedWithSimilarity.length === 1 && hasIdenticalDocs) {
      visitor.send();

      // choose for user
      event = {
        type: 'postback',
        input: edgesSortedWithSimilarity[0].node.id,
      };
      return await handlePostback({ data }, 'CHOOSING_ARTICLE', event, userId);
    }

    const articleOptions = edgesSortedWithSimilarity.map(
      ({ node: { text, id }, highlight, similarity }) => {
        const similarityPercentage = Math.round(similarity * 100);
        const similarityEmoji = ['😐', '🙂', '😀', '😃', '😄'][
          Math.floor(similarity * 4.999)
        ];
        const displayTextWhenChosen = ellipsis(text, 25, '...');

        const bodyContents = [];
        if (highlight && !highlight.text) {
          bodyContents.push({
            type: 'text',
            text: t`(Words found in the hyperlink)`,
            size: 'sm',
            color: '#ff7b7b',
            weight: 'bold',
          });
        }
        bodyContents.push({
          type: 'text',
          contents: createHighlightContents(highlight, text), // 50KB for entire Flex carousel
          maxLines: 6,
          flex: 0,
          gravity: 'top',
          weight: 'regular',
          wrap: true,
        });

        return {
          type: 'bubble',
          direction: 'ltr',
          header: {
            type: 'box',
            layout: 'horizontal',
            spacing: 'md',
            paddingBottom: 'none',
            contents: [
              {
                type: 'text',
                text: similarityEmoji,
                flex: 0,
              },
              {
                type: 'text',
                text: t`Looks ${similarityPercentage}% similar`,
                gravity: 'center',
                size: 'sm',
                weight: 'bold',
                wrap: true,
                color: '#AAAAAA',
              },
            ],
          },
          body: {
            type: 'box',
            layout: 'vertical',
            spacing: 'none',
            margin: 'none',
            contents: bodyContents,
          },
          footer: {
            type: 'box',
            layout: 'horizontal',
            contents: [
              {
                type: 'button',
                action: createPostbackAction(
                  t`Choose this one`,
                  id,
                  t`I choose “${displayTextWhenChosen}”`,
                  data.sessionId,
                  'CHOOSING_ARTICLE'
                ),
                style: 'primary',
                color: '#ffb600',
              },
            ],
          },
        };
      }
    );

    // Show "no-article-found" option only when no identical docs are found
    //
    if (!hasIdenticalDocs) {
      articleOptions.push({
        type: 'bubble',
        header: {
          type: 'box',
          layout: 'horizontal',
          paddingBottom: 'none',
          contents: [
            {
              type: 'text',
              text: '😶',
              margin: 'none',
              size: 'sm',
              weight: 'bold',
              color: '#AAAAAA',
            },
          ],
        },
        body: {
          type: 'box',
          layout: 'horizontal',
          spacing: 'none',
          margin: 'none',
          contents: [
            {
              type: 'text',
              text: t`None of these messages matches mine :(`,
              maxLines: 5,
              flex: 0,
              gravity: 'top',
              weight: 'regular',
              wrap: true,
            },
          ],
        },
        footer: {
          type: 'box',
          layout: 'horizontal',
          contents: [
            {
              type: 'button',
              action: createPostbackAction(
                t`Tell us more`,
                POSTBACK_NO_ARTICLE_FOUND,
                t`None of these messages matches mine :(`,
                data.sessionId,
                'CHOOSING_ARTICLE'
              ),
              style: 'primary',
              color: '#ffb600',
            },
          ],
        },
      });
    }

    const templateMessage = {
      type: 'flex',
      altText: t`Please choose the most similar message from the list.`,
      contents: {
        type: 'carousel',
        contents: articleOptions,
      },
    };

    const prefixTextArticleFound = [
      {
        type: 'text',
        text: `🔍 ${t`There are some messages that looks similar to "${inputSummary}" you have sent to me.`}`,
      },
    ];
    const textArticleFound = [
      {
        type: 'text',
        text:
          t`Internet rumors are often mutated and shared.
            Please choose the version that looks the most similar` + '👇',
      },
      templateMessage,
    ];
    if (event.message.type === 'image') {
      replies = textArticleFound;
    } else {
      replies = prefixTextArticleFound.concat(textArticleFound);
    }
  } else {
    // Track if find similar Articles in DB.
    visitor.event({
      ec: 'UserInput',
      ea: 'ArticleSearch',
      el: 'ArticleNotFound',
    });

    if (event.message.type === 'image') {
      replies = [
        {
          type: 'text',
          text: t`We didn't find anything about this image :(`,
        },
        createSuggestOtherFactCheckerReply(),
      ];
    } else {
      replies = [
        createTextMessage({
          text:
            t`Unfortunately, I currently don’t recognize “${inputSummary}”, but I would still like to help.` +
            '\n' +
            t`May I ask you a quick question?`,
        }),
        createArticleSourceReply(data.sessionId),
      ];
    }
  }
  visitor.send();
  return { data, event, userId, replies };
}
<commit_msg>Remove image-type related code in initState<commit_after>import stringSimilarity from 'string-similarity';
import { t } from 'ttag';
import gql from 'src/lib/gql';
import {
  createPostbackAction,
  ellipsis,
  POSTBACK_NO_ARTICLE_FOUND,
  createHighlightContents,
  createTextMessage,
  createArticleSourceReply,
} from './utils';
import ga from 'src/lib/ga';
import detectDialogflowIntent from 'src/lib/detectDialogflowIntent';
import handlePostback from '../handlePostback';

const SIMILARITY_THRESHOLD = 0.95;

export default async function initState(params) {
  let { data, event, userId, replies } = params;
  const state = '__INIT__';

  // Track text message type send by user
  const visitor = ga(userId, state, event.input);
  visitor.event({ ec: 'UserInput', ea: 'MessageType', el: event.message.type });

  // Store user input into context
  data.searchedText = event.input;

  // send input to dialogflow before doing search
  // uses dialogflowResponse as reply only when there's a intent matched and
  // input.length <= 10 or input.length > 10 but intentDetectionConfidence == 1
  const dialogflowResponse = await detectDialogflowIntent(data.searchedText);
  if (
    dialogflowResponse &&
    dialogflowResponse.queryResult &&
    dialogflowResponse.queryResult.intent &&
    (event.input.length <= 10 ||
      dialogflowResponse.queryResult.intentDetectionConfidence == 1)
  ) {
    replies = [
      {
        type: 'text',
        text: dialogflowResponse.queryResult.fulfillmentText,
      },
    ];
    visitor.event({
      ec: 'UserInput',
      ea: 'ChatWithBot',
      el: dialogflowResponse.queryResult.intent.displayName,
    });
    visitor.send();
    return { data, event, userId, replies };
  }

  // Search for articles
  const {
    data: { ListArticles },
  } = await gql`
    query($text: String!) {
      ListArticles(
        filter: { moreLikeThis: { like: $text } }
        orderBy: [{ _score: DESC }]
        first: 4
      ) {
        edges {
          node {
            text
            id
          }
          highlight {
            text
            hyperlinks {
              title
              summary
            }
          }
        }
      }
    }
  `({
    text: event.input,
  });

  const inputSummary = ellipsis(event.input, 12);

  if (ListArticles.edges.length) {
    // Track if find similar Articles in DB.
    visitor.event({ ec: 'UserInput', ea: 'ArticleSearch', el: 'ArticleFound' });

    // Track which Article is searched. And set tracking event as non-interactionHit.
    ListArticles.edges.forEach(edge => {
      visitor.event({
        ec: 'Article',
        ea: 'Search',
        el: edge.node.id,
        ni: true,
      });
    });

    const edgesSortedWithSimilarity = ListArticles.edges
      .map(edge => {
        edge.similarity = stringSimilarity.compareTwoStrings(
          // Remove spaces so that we count word's similarities only
          //
          edge.node.text.replace(/\s/g, ''),
          event.input.replace(/\s/g, '')
        );
        return edge;
      })
      .sort((edge1, edge2) => edge2.similarity - edge1.similarity)
      .slice(0, 9) /* flex carousel has at most 10 bubbles */;

    const hasIdenticalDocs =
      edgesSortedWithSimilarity[0].similarity >= SIMILARITY_THRESHOLD;

    if (edgesSortedWithSimilarity.length === 1 && hasIdenticalDocs) {
      visitor.send();

      // choose for user
      event = {
        type: 'postback',
        input: edgesSortedWithSimilarity[0].node.id,
      };
      return await handlePostback({ data }, 'CHOOSING_ARTICLE', event, userId);
    }

    const articleOptions = edgesSortedWithSimilarity.map(
      ({ node: { text, id }, highlight, similarity }) => {
        const similarityPercentage = Math.round(similarity * 100);
        const similarityEmoji = ['😐', '🙂', '😀', '😃', '😄'][
          Math.floor(similarity * 4.999)
        ];
        const displayTextWhenChosen = ellipsis(text, 25, '...');

        const bodyContents = [];
        if (highlight && !highlight.text) {
          bodyContents.push({
            type: 'text',
            text: t`(Words found in the hyperlink)`,
            size: 'sm',
            color: '#ff7b7b',
            weight: 'bold',
          });
        }
        bodyContents.push({
          type: 'text',
          contents: createHighlightContents(highlight, text), // 50KB for entire Flex carousel
          maxLines: 6,
          flex: 0,
          gravity: 'top',
          weight: 'regular',
          wrap: true,
        });

        return {
          type: 'bubble',
          direction: 'ltr',
          header: {
            type: 'box',
            layout: 'horizontal',
            spacing: 'md',
            paddingBottom: 'none',
            contents: [
              {
                type: 'text',
                text: similarityEmoji,
                flex: 0,
              },
              {
                type: 'text',
                text: t`Looks ${similarityPercentage}% similar`,
                gravity: 'center',
                size: 'sm',
                weight: 'bold',
                wrap: true,
                color: '#AAAAAA',
              },
            ],
          },
          body: {
            type: 'box',
            layout: 'vertical',
            spacing: 'none',
            margin: 'none',
            contents: bodyContents,
          },
          footer: {
            type: 'box',
            layout: 'horizontal',
            contents: [
              {
                type: 'button',
                action: createPostbackAction(
                  t`Choose this one`,
                  id,
                  t`I choose “${displayTextWhenChosen}”`,
                  data.sessionId,
                  'CHOOSING_ARTICLE'
                ),
                style: 'primary',
                color: '#ffb600',
              },
            ],
          },
        };
      }
    );

    // Show "no-article-found" option only when no identical docs are found
    //
    if (!hasIdenticalDocs) {
      articleOptions.push({
        type: 'bubble',
        header: {
          type: 'box',
          layout: 'horizontal',
          paddingBottom: 'none',
          contents: [
            {
              type: 'text',
              text: '😶',
              margin: 'none',
              size: 'sm',
              weight: 'bold',
              color: '#AAAAAA',
            },
          ],
        },
        body: {
          type: 'box',
          layout: 'horizontal',
          spacing: 'none',
          margin: 'none',
          contents: [
            {
              type: 'text',
              text: t`None of these messages matches mine :(`,
              maxLines: 5,
              flex: 0,
              gravity: 'top',
              weight: 'regular',
              wrap: true,
            },
          ],
        },
        footer: {
          type: 'box',
          layout: 'horizontal',
          contents: [
            {
              type: 'button',
              action: createPostbackAction(
                t`Tell us more`,
                POSTBACK_NO_ARTICLE_FOUND,
                t`None of these messages matches mine :(`,
                data.sessionId,
                'CHOOSING_ARTICLE'
              ),
              style: 'primary',
              color: '#ffb600',
            },
          ],
        },
      });
    }

    const templateMessage = {
      type: 'flex',
      altText: t`Please choose the most similar message from the list.`,
      contents: {
        type: 'carousel',
        contents: articleOptions,
      },
    };

    const prefixTextArticleFound = [
      {
        type: 'text',
        text: `🔍 ${t`There are some messages that looks similar to "${inputSummary}" you have sent to me.`}`,
      },
    ];
    const textArticleFound = [
      {
        type: 'text',
        text:
          t`Internet rumors are often mutated and shared.
            Please choose the version that looks the most similar` + '👇',
      },
      templateMessage,
    ];

    replies = prefixTextArticleFound.concat(textArticleFound);
  } else {
    // Track if find similar Articles in DB.
    visitor.event({
      ec: 'UserInput',
      ea: 'ArticleSearch',
      el: 'ArticleNotFound',
    });

    replies = [
      createTextMessage({
        text:
          t`Unfortunately, I currently don’t recognize “${inputSummary}”, but I would still like to help.` +
          '\n' +
          t`May I ask you a quick question?`,
      }),
      createArticleSourceReply(data.sessionId),
    ];
  }
  visitor.send();
  return { data, event, userId, replies };
}
<|endoftext|>


<commit_before>/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2021
 */

import React from 'react';
import PropTypes from 'prop-types';
import { Keyboard, StyleSheet, View } from 'react-native';
import { batch, connect } from 'react-redux';

import { FormControl } from '../FormControl';
import { DataTable, DataTableRow, DataTableHeaderRow } from '../DataTable';
import { PageButton } from '../PageButton';
import { FlexRow } from '../FlexRow';
import { FlexView } from '../FlexView';
import { VaccinePrescriptionInfo } from '../VaccinePrescriptionInfo';
import { PageButtonWithOnePress } from '../PageButtonWithOnePress';

import { selectSpecificEntityState } from '../../selectors/Entities';
import { selectEditingName, selectSortedPatients } from '../../selectors/Entities/name';
import { NameActions } from '../../actions/Entities/NameActions';
import { WizardActions } from '../../actions/WizardActions';
import { VaccinePrescriptionActions } from '../../actions/Entities/VaccinePrescriptionActions';
import { selectPatientSearchFormConfig } from '../../selectors/Entities/vaccinePrescription';
import { getItemLayout, getColumns } from '../../pages/dataTableUtilities';

import { MODALS } from '../constants';
import { buttonStrings, dispensingStrings, generalStrings } from '../../localization';
import globalStyles, { DARK_GREY } from '../../globalStyles';

/**
 * Layout component used for a tab within the vaccine prescription wizard.
 *
 * @prop {Func}   choosePatient    Callback for selecting a patient.
 * @prop {Func}   patients         Current set of patient data.
 * @prop {Func}   onFilterData     Callback for filtering patients.
 * @prop {Func}   onSortData       Callback for sorting patients by column.
 * @prop {Func}   searchTerm       The current filtering search term.
 * @prop {Func}   createPatient    Callback for creating a patient.
 * @prop {Func}   isComplete       Indicator for this prescription being complete.
 * @prop {String} sortKey          Current key the list of patients is sorted by.
 * @prop {Bool}   isAscending      Indicator if the list of patient is sorted ascending.
 */
const PatientSelectComponent = ({
  createPatient,
  currentPatient,
  formConfig,
  isAscending,
  isComplete,
  onCancelPrescription,
  onFilterData,
  onSortData,
  patients,
  selectPatient,
  sortKey,
}) => {
  const columns = React.useMemo(() => getColumns(MODALS.PATIENT_LOOKUP), []);
  const { pageTopViewContainer } = globalStyles;

  const renderRow = React.useCallback(
    listItem => {
      const { item, index } = listItem;
      const rowKey = item.id;
      return (
        <DataTableRow
          rowData={patients[index]}
          rowKey={rowKey}
          getCallback={() => (isComplete ? null : () => selectPatient(item))}
          columns={columns}
          rowIndex={index}
          onPress={isComplete ? null : selectPatient(item)}
        />
      );
    },
    [patients]
  );

  const renderHeader = React.useCallback(
    () => (
      <DataTableHeaderRow
        columns={columns}
        isAscending={isAscending}
        sortKey={sortKey}
        onPress={onSortData}
      />
    ),
    [columns, sortKey, isAscending]
  );

  return (
    <FlexView style={pageTopViewContainer}>
      <FlexRow style={{ marginBottom: 7 }} justifyContent="flex-end">
        <VaccinePrescriptionInfo />
        <PageButton
          text={`${dispensingStrings.new} ${dispensingStrings.patient}`}
          onPress={createPatient}
          style={{ marginLeft: 5 }}
        />
      </FlexRow>

      <View style={localStyles.container}>
        <View style={localStyles.formContainer}>
          <FormControl
            inputConfig={formConfig}
            onSave={onFilterData}
            showCancelButton={false}
            saveButtonText={generalStrings.search}
          />
        </View>
        <View style={localStyles.verticalSeparator} />
        <View style={localStyles.listContainer}>
          <DataTable
            data={patients}
            columns={columns}
            renderHeader={renderHeader}
            renderRow={renderRow}
            keyExtractor={item => item.id}
            getItemLayout={getItemLayout}
          />
        </View>
      </View>

      <FlexRow justifyContent="flex-end" alignItems="flex-end">
        <PageButtonWithOnePress
          text={buttonStrings.cancel}
          onPress={() => onCancelPrescription()}
          style={{ marginRight: 7 }}
        />
        <PageButton
          text={buttonStrings.next}
          debounceTimer={1000}
          onPress={() => selectPatient()}
          isDisabled={!currentPatient}
        />
      </FlexRow>
    </FlexView>
  );
};

const mapDispatchToProps = dispatch => {
  const onSortData = sortKey => dispatch(NameActions.sort(sortKey));
  const onCancelPrescription = () => dispatch(VaccinePrescriptionActions.cancel());
  const onFilterData = searchParameters =>
    batch(() => {
      Keyboard.dismiss();
      dispatch(NameActions.filter(searchParameters));
    });
  const selectPatient = patient =>
    batch(() => {
      dispatch(NameActions.save(patient));
      dispatch(WizardActions.nextTab());
    });
  const createPatient = () =>
    batch(() => {
      dispatch(NameActions.create());
      dispatch(WizardActions.nextTab());
    });

  return { createPatient, onCancelPrescription, onSortData, onFilterData, selectPatient };
};

const mapStateToProps = state => {
  const { wizard } = state;
  const patientState = selectSpecificEntityState(state, 'name');
  const { searchTerm, sortKey, isAscending } = patientState;
  const { isComplete } = wizard;

  const currentPatient = selectEditingName(state);
  const formConfig = selectPatientSearchFormConfig();
  const patients = selectSortedPatients(state);

  return {
    formConfig,
    searchTerm,
    isComplete,
    sortKey,
    isAscending,
    currentPatient,
    patients,
  };
};

PatientSelectComponent.defaultProps = {
  isComplete: false,
  currentPatient: null,
};

PatientSelectComponent.propTypes = {
  selectPatient: PropTypes.func.isRequired,
  formConfig: PropTypes.array.isRequired,
  patients: PropTypes.object.isRequired,
  onFilterData: PropTypes.func.isRequired,
  createPatient: PropTypes.func.isRequired,
  isComplete: PropTypes.bool,
  onSortData: PropTypes.func.isRequired,
  sortKey: PropTypes.string.isRequired,
  isAscending: PropTypes.bool.isRequired,
  onCancelPrescription: PropTypes.func.isRequired,
  currentPatient: PropTypes.object,
};

export const PatientSelect = connect(mapStateToProps, mapDispatchToProps)(PatientSelectComponent);
const localStyles = StyleSheet.create({
  container: {
    height: '75%',
    marginBottom: 20,
    flexDirection: 'row',
    justifyContent: 'space-between',
    backgroundColor: 'white',
  },
  formContainer: {
    flex: 1,
    flexDirection: 'row',
    backgroundColor: 'white',
    alignItems: 'stretch',
  },
  verticalSeparator: {
    width: 10,
    backgroundColor: DARK_GREY,
  },
  listContainer: {
    flex: 3,
    flexDirection: 'row',
    backgroundColor: 'white',
  },
});
<commit_msg>fix page fail<commit_after>/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2021
 */

import React from 'react';
import PropTypes from 'prop-types';
import { Keyboard, StyleSheet, View } from 'react-native';
import { batch, connect } from 'react-redux';

import { FormControl } from '../FormControl';
import { DataTable, DataTableRow, DataTableHeaderRow } from '../DataTable';
import { PageButton } from '../PageButton';
import { FlexRow } from '../FlexRow';
import { FlexView } from '../FlexView';
import { VaccinePrescriptionInfo } from '../VaccinePrescriptionInfo';
import { PageButtonWithOnePress } from '../PageButtonWithOnePress';

import { selectSpecificEntityState } from '../../selectors/Entities';
import { selectSortedPatients } from '../../selectors/Entities/name';
import { NameActions } from '../../actions/Entities/NameActions';
import { WizardActions } from '../../actions/WizardActions';
import { VaccinePrescriptionActions } from '../../actions/Entities/VaccinePrescriptionActions';
import { selectPatientSearchFormConfig } from '../../selectors/Entities/vaccinePrescription';
import { getItemLayout, getColumns } from '../../pages/dataTableUtilities';

import { MODALS } from '../constants';
import { buttonStrings, dispensingStrings, generalStrings } from '../../localization';
import globalStyles, { DARK_GREY } from '../../globalStyles';

/**
 * Layout component used for a tab within the vaccine prescription wizard.
 *
 * @prop {Func}   createPatient         Callback for creating a patient.
 * @prop {object} formConfig            Configuration of the search form
 * @prop {Bool}   isAscending           Indicator if the list of patient is sorted ascending.
 * @prop {Func}   onCancelPrescription  Cancels the prescription and returns to the vaccine page
 * @prop {Func}   onFilterData          Callback for filtering patients.
 * @prop {Func}   onSortData            Callback for sorting patients by column.
 * @prop {Func}   patients              Current set of patient data.
 * @prop {Func}   selectPatient         Callback for selecting a patient.
 * @prop {String} sortKey               Current key the list of patients is sorted by.
 *
 */
const PatientSelectComponent = ({
  createPatient,
  formConfig,
  isAscending,
  onCancelPrescription,
  onFilterData,
  onSortData,
  patients,
  selectPatient,
  sortKey,
}) => {
  const columns = React.useMemo(() => getColumns(MODALS.PATIENT_LOOKUP), []);
  const { pageTopViewContainer } = globalStyles;

  // withCallback () => selectPatient(item)
  const renderRow = React.useCallback(
    listItem => {
      const { item, index } = listItem;
      const rowKey = item.id;
      return (
        <DataTableRow
          rowData={patients[index]}
          rowKey={rowKey}
          getCallback={() => selectPatient(item)}
          columns={columns}
          rowIndex={index}
          onPress={() => selectPatient(item)}
        />
      );
    },
    [patients]
  );

  const renderHeader = React.useCallback(
    () => (
      <DataTableHeaderRow
        columns={columns}
        isAscending={isAscending}
        sortKey={sortKey}
        onPress={onSortData}
      />
    ),
    [columns, sortKey, isAscending]
  );

  return (
    <FlexView style={pageTopViewContainer}>
      <FlexRow style={{ marginBottom: 7 }} justifyContent="flex-end">
        <VaccinePrescriptionInfo />
      </FlexRow>

      <View style={localStyles.container}>
        <View style={localStyles.formContainer}>
          <FormControl
            inputConfig={formConfig}
            onSave={onFilterData}
            showCancelButton={false}
            saveButtonText={generalStrings.search}
          />
        </View>
        <View style={localStyles.verticalSeparator} />
        <View style={localStyles.listContainer}>
          <DataTable
            data={patients}
            columns={columns}
            renderHeader={renderHeader}
            renderRow={renderRow}
            keyExtractor={item => item.id}
            getItemLayout={getItemLayout}
          />
        </View>
      </View>

      <FlexRow justifyContent="flex-end" alignItems="flex-end">
        <PageButtonWithOnePress
          text={buttonStrings.cancel}
          onPress={() => onCancelPrescription()}
          style={{ marginRight: 7 }}
        />
        <PageButton
          text={`${dispensingStrings.new} ${dispensingStrings.patient}`}
          onPress={createPatient}
          style={{ marginLeft: 5 }}
        />
      </FlexRow>
    </FlexView>
  );
};

const mapDispatchToProps = dispatch => {
  const onSortData = sortKey => dispatch(NameActions.sort(sortKey));
  const onCancelPrescription = () => dispatch(VaccinePrescriptionActions.cancel());
  const onFilterData = searchParameters =>
    batch(() => {
      Keyboard.dismiss();
      dispatch(NameActions.filter(searchParameters));
    });
  const selectPatient = patient =>
    batch(() => {
      dispatch(NameActions.save(patient));
      dispatch(WizardActions.nextTab());
    });
  const createPatient = () =>
    batch(() => {
      dispatch(NameActions.create());
      dispatch(WizardActions.nextTab());
    });

  return { createPatient, onCancelPrescription, onSortData, onFilterData, selectPatient };
};

const mapStateToProps = state => {
  const patientState = selectSpecificEntityState(state, 'name');
  const { searchTerm, sortKey, isAscending } = patientState;

  const formConfig = selectPatientSearchFormConfig();
  const patients = selectSortedPatients(state);

  return {
    formConfig,
    searchTerm,
    sortKey,
    isAscending,
    patients,
  };
};

PatientSelectComponent.propTypes = {
  selectPatient: PropTypes.func.isRequired,
  formConfig: PropTypes.array.isRequired,
  patients: PropTypes.object.isRequired,
  onFilterData: PropTypes.func.isRequired,
  createPatient: PropTypes.func.isRequired,
  onSortData: PropTypes.func.isRequired,
  sortKey: PropTypes.string.isRequired,
  isAscending: PropTypes.bool.isRequired,
  onCancelPrescription: PropTypes.func.isRequired,
};

export const PatientSelect = connect(mapStateToProps, mapDispatchToProps)(PatientSelectComponent);
const localStyles = StyleSheet.create({
  container: {
    height: '75%',
    marginBottom: 20,
    flexDirection: 'row',
    justifyContent: 'space-between',
    backgroundColor: 'white',
  },
  formContainer: {
    flex: 1,
    flexDirection: 'row',
    backgroundColor: 'white',
    alignItems: 'stretch',
  },
  verticalSeparator: {
    width: 10,
    backgroundColor: DARK_GREY,
  },
  listContainer: {
    flex: 3,
    flexDirection: 'row',
    backgroundColor: 'white',
  },
});
<|endoftext|>


<commit_before>(function( window, document, undefined ) {
  'use strict';

  function calculateBrightness( ctx ) {
    var width  = ctx.canvas.width,
        height = ctx.canvas.height;

    var imageData = ctx.getImageData( 0, 0, width, height ).data;

    var brightness = 0;

    var i, il;
    var r, g, b, a;
    for ( i = 0, il = imageData.length; i < il; i += 4 ) {
      r = imageData[ i ];
      g = imageData[ i + 1 ];
      b = imageData[ i + 2 ];
      a = imageData[ i + 3 ];

      brightness += 0.299 * r + 0.587 * g + 0.114 * b;
    }

    return brightness / ( width * height );
  }

  var normalCanvas  = document.getElementById( 'normal-canvas' ),
      normalContext = normalCanvas.getContext( '2d' );

  var diffCanvas  = document.getElementById( 'diff-canvas' ),
      diffContext = diffCanvas.getContext( '2d' );

  var image = document.getElementById( 'periodic-img' );

  var WIDTH  = image.width  = normalCanvas.width  = diffCanvas.width  = 256,
      HEIGHT = image.height = normalCanvas.height = diffCanvas.height = 256;

  function drawNormal( ctx ) {
    drawLogo( ctx );
  }

  function drawDiff( ctx ) {
    ctx.globalCompositeOperation = 'normal';

    ctx.clearRect( 0, 0, WIDTH, HEIGHT );
    ctx.drawImage( image, 0, 0, WIDTH, HEIGHT );

    ctx.globalCompositeOperation = 'difference';
    drawLogo( ctx );
  }

  function drawLogo( ctx ) {
    ctx.beginPath();
    ctx.rect( 3, 3, WIDTH - 6, HEIGHT - 6 );

    ctx.lineWidth = 6;
    ctx.strokeStyle = 'white';
    ctx.stroke();

    var grad = ctx.createLinearGradient( 0, 0, WIDTH, HEIGHT );
    grad.addColorStop( 0, '#6F9969' );
    grad.addColorStop( 1, '#202E11' );

    ctx.fillStyle = grad;
    ctx.fill();

    var br = elements.filter(function( element ) {
      return element.symbol === 'Br';
    })[0];

    // Draw symbol.
    ctx.font = 'bold 110pt Helvetica';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillStyle = '#FCFBFF';
    ctx.fillText( br.symbol, 0.5 * WIDTH, 0.5 * HEIGHT - 3 );

    ctx.textAlign = 'left';

    // Draw atomic mass.
    ctx.font = '16pt Helvetica';
    ctx.fillText( br.atomicMass, 18, 30 );
    // Draw atomic number.
    ctx.font = '23pt Helvetica';
    ctx.fillText( br.atomicNumber, 13, 196 );
    // Draw electron configuration.
    ctx.font = '15pt Helvetica';
    ctx.fillText( br.electronConfiguration, 15, 230 );
    // Draw oxidation states.
    ctx.font = '16pt Helvetica';
    ctx.textAlign = 'right';
    br.oxidationStates.forEach(function( state, index ) {
      ctx.fillText( state, 246, 30 + index * 25 );
    });
  }

  var elements = [];

  image.src = './img/br.png';
  image.onload = function() {
    var xhr = new XMLHttpRequest();
    xhr.open( 'GET', './json/elements.json', true );

    xhr.onreadystatechange = function() {
      if ( this.readyState === 4 && this.status === 200 ) {
        elements = JSON.parse( this.responseText );
        drawNormal( normalContext );
        drawDiff( diffContext );

        console.log( 'norm: ' + calculateBrightness( normalContext ) );
        console.log( 'diff: ' + calculateBrightness( diffContext ) );
      }
    };

    xhr.send();
  };
}) ( window, document );
<commit_msg>ascii[periodic]: Move logo drawing to separate canvas, allowing for better diff. Convert to px units only.<commit_after>(function( window, document, undefined ) {
  'use strict';

  function calculateBrightness( ctx ) {
    var width  = ctx.canvas.width,
        height = ctx.canvas.height;

    var imageData = ctx.getImageData( 0, 0, width, height ).data;

    var brightness = 0;

    var i, il;
    var r, g, b, a;
    for ( i = 0, il = imageData.length; i < il; i += 4 ) {
      r = imageData[ i ];
      g = imageData[ i + 1 ];
      b = imageData[ i + 2 ];
      a = imageData[ i + 3 ];

      brightness += 0.299 * r + 0.587 * g + 0.114 * b;
    }

    return brightness / ( width * height );
  }

  var normalCanvas  = document.getElementById( 'normal-canvas' ),
      normalContext = normalCanvas.getContext( '2d' );

  var diffCanvas  = document.getElementById( 'diff-canvas' ),
      diffContext = diffCanvas.getContext( '2d' );

  var logoCanvas = document.createElement( 'canvas' ),
      logoContext = logoCanvas.getContext( '2d' );

  var image = document.getElementById( 'periodic-img' );

  var WIDTH  = image.width  = normalCanvas.width  = diffCanvas.width  = 256,
      HEIGHT = image.height = normalCanvas.height = diffCanvas.height = 256;

  logoCanvas.width = WIDTH;
  logoCanvas.height = HEIGHT;

  function drawNormal( ctx, symbol, options ) {
    drawLogo( logoContext, symbol, options );
    ctx.drawImage( logoCanvas, 0, 0, WIDTH, HEIGHT );
  }

  function drawDiff( ctx, symbol, options ) {
    ctx.globalCompositeOperation = 'normal';

    ctx.clearRect( 0, 0, WIDTH, HEIGHT );
    ctx.drawImage( image, 0, 0, WIDTH, HEIGHT );

    ctx.globalCompositeOperation = 'difference';

    drawLogo( logoContext, symbol, options );
    ctx.drawImage( logoCanvas, 0, 0, WIDTH, HEIGHT );
  }

  function drawLogo( ctx, symbol, options ) {
    ctx.beginPath();
    ctx.rect( 4, 4, WIDTH - 8, HEIGHT - 8 );

    ctx.lineWidth = 8;
    ctx.strokeStyle = 'white';
    ctx.stroke();

    var grad = ctx.createLinearGradient( 0, 0, WIDTH, HEIGHT );
    grad.addColorStop( 0, 'rgb(121, 184, 124)' );
    grad.addColorStop( 1, 'rgb(25, 38, 6)' );

    ctx.fillStyle = grad;
    ctx.fill();

    var el = elements.filter(function( element ) {
      return element.symbol === symbol;
    })[0];

    // Draw symbol.
    ctx.font = 'bold 130px Helvetica'; // Smaller font for wide symbols.
    ctx.font = 'bold 150px Helvetica';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillStyle = '#fff';
    ctx.fillText( el.symbol, 0.5 * WIDTH, 0.5 * HEIGHT - 3 );

    ctx.textAlign = 'left';

    // Draw atomic mass.
    ctx.font = '21px Helvetica';
    ctx.fillText( el.atomicMass, 18, 30 );
    // Draw atomic number.
    ctx.font = '31px Helvetica';
    ctx.fillText( el.atomicNumber, 13, 195 );
    // Draw electron configuration.
    ctx.font = '20px Helvetica';
    ctx.fillText( el.electronConfiguration, 15, 230 );
    // Draw oxidation states.
    ctx.font = '21px Helvetica';
    ctx.textAlign = 'right';
    el.oxidationStates.forEach(function( state, index ) {
      ctx.fillText( state, 246, 30 + index * 25 );
    });
  }

  var elements = [];

  image.src = './img/br.png';
  image.onload = function() {
    var xhr = new XMLHttpRequest();
    xhr.open( 'GET', './json/elements.json', true );

    xhr.onreadystatechange = function() {
      if ( this.readyState === 4 && this.status === 200 ) {
        elements = JSON.parse( this.responseText );

        var symbol = 'Br';
        drawNormal( normalContext, symbol );
        drawDiff( diffContext, symbol );

        console.log( 'norm: ' + calculateBrightness( normalContext ) );
        console.log( 'diff: ' + calculateBrightness( diffContext ) );

        var current = 12.7373641815189;
        console.log( current - calculateBrightness( diffContext ) );
      }
    };

    xhr.send();
  };
}) ( window, document );
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

'use strict';

KylinApp
  .controller('ProjectMetaCtrl', function ($scope, $q, ProjectService, QueryService) {
    $scope.selectedSrcDb = [];
    $scope.selectedSrcTable = {};
    $scope.treeOptions = {
      nodeChildren: "columns",
      injectClasses: {
        ul: "a1",
        li: "a2",
        liSelected: "a7",
        iExpanded: "a3",
        iCollapsed: "a4",
        iLeaf: "a5",
        label: "a6",
        labelSelected: "a8"
      }
    };

    $scope.showSelected = function (table) {
      if (table.uuid) {
        $scope.selectedSrcTable = table;
      }
      else {
        $scope.selectedSrcTable.selectedSrcColumn = table;
      }
    }

    $scope.projectMetaLoad = function () {
      var defer = $q.defer();
      $scope.selectedSrcDb = [];
      $scope.loading = true;
      QueryService.getTables({project: $scope.projectModel.selectedProject}, {}, function (tables) {
        var tableMap = [];
        angular.forEach(tables, function (table) {
          if (!tableMap[table.table_SCHEM]) {
            tableMap[table.table_SCHEM] = [];
          }
          table.name = table.table_NAME;
          angular.forEach(table.columns, function (column, index) {
            column.name = column.column_NAME;
          });
          tableMap[table.table_SCHEM].push(table);
        });

        for (var key in  tableMap) {
          var tables = tableMap[key];
          $scope.selectedSrcDb.push({
            "name": key,
            "columns": tables
          });
        }

        $scope.loading = false;
        defer.resolve();
      });
      return defer.promise;
    };

    $scope.$watch('projectModel.selectedProject', function (newValue, oldValue) {
      if (newValue) {
        $scope.projectMetaLoad();
      }
    });

    $scope.trimType = function (typeName) {
      if (typeName.match(/VARCHAR/i)) {
        typeName = "VARCHAR";
      }

      return typeName.trim().toLowerCase();
    }

  });

<commit_msg>KYLIN-946, check project when refresh query page<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

'use strict';

KylinApp
  .controller('ProjectMetaCtrl', function ($scope, $q, ProjectService, QueryService) {
    $scope.selectedSrcDb = [];
    $scope.selectedSrcTable = {};
    $scope.treeOptions = {
      nodeChildren: "columns",
      injectClasses: {
        ul: "a1",
        li: "a2",
        liSelected: "a7",
        iExpanded: "a3",
        iCollapsed: "a4",
        iLeaf: "a5",
        label: "a6",
        labelSelected: "a8"
      }
    };

    $scope.showSelected = function (table) {
      if (table.uuid) {
        $scope.selectedSrcTable = table;
      }
      else {
        $scope.selectedSrcTable.selectedSrcColumn = table;
      }
    }

    $scope.projectMetaLoad = function () {
      var defer = $q.defer();
      $scope.selectedSrcDb = [];
      if(!$scope.projectModel.getSelectedProject()) {
        return;
      }
      $scope.loading = true;
      QueryService.getTables({project: $scope.projectModel.getSelectedProject()}, {}, function (tables) {
        var tableMap = [];
        angular.forEach(tables, function (table) {
          if (!tableMap[table.table_SCHEM]) {
            tableMap[table.table_SCHEM] = [];
          }
          table.name = table.table_NAME;
          angular.forEach(table.columns, function (column, index) {
            column.name = column.column_NAME;
          });
          tableMap[table.table_SCHEM].push(table);
        });

        for (var key in  tableMap) {
          var tables = tableMap[key];
          $scope.selectedSrcDb.push({
            "name": key,
            "columns": tables
          });
        }

        $scope.loading = false;
        defer.resolve();
      });
      return defer.promise;
    };

    $scope.$watch('projectModel.selectedProject', function (newValue, oldValue) {
      if (newValue) {
        $scope.projectMetaLoad();
      }
    });

    $scope.trimType = function (typeName) {
      if (typeName.match(/VARCHAR/i)) {
        typeName = "VARCHAR";
      }

      return typeName.trim().toLowerCase();
    }

  });

<|endoftext|>


<commit_before>'use strict';

angular.module('nncloud')
  .controller('ExportCtrl', ['$scope','commonDataService', function ($scope, commonDataService) {
	  $scope.formats = [{name:"XML"}];
	  $scope.download = function()
	  {
		  if($scope.selectedFormat.name == "XML")
		  {
			var blob = new Blob(commonDataService.xmlNetwork,  {type:"application/xml;charset=utf-8;" });
			var downloadLink = angular.element('');
			downloadLink.attr('href',window.URL.createObjectURL(blob));
			downloadLink.attr('download', 'network.xml');
			downloadLink[0].click();
		  }
	  }
  }]);
<commit_msg>New way of downloading network<commit_after>'use strict';

angular.module('nncloud')
  .controller('ExportCtrl', ['$scope','commonDataService', function ($scope, commonDataService) {
	  $scope.formats = [{name:"XML"}];
	  $scope.download = function()
	  {
		  if($scope.selectedFormat.name == "XML")
		  {
			var fileContent = commonDataService.xmlNetwork;
			var element = document.createElement('a');
			element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(fileContent));
			element.setAttribute('download', 'myNetwork.xml');
			element.style.display = 'none';
			document.body.appendChild(element);
			element.click();
			document.body.removeChild(element);
		  }
	  }
  }]);
<|endoftext|>


<commit_before>// TODO:
// - Check success of Roon API calls
// - Volume control
// - Seek

"use strict";

var pjson = require('./package.json');
var package_version = pjson.version; //process.env.npm_package_version does not work under forever

// Roon setup

var RoonApi        = require("node-roon-api"),
  RoonApiSettings  = require('node-roon-api-settings'),
  RoonApiStatus    = require('node-roon-api-status'),
  RoonApiTransport = require('node-roon-api-transport');

var core;
var roon = new RoonApi({
    extension_id:        'roonrest',
    display_name:        'Roon Rest Controller',
    display_version:     package_version,
    publisher:           'Matthew Eckhaus',
    email:               'contact@roonlabs.com',
    website:             'https://github.com/matteck/roonrest',

    core_paired: function(core_) {
        core = core_;
    },
    core_unpaired: function(core_) {
	      core = undefined;
    }
});

var mysettings = roon.load_config("settings") || {
    zone:             null,
};

function makelayout(settings) {
    var l = {
        values:    settings,
        layout:    [],
        has_error: false
    };

  l.layout.push({
	type:    "zone",
	title:   "Zone",
	setting: "zone",
    });

    return l;
}

var svc_settings = new RoonApiSettings(roon, {
    get_settings: function(cb) {
        cb(makelayout(mysettings));
    },
    save_settings: function(req, isdryrun, settings) {
	let l = makelayout(settings.values);
        req.send_complete(l.has_error ? "NotValid" : "Success", { settings: l });

        if (!isdryrun && !l.has_error) {
            mysettings = l.values;
            svc_settings.update_settings(l);
            roon.save_config("settings", mysettings);
            update_status();
        }
    }
});

var svc_status = new RoonApiStatus(roon);

roon.init_services({
    required_services:   [ RoonApiTransport ],
    provided_services:   [ svc_settings, svc_status ],
});

function update_status() {
  if (mysettings.zone.name) {
	  svc_status.set_status("Ready. Attached to " + mysettings.zone.name, false);
  } else {
    svc_status.set_status("Loaded. No zone assigned");
  }
}

update_status();
roon.start_discovery();

// Express
var port = 3000

var express = require('express')
var app = express()

var not_registered_error = "The RoonRest extension is not enabled. Please enable it in Roon settings and try again.";

// Zone-specific control actions
app.put('/api/v1/zone/current/control/:action(play|pause|playpause|stop|previous|next)', function (req, res) {
  if (core == undefined) {
    res.status('503').send(not_registered_error);
  } else {
    var action = req.params['action'];
    console.log('action is ' + action);
    core.services.RoonApiTransport.control(mysettings.zone, action);
    res.send('OK');
  }
})

// Universal control actions
app.put('/api/v1/zone/all/control/pause', function (req, res) {
  if (core == undefined) {
    res.status('503').send(not_registered_error);
  } else {
    console.log('Doing pause_all');
    core.services.RoonApiTransport.pause_all();
    res.send('OK');
  }
})

app.put('/api/v1/zone/current/settings/:name(shuffle|auto_radio)/:value(on|off)', function (req, res) {
  if (core == undefined) {
    res.status('503').send(not_registered_error);
  } else {
    console.log('Doing set ' + req.params['name'] + ' to ' + req.params['value']);
    var settings_object = {};
    settings_object[req.params['name']] = ((req.params['value'] == 'on') ? 1 : 0 );
    core.services.RoonApiTransport.change_settings(mysettings.zone, settings_object);
    res.send('OK');
  }
})

app.get('/api/v1', function (req, res) {
  res.send('RoonRest extensions v1')
})

app.listen(port, function () {
  console.log('RoonRest extension started, listening on port ' + port)
})<commit_msg>Added attribution and de-obfuscated code<commit_after>// TODO:
// - Check success of Roon API calls
// - Volume control
// - Seek

// Parts of this code are copied from https://github.com/RoonLabs/roon-extension-powermate

"use strict";

var pjson = require('./package.json');
var package_version = pjson.version; //process.env.npm_package_version does not work under forever

// Roon setup

var RoonApi        = require("node-roon-api"),
  RoonApiSettings  = require('node-roon-api-settings'),
  RoonApiStatus    = require('node-roon-api-status'),
  RoonApiTransport = require('node-roon-api-transport');

var core;
var roon = new RoonApi({
    extension_id:        'roonrest',
    display_name:        'Roon Rest Controller',
    display_version:     package_version,
    publisher:           'Matthew Eckhaus',
    email:               'contact@roonlabs.com',
    website:             'https://github.com/matteck/roonrest',

    core_paired: function(core_) {
        core = core_;
    },
    core_unpaired: function(core_) {
	      core = undefined;
    }
});

var mysettings = roon.load_config("settings") || {
    zone:             null,
};

function makelayout(settings) {
    var l = {
        values:    settings,
        layout:    [],
        has_error: false
    };

  l.layout.push({
	type:    "zone",
	title:   "Zone",
	setting: "zone",
    });

    return l;
}

var svc_settings = new RoonApiSettings(roon, {
    get_settings: function(cb) {
        cb(makelayout(mysettings));
    },
    save_settings: function(req, isdryrun, settings) {
	let l = makelayout(settings.values);
        req.send_complete(l.has_error ? "NotValid" : "Success", { settings: l });

        if (!isdryrun && !l.has_error) {
            mysettings = l.values;
            svc_settings.update_settings(l);
            roon.save_config("settings", mysettings);
            update_status();
        }
    }
});

var svc_status = new RoonApiStatus(roon);

roon.init_services({
    required_services:   [ RoonApiTransport ],
    provided_services:   [ svc_settings, svc_status ],
});

function update_status() {
  if (mysettings.zone.name) {
	  svc_status.set_status("Ready. Attached to " + mysettings.zone.name, false);
  } else {
    svc_status.set_status("Loaded. No zone assigned");
  }
}

update_status();
roon.start_discovery();

// Express
var port = 3000

var express = require('express')
var app = express()

var not_registered_error = "The RoonRest extension is not enabled. Please enable it in Roon settings and try again.";

// Zone-specific control actions
app.put('/api/v1/zone/current/control/:action(play|pause|playpause|stop|previous|next)', function (req, res) {
  if (core == undefined) {
    res.status('503').send(not_registered_error);
  } else {
    var action = req.params['action'];
    console.log('action is ' + action);
    core.services.RoonApiTransport.control(mysettings.zone, action);
    res.send('OK');
  }
})

// Universal control actions
app.put('/api/v1/zone/all/control/pause', function (req, res) {
  if (core == undefined) {
    res.status('503').send(not_registered_error);
  } else {
    console.log('Doing pause_all');
    core.services.RoonApiTransport.pause_all();
    res.send('OK');
  }
})

app.put('/api/v1/zone/current/settings/:name(shuffle|auto_radio)/:value(on|off)', function (req, res) {
  if (core == undefined) {
    res.status('503').send(not_registered_error);
  } else {
    console.log('Doing set ' + req.params['name'] + ' to ' + req.params['value']);
    var settings_object = {};
    var setting_name = req.params['name']
    if (req.params['value'] == 'on') {
      settings_object[setting_name] = 1;
    } else {
      settings_object[setting_name] = 0;
    }
    core.services.RoonApiTransport.change_settings(mysettings.zone, settings_object);
    res.send('OK');
  }
})

app.get('/api/v1', function (req, res) {
  res.send('RoonRest extensions v1')
})

app.listen(port, function () {
  console.log('RoonRest extension started, listening on port ' + port)
})<|endoftext|>


<commit_before>import chai from 'chai';
import rh from "../src/lib/database/dbHelper.js";
import rc from "../src/lib/database/constants.js";
import UserDal from "../src/lib/dal/UserDal.js";
import DbTestSession from "./DbTestSession.js";
import constants from "./testConstants.js";
import googleProfileSample from "./resources/googleProfileSample.js";

const assert = chai.assert;
let users = new UserDal({dbName: constants.DB_TESTS});

describe('BaseDal', function() {

    let testSession = new DbTestSession();
    testSession.setupSession(before, beforeEach, after, afterEach, [rc.TABLE_USERS]);

    it('Create and filter', (done) => {
        users.upsert(testSession.connection, {
            displayName: 'André Pena',
            photo: 'pic134'
        }, (error) => {
            if(error) {
                throw error;
            }
            users.filter(testSession.connection, { photo: 'pic134'}, (error, result) => {
                if(error) {
                    throw error;
                }
                assert.strictEqual(result.length, 1);
                done();
            });
        });
    });

});<commit_msg>Tests for BaseDal.find<commit_after>import chai from 'chai';
import rh from "../src/lib/database/dbHelper.js";
import rc from "../src/lib/database/constants.js";
import UserDal from "../src/lib/dal/UserDal.js";
import DbTestSession from "./DbTestSession.js";
import constants from "./testConstants.js";
import googleProfileSample from "./resources/googleProfileSample.js";

const assert = chai.assert;
let users = new UserDal({dbName: constants.DB_TESTS});

describe('BaseDal', function() {

    let testSession = new DbTestSession();
    testSession.setupSession(before, beforeEach, after, afterEach, [rc.TABLE_USERS]);

    it('Create and filter', (done) => {
        users.insert(testSession.connection, {
            displayName: 'André Pena',
            photo: 'pic134'
        }, (error) => {
            if(error) {
                throw error;
            }
            users.filter(testSession.connection, { photo: 'pic134'}, (error, users) => {
                if(error) {
                    throw error;
                }
                assert.strictEqual(users.length, 1);
                done();
            });
        });
    });

    it('Create and find', (done) => {
        users.insert(testSession.connection, {
            id: 'b446822f-8057-4124-8337-01c78209cf70',
            displayName: 'André Pena',
            photo: 'pic134'
        }, (error) => {
            if(error) {
                throw error;
            }
            users.find(testSession.connection, 'b446822f-8057-4124-8337-01c78209cf70', (error, user) => {
                if(error) {
                    throw error;
                }
                assert.strictEqual(user.id, 'b446822f-8057-4124-8337-01c78209cf70');
                assert.strictEqual(user.displayName, 'André Pena');
                done();
            });
        });
    });

});<|endoftext|>


<commit_before>/* eslint-env node, mocha */

const assert = require('chai').assert;

const filmwebFactory = require('..');
const {waitForEmptyQueue} = require('../lib/delayUtils');

const {login, password} = require('./lib/credentials');

const BAD_QUERY = 'sldjfslkdjf';
const GOOD_QUERY = 'star wars';
const PRECISE_QUERY = 'star wars new hope';

describe('FilmWeb#search()', function () {
    var filmweb;

    this.slow(10000);
    this.timeout(30000);

    beforeEach(function () {
        this.timeout(120000);
        waitForEmptyQueue();
    });

    before(function createInstance () {
        filmweb = filmwebFactory.createInstance();
    });

    after(function () {
        filmweb.destroy();
    });

    describe('with nonsense query', function () {
        var searchPromise;
        before(function getResults () {
            searchPromise = filmweb.search(BAD_QUERY);
        });

        it('should not return any results', function () {
            return searchPromise
                .then(({total}) => {
                    if (0 !== total)
                    {
                        throw new Error(`Got ${total} results.`);
                    }
                });
        });
    });

    describe('with sensible query', function () {
        var searchPromise;
        before(function getResults () {
            searchPromise = filmweb.search(GOOD_QUERY);
        });

        it('should return results', function () {
            return searchPromise;
        });

        it('should return more than 5 pages of results', function () {
            return searchPromise
                .then(({totalPages}) => assert.isAbove(totalPages, 5));
        });

        it('all results [on the page] should contain search query', function () {
            var titleRegExp = new RegExp(GOOD_QUERY, 'i');
            return searchPromise
                .then(({items}) => {
                    items.forEach(item => {
                        assert.match(
                            [item.title]
                                .concat(item.alternativeTitles.map(title => title.value))
                                .join('\0'),
                            titleRegExp
                        );
                    });
                });
        });
    });

    describe('first element of search result', function () {
        const EXPECTED_PROPS = [
            'url', 'title', 'year', 'posterUrl',
            'ratings', 'alternativeTitles', 'directors', 'cast',
        ];
        var result;
        before(function getResults () {
            return filmweb.search(PRECISE_QUERY)
                .then(({items: [firstResult]}) => { result = firstResult; } );
        });

        for (let prop of EXPECTED_PROPS)
        {
            it('should contain ' + prop, function () {
                assert(prop in result);
            });
        }

        it('should contain average rating', function () {
            assert('average' in result.ratings);
        });

        it('should have some directors', function () {
            assert.isAbove(result.directors.length, 0);
        });

        it('should have url and name in first director', function () {
            assert('url' in result.directors[0]);
            assert('name' in result.directors[0]);
        });

        it('should have some cast', function () {
            assert.isAbove(result.directors.length, 0);
        });

        it('should have url and name in first cast member', function () {
            assert('url' in result.cast[0]);
            assert('name' in result.cast[0]);
        });
    });

    describe('with sensible query on authenticated instance', function () {
        var searchPromise;

        before(function getResults () {
            this.timeout(40000);
            searchPromise = filmweb
                .authenticate(login, password)
                .then(() => filmweb.search(GOOD_QUERY));
        });

        it('should contain user rating data when authenticated', function () {
            return searchPromise.then(({items}) => {
                items.forEach(item => {
                    if (! ('user' in item.ratings))
                    {
                        throw Error('Missing user rating data.');
                    }
                });
            });
        });
    });
});
<commit_msg>Increase timeout on Fimlweb#search() test using authentication<commit_after>/* eslint-env node, mocha */

const assert = require('chai').assert;

const filmwebFactory = require('..');
const {waitForEmptyQueue} = require('../lib/delayUtils');

const {login, password} = require('./lib/credentials');

const BAD_QUERY = 'sldjfslkdjf';
const GOOD_QUERY = 'star wars';
const PRECISE_QUERY = 'star wars new hope';

const TIMEOUT = 40000;
const AUTH_TIMEOUT = 40000;

describe('FilmWeb#search()', function () {
    var filmweb;

    this.slow(10000);
    this.timeout(TIMEOUT);

    beforeEach(function () {
        this.timeout(120000);
        waitForEmptyQueue();
    });

    before(function createInstance () {
        filmweb = filmwebFactory.createInstance();
    });

    after(function () {
        filmweb.destroy();
    });

    describe('with nonsense query', function () {
        var searchPromise;
        before(function getResults () {
            searchPromise = filmweb.search(BAD_QUERY);
        });

        it('should not return any results', function () {
            return searchPromise
                .then(({total}) => {
                    if (0 !== total)
                    {
                        throw new Error(`Got ${total} results.`);
                    }
                });
        });
    });

    describe('with sensible query', function () {
        var searchPromise;
        before(function getResults () {
            searchPromise = filmweb.search(GOOD_QUERY);
        });

        it('should return results', function () {
            return searchPromise;
        });

        it('should return more than 5 pages of results', function () {
            return searchPromise
                .then(({totalPages}) => assert.isAbove(totalPages, 5));
        });

        it('all results [on the page] should contain search query', function () {
            var titleRegExp = new RegExp(GOOD_QUERY, 'i');
            return searchPromise
                .then(({items}) => {
                    items.forEach(item => {
                        assert.match(
                            [item.title]
                                .concat(item.alternativeTitles.map(title => title.value))
                                .join('\0'),
                            titleRegExp
                        );
                    });
                });
        });
    });

    describe('first element of search result', function () {
        const EXPECTED_PROPS = [
            'url', 'title', 'year', 'posterUrl',
            'ratings', 'alternativeTitles', 'directors', 'cast',
        ];
        var result;
        before(function getResults () {
            return filmweb.search(PRECISE_QUERY)
                .then(({items: [firstResult]}) => { result = firstResult; } );
        });

        for (let prop of EXPECTED_PROPS)
        {
            it('should contain ' + prop, function () {
                assert(prop in result);
            });
        }

        it('should contain average rating', function () {
            assert('average' in result.ratings);
        });

        it('should have some directors', function () {
            assert.isAbove(result.directors.length, 0);
        });

        it('should have url and name in first director', function () {
            assert('url' in result.directors[0]);
            assert('name' in result.directors[0]);
        });

        it('should have some cast', function () {
            assert.isAbove(result.directors.length, 0);
        });

        it('should have url and name in first cast member', function () {
            assert('url' in result.cast[0]);
            assert('name' in result.cast[0]);
        });
    });

    describe('with sensible query on authenticated instance', function () {
        var searchPromise;

        this.timeout(AUTH_TIMEOUT + TIMEOUT);

        before(function getResults () {
            this.timeout(AUTH_TIMEOUT);
            searchPromise = filmweb
                .authenticate(login, password)
                .then(() => filmweb.search(GOOD_QUERY));
        });

        it('should contain user rating data when authenticated', function () {
            this.timeout(TIMEOUT);
            return searchPromise.then(({items}) => {
                items.forEach(item => {
                    if (! ('user' in item.ratings))
                    {
                        throw Error('Missing user rating data.');
                    }
                });
            });
        });
    });
});
<|endoftext|>


<commit_before>/* eslint no-undef: "off" */

const SRC = '../src';
const MockServer = require(`${SRC}/MockServer`);
const qnodeError = require('qnode-error');
const supertest = require('supertest');
const MissingParamError = qnodeError.MissingParamError;
const InternalError = qnodeError.InternalError;
const RequestError = qnodeError.RequestError;
const Beans = require('qnode-beans');
const finishTestcase = require('jasmine-supertest');

const Logger = require('qnode-log');

Logger.prototype.debug = function() {};
Logger.prototype.error = function() {};
Logger.prototype.info = function() {};
Logger.prototype.warn = function() {};

describe("MockServer test suite: ", function() {

    it("resolveDomain()", function() {
        expect(MockServer.resolveDomain('localhost:12345')).toBe('localhost');
        expect(MockServer.resolveDomain('localhost')).toBe('localhost');
        expect(MockServer.resolveDomain('127.0.0.1:12345')).toBe('127.0.0.1');
        expect(MockServer.resolveDomain('127.0.0.1')).toBe('127.0.0.1');
    });

    it("happy", function(done) {
        const def = {
            port: 12345,
            vhosts: {
                test: {}
            }
        };
        const r = new MockServer(def);
        r.init();
        const server = r.start();

        const rule = { path: '/abc' };
        r.putRule('test', rule);

        supertest(server).get('/abc').expect(500).end(function(err, res) {
            if (err) {
                console.error(err);
                return done(err);
            }
            done();
        });
    });

    it("normalizeRequest(): normalize request", function() {
        const header = {};
        const body = {};

        const req = {
            header: header,
            method: 'POST',
            url: 'http://temp/query?a=b',
            path: 'http://temp/query',
            charset: 'UTF-8',
            query: 'a=b',
            protocol: 'HTTP',
            ip: '192.168.0.1',
            body: body
        };

        const r = MockServer.normalizeRequest(req);

        expect(r.header).toEqual(header);
        expect(r.method).toBe('post');
        expect(r.url).toBe('http://temp/query?a=b');
        expect(r.path).toBe('http://temp/query');
        expect(r.charset).toBe('utf-8');
        expect(r.query).toBe('a=b');
        expect(r.protocol).toBe('http');
        expect(r.ip).toBe('192.168.0.1');
        expect(r.body).toEqual(body);
    });

});<commit_msg>Fix MockServerTest<commit_after>/* eslint no-undef: "off" */

const SRC = '../src';
const MockServer = require(`${SRC}/MockServer`);
const qnodeError = require('qnode-error');
const supertest = require('supertest');
const MissingParamError = qnodeError.MissingParamError;
const InternalError = qnodeError.InternalError;
const RequestError = qnodeError.RequestError;
const Beans = require('qnode-beans');
const finishTestcase = require('jasmine-supertest');

const Logger = require('qnode-log');

Logger.prototype.debug = function() {};
Logger.prototype.error = function() {};
Logger.prototype.info = function() {};
Logger.prototype.warn = function() {};

describe("MockServer test suite: ", function() {

    it("resolveDomain()", function() {
        expect(MockServer.resolveDomain('localhost:12345')).toBe('localhost');
        expect(MockServer.resolveDomain('localhost')).toBe('localhost');
        expect(MockServer.resolveDomain('127.0.0.1:12345')).toBe('127.0.0.1');
        expect(MockServer.resolveDomain('127.0.0.1')).toBe('127.0.0.1');
    });

    it("happy", function(done) {
        const def = {
            port: 12345,
            vhosts: {
                test: {
                    name: 'test',
                    domains: ['test']
                }
            }
        };
        const r = new MockServer(def);
        r.init();
        const server = r.start();

        const rule = { path: '/abc' };
        r.putRule('test', rule);

        supertest(server).get('/abc').expect(500).end(function(err, res) {
            if (err) {
                console.error(err);
                return done(err);
            }
            done();
        });
    });

    it("normalizeRequest(): normalize request", function() {
        const header = {};
        const body = {};

        const req = {
            header: header,
            method: 'POST',
            url: 'http://temp/query?a=b',
            path: 'http://temp/query',
            charset: 'UTF-8',
            query: 'a=b',
            protocol: 'HTTP',
            ip: '192.168.0.1',
            body: body
        };

        const r = MockServer.normalizeRequest(req);

        expect(r.header).toEqual(header);
        expect(r.method).toBe('post');
        expect(r.url).toBe('http://temp/query?a=b');
        expect(r.path).toBe('http://temp/query');
        expect(r.charset).toBe('utf-8');
        expect(r.query).toBe('a=b');
        expect(r.protocol).toBe('http');
        expect(r.ip).toBe('192.168.0.1');
        expect(r.body).toEqual(body);
    });

});<|endoftext|>