<commit_before>/*
 * apps-test.js: Tests for `jitsu apps *` command(s).
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

var assert = require('assert'),
    fs = require('fs'),
    path = require('path'),
    nock = require('nock'),
    vows = require('vows'),
    jitsu = require('../../lib/jitsu'),
    macros = require('../helpers/macros');

var shouldNodejitsuOk = macros.shouldNodejitsuOk,
    useAppFixture = macros.useAppFixture;

var mainDirectory = process.cwd();

vows.describe('jitsu/commands/apps').addBatch({
  'apps list': shouldNodejitsuOk(function setup() {
    nock('http://api.mockjitsu.com')
      .get('/apps/tester')
      .reply(200, {
        apps:[{
          name: 'application',
          state: 'stopped',
          subdomain:'application',
          scripts: { start: './server.js' },
          snapshots: [{ filename: 'FILENAME' }]
        }]
      }, { 'x-powered-by': 'Nodejitsu' })
  })
}).addBatch({
  'apps view application2': shouldNodejitsuOk(function setup() {
    nock('http://api.mockjitsu.com')
      .get('/apps/tester/application2')
      .reply(200, {
        app: {
          _id: 'tester/application2',
          name: 'application2',
          state: 'stopped',
          subdomain:'application2',
          scripts: { start: './server.js' },
          snapshots: [{
            id: '0.0.0',
            filename: 'FILENAME',
            ctime: 1234567898765,
          }]
        }
      }, { 'x-powered-by': 'Nodejitsu' })
  })
}).addBatch({
  'apps start application3': shouldNodejitsuOk(function setup() {
    nock('http://api.mockjitsu.com')
      .post('/apps/tester/application3/start', {})
        .reply(200, {}, { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/application3')
        .reply(200, {
          app: {
            state: 'started',
            subdomain: 'application3'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
  })
}).addBatch({
  'apps stop application3': shouldNodejitsuOk(function setup() {
    nock('http://api.mockjitsu.com')
      .post('/apps/tester/application3/stop', {})
        .reply(200, '', { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/application3')
      .reply(200, {
          app: { state: 'stopped' }
      }, { 'x-powered-by': 'Nodejitsu' })
  })
})
.addBatch({
  'apps view': shouldNodejitsuOk(function setup() {

    useAppFixture();

    nock('http://api.mockjitsu.com')
      .get('/apps/tester/example-app')
      .reply(200, {
        app: {
          _id: 'tester/example-app',
          name: 'application',
          state: 'stopped',
          subdomain: 'application',
          scripts: { start: './server.js' },
          snapshots: [{
            id: '0.0.0-1',
            filename: 'FILENAME',
            ctime: 1234567898765,
          }]
        }
      }, { 'x-powered-by': 'Nodejitsu' })
  }, function assertion (err) {
    process.chdir(mainDirectory);
    assert.ok(!err);
  })
}).addBatch({
  'apps start': shouldNodejitsuOk(function setup() {

    useAppFixture();

    nock('http://api.mockjitsu.com')
      .post('/apps/tester/example-app/start', {})
        .reply(200, '', { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/example-app')
        .reply(200, {
          app: {
            state: 'started',
            subdomain: 'example-app'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
  }, function assertion (err) {
    process.chdir(mainDirectory);
    assert.ok(!err);
  })
}).addBatch({
  'apps stop': shouldNodejitsuOk(function setup() {

    useAppFixture();

    nock('http://api.mockjitsu.com')
      .post('/apps/tester/example-app/stop', {})
        .reply(200, '', { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/example-app')
      .reply(200, {
          app: { state: 'stopped' }
      }, { 'x-powered-by': 'Nodejitsu' })
  }, function assertion (err) {
    process.chdir(mainDirectory);
    assert.ok(!err);
  })
}).addBatch({
  'apps deploy': shouldNodejitsuOk(function setup() {

    // Simulate a broken package.json
    useAppFixture('{ "name": "example-app", "subdomain": "example-app", }');

    jitsu.prompt.override.answer = 'yes';
    jitsu.prompt.override.name = 'example-app';
    jitsu.prompt.override.subdomain = 'example-app';
    jitsu.prompt.override.version = '0.0.0-1';
    jitsu.prompt.override['scripts.start'] = 'server.js';
    jitsu.prompt.override['engines.node'] = '0.6.x';

    nock('http://api.mockjitsu.com')
      .filteringRequestBody(function (route) {
        return '*';
      })
      .post('/apps/tester/example-app/snapshots/0.0.0-2', '*')
        .reply(200, {
          app: { state: 'stopped' }
        }, { 'x-powered-by': 'Nodejitsu' })

    nock('http://api.mockjitsu.com')
      .post('/apps/tester/example-app/available', {
        name: 'example-app',
        subdomain: 'example-app',
        scripts: {
          start: 'server.js'
        },
        version: '0.0.0-1',
        engines: {
          node: '0.6.x'
        }
      })
        .reply(200, {
          available: true,
        }, { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/example-app')
        .reply(200, {
          app: {
            name: 'example-app',
            state: 'stopped',
            subdomain:'example-app',
            scripts: { start: './server.js' },
            snapshots: [{ filename: 'FILENAME' }]
          }
        }, { 'x-powered-by': 'Nodejitsu' })
      .put('/apps/tester/example-app', {
          name: 'example-app',
          subdomain: 'example-app',
          scripts: {
            start: 'server.js'
          },
          version: '0.0.0-2',
          engines: { node: '0.6.x' }
        })
        .reply(200, {
          app: { state: 'stopped' }
        }, { 'x-powered-by': 'Nodejitsu' })
      .post('/apps/tester/example-app/snapshots/0.0.0-2/activate', {})
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0-2'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
      .post('/apps/tester/example-app/stop', {})
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0-2'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
      .post('/apps/tester/example-app/start', {})
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0-2'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/example-app')
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0-1'
          }
        }, { 'x-powered-by': 'Nodejitsu' });

  }, function assertion (err) {
    process.chdir(mainDirectory);
    assert.ok(!err);
  })
}).export(module);
<commit_msg>[test] Working deploy test for a taken domain<commit_after>/*
 * apps-test.js: Tests for `jitsu apps *` command(s).
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

var assert = require('assert'),
    fs = require('fs'),
    path = require('path'),
    nock = require('nock'),
    vows = require('vows'),
    jitsu = require('../../lib/jitsu'),
    macros = require('../helpers/macros');

var shouldNodejitsuOk = macros.shouldNodejitsuOk,
    useAppFixture = macros.useAppFixture;

var mainDirectory = process.cwd();

vows.describe('jitsu/commands/apps').addBatch({
  'apps list': shouldNodejitsuOk(function setup() {
    nock('http://api.mockjitsu.com')
      .get('/apps/tester')
      .reply(200, {
        apps:[{
          name: 'application',
          state: 'stopped',
          subdomain:'application',
          scripts: { start: './server.js' },
          snapshots: [{ filename: 'FILENAME' }]
        }]
      }, { 'x-powered-by': 'Nodejitsu' })
  })
}).addBatch({
  'apps view application2': shouldNodejitsuOk(function setup() {
    nock('http://api.mockjitsu.com')
      .get('/apps/tester/application2')
      .reply(200, {
        app: {
          _id: 'tester/application2',
          name: 'application2',
          state: 'stopped',
          subdomain:'application2',
          scripts: { start: './server.js' },
          snapshots: [{
            id: '0.0.0',
            filename: 'FILENAME',
            ctime: 1234567898765,
          }]
        }
      }, { 'x-powered-by': 'Nodejitsu' })
  })
}).addBatch({
  'apps start application3': shouldNodejitsuOk(function setup() {
    nock('http://api.mockjitsu.com')
      .post('/apps/tester/application3/start', {})
        .reply(200, {}, { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/application3')
        .reply(200, {
          app: {
            state: 'started',
            subdomain: 'application3'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
  })
}).addBatch({
  'apps stop application3': shouldNodejitsuOk(function setup() {
    nock('http://api.mockjitsu.com')
      .post('/apps/tester/application3/stop', {})
        .reply(200, '', { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/application3')
      .reply(200, {
          app: { state: 'stopped' }
      }, { 'x-powered-by': 'Nodejitsu' })
  })
})
.addBatch({
  'apps view': shouldNodejitsuOk(function setup() {

    useAppFixture();

    nock('http://api.mockjitsu.com')
      .get('/apps/tester/example-app')
      .reply(200, {
        app: {
          _id: 'tester/example-app',
          name: 'application',
          state: 'stopped',
          subdomain: 'application',
          scripts: { start: './server.js' },
          snapshots: [{
            id: '0.0.0-1',
            filename: 'FILENAME',
            ctime: 1234567898765,
          }]
        }
      }, { 'x-powered-by': 'Nodejitsu' })
  }, function assertion (err) {
    process.chdir(mainDirectory);
    assert.ok(!err);
  })
}).addBatch({
  'apps start': shouldNodejitsuOk(function setup() {

    useAppFixture();

    nock('http://api.mockjitsu.com')
      .post('/apps/tester/example-app/start', {})
        .reply(200, '', { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/example-app')
        .reply(200, {
          app: {
            state: 'started',
            subdomain: 'example-app'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
  }, function assertion (err) {
    process.chdir(mainDirectory);
    assert.ok(!err);
  })
}).addBatch({
  'apps stop': shouldNodejitsuOk(function setup() {

    useAppFixture();

    nock('http://api.mockjitsu.com')
      .post('/apps/tester/example-app/stop', {})
        .reply(200, '', { 'x-powered-by': 'Nodejitsu' });

  }, function assertion (err) {
    process.chdir(mainDirectory);
    assert.ok(!err);
  })
}).addBatch({
  'apps deploy': shouldNodejitsuOk(function setup() {

    // Test for trying to use a taken subdomain
    useAppFixture({
      "name": "example-app",
      "subdomain": "taken",
      "scripts": {
        "start": "server.js"
      },
      "version": "0.0.0",
      "engines": {
        "node": "0.6.x"
      }
    });

    jitsu.prompt.override.answer = 'yes';
    jitsu.prompt.override.subdomain = 'example-app';

    nock('http://api.mockjitsu.com')
      .get('/apps/tester/example-app')
        .reply(500, {
          error: 'not found'
        }, { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester')
        .reply(200, {
          apps: []
        }, { 'x-powered-by': 'Nodejitsu' })
      .post('/apps/tester/example-app/available', {
        name: 'example-app',
        subdomain: 'taken',
        scripts: {
          start: 'server.js'
        },
        version: '0.0.0',
        engines: {
          node: '0.6.x'
        },
        analyzed: true,
        state: 'stopped'
      })
        .reply(200, {
          available: false,
          subdomain: false,
          appname: true
        }, { 'x-powered-by': 'Nodejitsu' })
      .post('/apps/tester/example-app/available', {
        name: 'example-app',
        scripts: {
          start: 'server.js'
        },
        version: '0.0.0',
        engines: {
          node: '0.6.x'
        },
        analyzed: true,
        state: 'stopped',
        subdomain: 'example-app'
      })
        .reply(200, {
          available: true,
        }, { 'x-powered-by': 'Nodejitsu' })
      .put('/apps/tester/example-app', {
          name: 'example-app',
          scripts: {
            start: 'server.js'
          },
          version: '0.0.0',
          engines: { node: '0.6.x' },
          analyzed: true,
          state: 'stopped',
          subdomain: 'example-app'
        })
        .reply(200, {
          app: { state: 'stopped' }
        }, { 'x-powered-by': 'Nodejitsu' })
      .post('/apps/tester/example-app', {
        name: 'example-app',
        scripts: {
          start: 'server.js'
        },
        version: '0.0.0',
        engines: { node: '0.6.x' },
        analyzed: true,
        state: 'stopped',
        subdomain: 'example-app'
      })
        .reply(201, "", { 'x-powered-by': 'Nodejitsu' });

    nock('http://api.mockjitsu.com')
      .filteringRequestBody(function (route) {
        return '*';
      })
      .post('/apps/tester/example-app/snapshots/0.0.0', '*')
        .reply(200, {
          app: { state: 'stopped' }
        }, { 'x-powered-by': 'Nodejitsu' });

    nock('http://api.mockjitsu.com')
      .post('/apps/tester/example-app/snapshots/0.0.0/activate', {})
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
      .post('/apps/tester/example-app/stop', {})
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
      .post('/apps/tester/example-app/start', {})
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/example-app')
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0'
          }
        }, { 'x-powered-by': 'Nodejitsu' });

  }, function assertion (err) {
    process.chdir(mainDirectory);
    assert.ok(!err);
  })
}).addBatch({
  'apps deploy': shouldNodejitsuOk(function setup() {

    // Simulate a broken package.json
    useAppFixture('{ "name": "example-app", "subdomain": "example-app", }');

    jitsu.prompt.override.answer = 'yes';
    jitsu.prompt.override.name = 'example-app';
    jitsu.prompt.override.subdomain = 'example-app';
    jitsu.prompt.override.version = '0.0.0-1';
    jitsu.prompt.override['scripts.start'] = 'server.js';
    jitsu.prompt.override['engines.node'] = '0.6.x';

    nock('http://api.mockjitsu.com')
      .filteringRequestBody(function (route) {
        return '*';
      })
      .post('/apps/tester/example-app/snapshots/0.0.0-2', '*')
        .reply(200, {
          app: { state: 'stopped' }
        }, { 'x-powered-by': 'Nodejitsu' })

    nock('http://api.mockjitsu.com')
      .post('/apps/tester/example-app/available', {
        name: 'example-app',
        subdomain: 'example-app',
        scripts: {
          start: 'server.js'
        },
        version: '0.0.0-1',
        engines: {
          node: '0.6.x'
        }
      })
        .reply(200, {
          available: true,
        }, { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/example-app')
        .reply(200, {
          app: {
            name: 'example-app',
            state: 'stopped',
            subdomain:'example-app',
            scripts: { start: './server.js' },
            snapshots: [{ filename: 'FILENAME' }]
          }
        }, { 'x-powered-by': 'Nodejitsu' })
      .put('/apps/tester/example-app', {
          name: 'example-app',
          subdomain: 'example-app',
          scripts: {
            start: 'server.js'
          },
          version: '0.0.0-2',
          engines: { node: '0.6.x' }
        })
        .reply(200, {
          app: { state: 'stopped' }
        }, { 'x-powered-by': 'Nodejitsu' })
      .post('/apps/tester/example-app/snapshots/0.0.0-2/activate', {})
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0-2'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
      .post('/apps/tester/example-app/stop', {})
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0-2'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
      .post('/apps/tester/example-app/start', {})
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0-2'
          }
        }, { 'x-powered-by': 'Nodejitsu' })
      .get('/apps/tester/example-app')
        .reply(200, {
          app: {
            name: 'example-app',
            subdomain: 'example-app',
            scripts: { start: 'server.js' },
            version: '0.0.0-1'
          }
        }, { 'x-powered-by': 'Nodejitsu' });

  }, function assertion (err) {
    process.chdir(mainDirectory);
    assert.ok(!err);
  })
}).export(module);
<|endoftext|>


<commit_before>/*
## Incrementally track the structure of nodes with metadata

This metadata can be later utilized for schema inference
(via building `exampleValue` or directly)

### Usage example:

```javascript
  const node1 = { id: '1', foo: 25, bar: 'str' }
  const node2 = { id: '1', foo: 'conflict' }

  let meta = { ignoredFields: new Set(['id']) }
  meta = addNode(meta, node1)
  meta = addNode(meta, node2)
  console.log(meta.fieldMap)
  // outputs: {
  //   foo: {
  //     int: { total: 1, example: 25 },
  //     string: { total: 1, example: 'conflict' },
  //   },
  //   bar: {
  //     string: { total: 1, example: 'str' },
  //   },
  // }

  const example1 = getExampleObject({ meta, typeName, typeConflictReporter })
  console.log(example1)
  // outputs { bar: 'str' }
  // and reports conflicts discovered

  meta = deleteNode(meta, node2)
  console.log(meta.fieldMap)
  // outputs: {
  //   foo: {
  //     int: { total: 1, example: 25 },
  //     string: { total: 0, example: 'conflict' },
  //   },
  //   bar: { string: { total: 1, example: 'str' } },
  // }

  const example2 = getExampleObject({ meta, typeName, typeConflictReporter })
  // outputs: { foo: 25, bar: 'str' }
```

`addNode`, `deleteNode`, `getExampleObject` are O(N) where N is the number
of fields in the node object (including nested fields)

### Metadata structure

```javascript
type TypeMetadata = {
  total?: number,
  ignored?: boolean,
  ignoredFields?: Set<string>,
  fieldMap?: { [string]: ValueDescriptor },
  typeName?: string,
  dirty?: boolean, // tracks structural changes only
  disabled?: boolean,
}

type Count = number
type NodeId = string

type ValueDescriptor = {
  int?: TypeInfoNumber,
  float?: TypeInfoNumber,
  date?: TypeInfoDate,
  string?: TypeInfoString,
  boolean?: TypeInfoBoolean,
  array?: TypeInfoArray,
  relatedNode?: TypeInfoRelatedNode,
  relatedNodeList?: TypeInfoRelatedNode,
  object?: TypeInfoObject,
}

abstract type TypeInfo = {
  first?: NodeId | void, // Set to undefined if "del"
  total: Count,
}

type TypeInfoString = TypeInfo & {
  empty: Count,
  example: string,
}

type TypeInfoDate = TypeInfo & {
  example: string,
}

type TypeInfoNumber = {
  example: number,
}

type TypeInfoBoolean = {
  example: boolean,
}

// "dprops" is "descriptor props", which makes it easier to search for than "props"
type TypeInfoObject = TypeInfo & {
  dprops?: {[name: "number" | "string" | "boolean" | "null" | "date" | "string" | "array" | "object"]: Descriptor},
}

type TypeInfoArray = TypeInfo & {
  item: ValueDescriptor,
}

type TypeInfoRelatedNode = TypeInfo & {
  nodes: { [NodeId]: Count }
}

```

### Caveats

* Conflict tracking for arrays is tricky, i.e.: { a: [5, "foo"] } and { a: [5] }, { a: ["foo"] }
  are represented identically in metadata. To workaround it we additionally track first NodeId:
  { a: { array: { item: { int: { total: 1, first: `1` }, string: { total: 1, first: `1` } }}
  { a: { array: { item: { int: { total: 1, first: `1` }, string: { total: 1, first: `2` } }}
  This way we can produce more useful conflict reports
  (still rare edge cases possible when reporting may be confusing, i.e. when node is deleted)
*/

const { isEqual } = require(`lodash`)
const is32BitInteger = require(`./is-32-bit-integer`)
const { looksLikeADate } = require(`../types/date`)

const getType = (value, key) => {
  // Staying as close as possible to GraphQL types
  switch (typeof value) {
    case `number`:
      return is32BitInteger(value) ? `int` : `float`
    case `string`:
      if (key.includes(`___NODE`)) {
        return `relatedNode`
      }
      return looksLikeADate(value) ? `date` : `string`
    case `boolean`:
      return `boolean`
    case `object`:
      if (value === null) return `null`
      if (value instanceof Date) return `date`
      if (value instanceof String) return `string`
      if (Array.isArray(value)) {
        if (value.length === 0) {
          return `null`
        }
        return key.includes(`___NODE`) ? `relatedNodeList` : `array`
      }
      if (!Object.keys(value).length) return `null`
      return `object`
    default:
      // bigint, symbol, function, unknown (host objects in IE were typeof "unknown", for example)
      return `null`
  }
}

const updateValueDescriptorObject = (
  value,
  typeInfo,
  nodeId,
  operation,
  metadata,
  path
) => {
  path.push(value)

  const { dprops = {} } = typeInfo
  typeInfo.dprops = dprops

  Object.keys(value).forEach(key => {
    const v = value[key]

    let descriptor = dprops[key]
    if (descriptor === undefined) {
      descriptor = {}
      dprops[key] = descriptor
    }

    updateValueDescriptor(nodeId, key, v, operation, descriptor, metadata, path)
  })

  path.pop()
}

const updateValueDescriptorArray = (
  value,
  key,
  typeInfo,
  nodeId,
  operation,
  metadata,
  path
) => {
  value.forEach(item => {
    let descriptor = typeInfo.item
    if (descriptor === undefined) {
      descriptor = {}
      typeInfo.item = descriptor
    }

    updateValueDescriptor(
      nodeId,
      key,
      item,
      operation,
      descriptor,
      metadata,
      path
    )
  })
}

const updateValueDescriptorRelNodes = (
  listOfNodeIds,
  delta,
  operation,
  typeInfo,
  metadata
) => {
  const { nodes = {} } = typeInfo
  typeInfo.nodes = nodes

  listOfNodeIds.forEach(nodeId => {
    nodes[nodeId] = (nodes[nodeId] || 0) + delta

    // Treat any new related node addition or removal as a structural change
    // FIXME: this will produce false positives as this node can be
    //  of the same type as another node already in the map (but we don't know it here)
    if (nodes[nodeId] === 0 || (operation === `add` && nodes[nodeId] === 1)) {
      metadata.dirty = true
    }
  })
}

const updateValueDescriptorString = (value, delta, typeInfo) => {
  if (value === ``) {
    const { empty = 0 } = typeInfo
    typeInfo.empty = empty + delta
  }
  typeInfo.example =
    typeof typeInfo.example !== `undefined` ? typeInfo.example : value
}

const updateValueDescriptor = (
  nodeId,
  key,
  value,
  operation = `add` /* add | del */,
  descriptor,
  metadata,
  path
) => {
  // The object may be traversed multiple times from root.
  // Each time it does it should not revisit the same node twice
  if (path.includes(value)) {
    return
  }

  const typeName = getType(value, key)

  if (typeName === `null`) {
    return
  }

  const delta = operation === `del` ? -1 : 1
  let typeInfo = descriptor[typeName]
  if (typeInfo === undefined) {
    typeInfo = descriptor[typeName] = { total: 0 }
  }
  typeInfo.total += delta

  // Keeping track of structural changes
  // (when value of a new type is added or an existing type has no more values assigned)
  if (typeInfo.total === 0 || (operation === `add` && typeInfo.total === 1)) {
    metadata.dirty = true
  }

  // Keeping track of the first node for this type. Only used for better conflict reporting.
  // (see Caveats section in the header comments)
  if (operation === `add`) {
    if (!typeInfo.first) {
      typeInfo.first = nodeId
    }
  } else if (operation === `del`) {
    if (typeInfo.first === nodeId || typeInfo.total === 0) {
      typeInfo.first = undefined
    }
  }

  switch (typeName) {
    case `object`:
      updateValueDescriptorObject(
        value,
        typeInfo,
        nodeId,
        operation,
        metadata,
        path
      )
      return
    case `array`:
      updateValueDescriptorArray(
        value,
        key,
        typeInfo,
        nodeId,
        operation,
        metadata,
        path
      )
      return
    case `relatedNode`:
      updateValueDescriptorRelNodes(
        [value],
        delta,
        operation,
        typeInfo,
        metadata
      )
      return
    case `relatedNodeList`:
      updateValueDescriptorRelNodes(value, delta, operation, typeInfo, metadata)
      return
    case `string`:
      updateValueDescriptorString(value, delta, typeInfo)
      return
  }

  // int, float, boolean, null

  typeInfo.example =
    typeof typeInfo.example !== `undefined` ? typeInfo.example : value
}

const mergeObjectKeys = (dpropsKeysA, dpropsKeysB) => {
  const dprops = Object.keys(dpropsKeysA)
  const otherProps = Object.keys(dpropsKeysB)
  return [...new Set(dprops.concat(otherProps))]
}

const descriptorsAreEqual = (descriptor, otherDescriptor) => {
  const types = possibleTypes(descriptor)
  const otherTypes = possibleTypes(otherDescriptor)

  // Empty are equal
  if (types.length === 0 && otherTypes.length === 0) {
    return true
  }
  // Conflicting and non-matching types are not equal
  // TODO: consider descriptors with equal conflicts as equal?
  if (types.length > 1 || otherTypes.length > 1 || types[0] !== otherTypes[0]) {
    return false
  }
  switch (types[0]) {
    case `array`:
      return descriptorsAreEqual(
        descriptor.array.item,
        otherDescriptor.array.item
      )
    case `object`: {
      const dpropsKeys = mergeObjectKeys(
        descriptor.object.dprops,
        otherDescriptor.object.dprops
      )
      return dpropsKeys.every(prop =>
        descriptorsAreEqual(
          descriptor.object.dprops[prop],
          otherDescriptor.object.dprops[prop]
        )
      )
    }
    case `relatedNode`:
    case `relatedNodeList`: {
      return isEqual(descriptor.nodes, otherDescriptor.nodes)
    }
    default:
      return true
  }
}

const nodeFields = (node, ignoredFields = new Set()) =>
  Object.keys(node).filter(key => !ignoredFields.has(key))

const updateTypeMetadata = (metadata = initialMetadata(), operation, node) => {
  if (metadata.disabled) {
    return metadata
  }
  metadata.total = (metadata.total || 0) + (operation === `add` ? 1 : -1)
  if (metadata.ignored) {
    return metadata
  }
  const { ignoredFields, fieldMap = {} } = metadata

  nodeFields(node, ignoredFields).forEach(field => {
    let descriptor = fieldMap[field]
    if (descriptor === undefined) {
      descriptor = {}
      fieldMap[field] = descriptor
    }

    updateValueDescriptor(
      node.id,
      field,
      node[field],
      operation,
      descriptor,
      metadata,
      []
    )
  })
  metadata.fieldMap = fieldMap
  return metadata
}

const ignore = (metadata = initialMetadata(), set = true) => {
  metadata.ignored = set
  metadata.fieldMap = {}
  return metadata
}

const disable = (metadata = initialMetadata(), set = true) => {
  metadata.disabled = set
  return metadata
}

const addNode = (metadata, node) => updateTypeMetadata(metadata, `add`, node)
const deleteNode = (metadata, node) => updateTypeMetadata(metadata, `del`, node)
const addNodes = (metadata = initialMetadata(), nodes) =>
  nodes.reduce(addNode, metadata)

const possibleTypes = (descriptor = {}) =>
  Object.keys(descriptor).filter(type => descriptor[type].total > 0)

const isEmpty = ({ fieldMap }) =>
  Object.keys(fieldMap).every(
    field => possibleTypes(fieldMap[field]).length === 0
  )

// Even empty type may still have nodes
const hasNodes = typeMetadata => typeMetadata.total > 0

const haveEqualFields = (
  { fieldMap = {} } = {},
  { fieldMap: otherFieldMap = {} } = {}
) => {
  const fields = mergeObjectKeys(fieldMap, otherFieldMap)
  return fields.every(field =>
    descriptorsAreEqual(fieldMap[field], otherFieldMap[field])
  )
}

const initialMetadata = state => {
  return {
    typeName: undefined,
    disabled: false,
    ignored: false,
    dirty: false,
    total: 0,
    ignoredFields: undefined,
    fieldMap: {},
    ...state,
  }
}

module.exports = {
  addNode,
  addNodes,
  deleteNode,
  ignore,
  disable,
  isEmpty,
  hasNodes,
  haveEqualFields,
  initialMetadata,
}
<commit_msg>fix(gatsby): do not rebuild schema having identical conflicts (#20099)<commit_after>/*
## Incrementally track the structure of nodes with metadata

This metadata can be later utilized for schema inference
(via building `exampleValue` or directly)

### Usage example:

```javascript
  const node1 = { id: '1', foo: 25, bar: 'str' }
  const node2 = { id: '1', foo: 'conflict' }

  let meta = { ignoredFields: new Set(['id']) }
  meta = addNode(meta, node1)
  meta = addNode(meta, node2)
  console.log(meta.fieldMap)
  // outputs: {
  //   foo: {
  //     int: { total: 1, example: 25 },
  //     string: { total: 1, example: 'conflict' },
  //   },
  //   bar: {
  //     string: { total: 1, example: 'str' },
  //   },
  // }

  const example1 = getExampleObject({ meta, typeName, typeConflictReporter })
  console.log(example1)
  // outputs { bar: 'str' }
  // and reports conflicts discovered

  meta = deleteNode(meta, node2)
  console.log(meta.fieldMap)
  // outputs: {
  //   foo: {
  //     int: { total: 1, example: 25 },
  //     string: { total: 0, example: 'conflict' },
  //   },
  //   bar: { string: { total: 1, example: 'str' } },
  // }

  const example2 = getExampleObject({ meta, typeName, typeConflictReporter })
  // outputs: { foo: 25, bar: 'str' }
```

`addNode`, `deleteNode`, `getExampleObject` are O(N) where N is the number
of fields in the node object (including nested fields)

### Metadata structure

```javascript
type TypeMetadata = {
  total?: number,
  ignored?: boolean,
  ignoredFields?: Set<string>,
  fieldMap?: { [string]: ValueDescriptor },
  typeName?: string,
  dirty?: boolean, // tracks structural changes only
  disabled?: boolean,
}

type Count = number
type NodeId = string

type ValueDescriptor = {
  int?: TypeInfoNumber,
  float?: TypeInfoNumber,
  date?: TypeInfoDate,
  string?: TypeInfoString,
  boolean?: TypeInfoBoolean,
  array?: TypeInfoArray,
  relatedNode?: TypeInfoRelatedNode,
  relatedNodeList?: TypeInfoRelatedNode,
  object?: TypeInfoObject,
}

abstract type TypeInfo = {
  first?: NodeId | void, // Set to undefined if "del"
  total: Count,
}

type TypeInfoString = TypeInfo & {
  empty: Count,
  example: string,
}

type TypeInfoDate = TypeInfo & {
  example: string,
}

type TypeInfoNumber = {
  example: number,
}

type TypeInfoBoolean = {
  example: boolean,
}

// "dprops" is "descriptor props", which makes it easier to search for than "props"
type TypeInfoObject = TypeInfo & {
  dprops?: {[name: "number" | "string" | "boolean" | "null" | "date" | "string" | "array" | "object"]: Descriptor},
}

type TypeInfoArray = TypeInfo & {
  item: ValueDescriptor,
}

type TypeInfoRelatedNode = TypeInfo & {
  nodes: { [NodeId]: Count }
}

```

### Caveats

* Conflict tracking for arrays is tricky, i.e.: { a: [5, "foo"] } and { a: [5] }, { a: ["foo"] }
  are represented identically in metadata. To workaround it we additionally track first NodeId:
  { a: { array: { item: { int: { total: 1, first: `1` }, string: { total: 1, first: `1` } }}
  { a: { array: { item: { int: { total: 1, first: `1` }, string: { total: 1, first: `2` } }}
  This way we can produce more useful conflict reports
  (still rare edge cases possible when reporting may be confusing, i.e. when node is deleted)
*/

const { isEqual } = require(`lodash`)
const is32BitInteger = require(`./is-32-bit-integer`)
const { looksLikeADate } = require(`../types/date`)

const getType = (value, key) => {
  // Staying as close as possible to GraphQL types
  switch (typeof value) {
    case `number`:
      return is32BitInteger(value) ? `int` : `float`
    case `string`:
      if (key.includes(`___NODE`)) {
        return `relatedNode`
      }
      return looksLikeADate(value) ? `date` : `string`
    case `boolean`:
      return `boolean`
    case `object`:
      if (value === null) return `null`
      if (value instanceof Date) return `date`
      if (value instanceof String) return `string`
      if (Array.isArray(value)) {
        if (value.length === 0) {
          return `null`
        }
        return key.includes(`___NODE`) ? `relatedNodeList` : `array`
      }
      if (!Object.keys(value).length) return `null`
      return `object`
    default:
      // bigint, symbol, function, unknown (host objects in IE were typeof "unknown", for example)
      return `null`
  }
}

const updateValueDescriptorObject = (
  value,
  typeInfo,
  nodeId,
  operation,
  metadata,
  path
) => {
  path.push(value)

  const { dprops = {} } = typeInfo
  typeInfo.dprops = dprops

  Object.keys(value).forEach(key => {
    const v = value[key]

    let descriptor = dprops[key]
    if (descriptor === undefined) {
      descriptor = {}
      dprops[key] = descriptor
    }

    updateValueDescriptor(nodeId, key, v, operation, descriptor, metadata, path)
  })

  path.pop()
}

const updateValueDescriptorArray = (
  value,
  key,
  typeInfo,
  nodeId,
  operation,
  metadata,
  path
) => {
  value.forEach(item => {
    let descriptor = typeInfo.item
    if (descriptor === undefined) {
      descriptor = {}
      typeInfo.item = descriptor
    }

    updateValueDescriptor(
      nodeId,
      key,
      item,
      operation,
      descriptor,
      metadata,
      path
    )
  })
}

const updateValueDescriptorRelNodes = (
  listOfNodeIds,
  delta,
  operation,
  typeInfo,
  metadata
) => {
  const { nodes = {} } = typeInfo
  typeInfo.nodes = nodes

  listOfNodeIds.forEach(nodeId => {
    nodes[nodeId] = (nodes[nodeId] || 0) + delta

    // Treat any new related node addition or removal as a structural change
    // FIXME: this will produce false positives as this node can be
    //  of the same type as another node already in the map (but we don't know it here)
    if (nodes[nodeId] === 0 || (operation === `add` && nodes[nodeId] === 1)) {
      metadata.dirty = true
    }
  })
}

const updateValueDescriptorString = (value, delta, typeInfo) => {
  if (value === ``) {
    const { empty = 0 } = typeInfo
    typeInfo.empty = empty + delta
  }
  typeInfo.example =
    typeof typeInfo.example !== `undefined` ? typeInfo.example : value
}

const updateValueDescriptor = (
  nodeId,
  key,
  value,
  operation = `add` /* add | del */,
  descriptor,
  metadata,
  path
) => {
  // The object may be traversed multiple times from root.
  // Each time it does it should not revisit the same node twice
  if (path.includes(value)) {
    return
  }

  const typeName = getType(value, key)

  if (typeName === `null`) {
    return
  }

  const delta = operation === `del` ? -1 : 1
  let typeInfo = descriptor[typeName]
  if (typeInfo === undefined) {
    typeInfo = descriptor[typeName] = { total: 0 }
  }
  typeInfo.total += delta

  // Keeping track of structural changes
  // (when value of a new type is added or an existing type has no more values assigned)
  if (typeInfo.total === 0 || (operation === `add` && typeInfo.total === 1)) {
    metadata.dirty = true
  }

  // Keeping track of the first node for this type. Only used for better conflict reporting.
  // (see Caveats section in the header comments)
  if (operation === `add`) {
    if (!typeInfo.first) {
      typeInfo.first = nodeId
    }
  } else if (operation === `del`) {
    if (typeInfo.first === nodeId || typeInfo.total === 0) {
      typeInfo.first = undefined
    }
  }

  switch (typeName) {
    case `object`:
      updateValueDescriptorObject(
        value,
        typeInfo,
        nodeId,
        operation,
        metadata,
        path
      )
      return
    case `array`:
      updateValueDescriptorArray(
        value,
        key,
        typeInfo,
        nodeId,
        operation,
        metadata,
        path
      )
      return
    case `relatedNode`:
      updateValueDescriptorRelNodes(
        [value],
        delta,
        operation,
        typeInfo,
        metadata
      )
      return
    case `relatedNodeList`:
      updateValueDescriptorRelNodes(value, delta, operation, typeInfo, metadata)
      return
    case `string`:
      updateValueDescriptorString(value, delta, typeInfo)
      return
  }

  // int, float, boolean, null

  typeInfo.example =
    typeof typeInfo.example !== `undefined` ? typeInfo.example : value
}

const mergeObjectKeys = (dpropsKeysA, dpropsKeysB) => {
  const dprops = Object.keys(dpropsKeysA)
  const otherProps = Object.keys(dpropsKeysB)
  return [...new Set(dprops.concat(otherProps))]
}

const descriptorsAreEqual = (descriptor, otherDescriptor) => {
  const types = possibleTypes(descriptor)
  const otherTypes = possibleTypes(otherDescriptor)

  const childDescriptorsAreEqual = type => {
    switch (type) {
      case `array`:
        return descriptorsAreEqual(
          descriptor.array.item,
          otherDescriptor.array.item
        )
      case `object`: {
        const dpropsKeys = mergeObjectKeys(
          descriptor.object.dprops,
          otherDescriptor.object.dprops
        )
        return dpropsKeys.every(prop =>
          descriptorsAreEqual(
            descriptor.object.dprops[prop],
            otherDescriptor.object.dprops[prop]
          )
        )
      }
      case `relatedNode`:
      case `relatedNodeList`: {
        return isEqual(descriptor.nodes, otherDescriptor.nodes)
      }
      default:
        return true
    }
  }

  // Equal when all possible types are equal (including conflicts)
  return isEqual(types, otherTypes) && types.every(childDescriptorsAreEqual)
}

const nodeFields = (node, ignoredFields = new Set()) =>
  Object.keys(node).filter(key => !ignoredFields.has(key))

const updateTypeMetadata = (metadata = initialMetadata(), operation, node) => {
  if (metadata.disabled) {
    return metadata
  }
  metadata.total = (metadata.total || 0) + (operation === `add` ? 1 : -1)
  if (metadata.ignored) {
    return metadata
  }
  const { ignoredFields, fieldMap = {} } = metadata

  nodeFields(node, ignoredFields).forEach(field => {
    let descriptor = fieldMap[field]
    if (descriptor === undefined) {
      descriptor = {}
      fieldMap[field] = descriptor
    }

    updateValueDescriptor(
      node.id,
      field,
      node[field],
      operation,
      descriptor,
      metadata,
      []
    )
  })
  metadata.fieldMap = fieldMap
  return metadata
}

const ignore = (metadata = initialMetadata(), set = true) => {
  metadata.ignored = set
  metadata.fieldMap = {}
  return metadata
}

const disable = (metadata = initialMetadata(), set = true) => {
  metadata.disabled = set
  return metadata
}

const addNode = (metadata, node) => updateTypeMetadata(metadata, `add`, node)
const deleteNode = (metadata, node) => updateTypeMetadata(metadata, `del`, node)
const addNodes = (metadata = initialMetadata(), nodes) =>
  nodes.reduce(addNode, metadata)

const possibleTypes = (descriptor = {}) =>
  Object.keys(descriptor).filter(type => descriptor[type].total > 0)

const isEmpty = ({ fieldMap }) =>
  Object.keys(fieldMap).every(
    field => possibleTypes(fieldMap[field]).length === 0
  )

// Even empty type may still have nodes
const hasNodes = typeMetadata => typeMetadata.total > 0

const haveEqualFields = (
  { fieldMap = {} } = {},
  { fieldMap: otherFieldMap = {} } = {}
) => {
  const fields = mergeObjectKeys(fieldMap, otherFieldMap)
  return fields.every(field =>
    descriptorsAreEqual(fieldMap[field], otherFieldMap[field])
  )
}

const initialMetadata = state => {
  return {
    typeName: undefined,
    disabled: false,
    ignored: false,
    dirty: false,
    total: 0,
    ignoredFields: undefined,
    fieldMap: {},
    ...state,
  }
}

module.exports = {
  addNode,
  addNodes,
  deleteNode,
  ignore,
  disable,
  isEmpty,
  hasNodes,
  haveEqualFields,
  initialMetadata,
}
<|endoftext|>


<commit_before>/* eslint-disable no-console */
const commando = require('discord.js-commando');
const path = require('path');
const oneLine = require('common-tags').oneLine;
const token = require('./auth.json').token;

const client = new commando.Client({
	owner: '81440962496172032',
	commandPrefix: 'c!',
	unknownCommandResponse: true
});

client.on('error', console.error)
	.on('warn', console.warn)
	.on('debug', console.log)
	.on('ready', () => {
		console.log(`Client ready; logged in as ${client.user.username}#${client.user.discriminator} (${client.user.id})`);
	})
	.on('disconnect', () => { console.warn('Disconnected!'); })
	.on('reconnect', () => { console.warn('Reconnecting...'); })
	.on('commandError', (cmd, err) => {
		if (err instanceof commando.FriendlyError) return;
		console.error(`Error in command ${cmd.groupID}:${cmd.memberName}`, err);
	})
	.on('commandBlocked', (msg, reason) => {
		console.log(oneLine`
			Command ${msg.command ? `${msg.command.groupID}:${msg.command.memberName}` : ''}
			blocked; ${reason}
		`);
	})
	.on('commandPrefixChange', (guild, prefix) => {
		console.log(oneLine`
			Prefix changed to ${prefix || 'the default'}
			${guild ? `in guild ${guild.name} (${guild.id})` : 'globally'}.
		`);
	})
	.on('commandStatusChange', (guild, command, enabled) => {
		console.log(oneLine`
			Command ${command.groupID}:${command.memberName}
			${enabled ? 'enabled' : 'disabled'}
			${guild ? `in guild ${guild.name} (${guild.id})` : 'globally'}.
		`);
	})
	.on('groupStatusChange', (guild, group, enabled) => {
		console.log(oneLine`
			Group ${group.id}
			${enabled ? 'enabled' : 'disabled'}
			${guild ? `in guild ${guild.name} (${guild.id})` : 'globally'}.
		`);
	});

client.registry
	.registerGroups([
		['info', 'Info'],
		['math', 'Math'],
		['fun', 'Fun'],
		['music', 'Music'],
		['tags', 'Tags']
	])
	.registerDefaults()
	.registerCommandsIn(path.join(__dirname, 'commands'));

client.login(token);
<commit_msg>Shoulda been in there.<commit_after>/* eslint-disable no-console */
const commando = require('discord.js-commando');
const path = require('path');
const oneLine = require('common-tags').oneLine;
const token = require('./auth.json').token;

const client = new commando.Client({
	owner: '81440962496172032',
	commandPrefix: 'c!',
	disableEveryone: true
});

client.on('error', console.error)
	.on('warn', console.warn)
	.on('debug', console.log)
	.on('ready', () => {
		console.log(`Client ready; logged in as ${client.user.username}#${client.user.discriminator} (${client.user.id})`);
	})
	.on('disconnect', () => { console.warn('Disconnected!'); })
	.on('reconnect', () => { console.warn('Reconnecting...'); })
	.on('commandError', (cmd, err) => {
		if (err instanceof commando.FriendlyError) return;
		console.error(`Error in command ${cmd.groupID}:${cmd.memberName}`, err);
	})
	.on('commandBlocked', (msg, reason) => {
		console.log(oneLine`
			Command ${msg.command ? `${msg.command.groupID}:${msg.command.memberName}` : ''}
			blocked; ${reason}
		`);
	})
	.on('commandPrefixChange', (guild, prefix) => {
		console.log(oneLine`
			Prefix changed to ${prefix || 'the default'}
			${guild ? `in guild ${guild.name} (${guild.id})` : 'globally'}.
		`);
	})
	.on('commandStatusChange', (guild, command, enabled) => {
		console.log(oneLine`
			Command ${command.groupID}:${command.memberName}
			${enabled ? 'enabled' : 'disabled'}
			${guild ? `in guild ${guild.name} (${guild.id})` : 'globally'}.
		`);
	})
	.on('groupStatusChange', (guild, group, enabled) => {
		console.log(oneLine`
			Group ${group.id}
			${enabled ? 'enabled' : 'disabled'}
			${guild ? `in guild ${guild.name} (${guild.id})` : 'globally'}.
		`);
	});

client.registry
	.registerGroups([
		['info', 'Info'],
		['math', 'Math'],
		['fun', 'Fun'],
		['music', 'Music'],
		['tags', 'Tags']
	])
	.registerDefaults()
	.registerCommandsIn(path.join(__dirname, 'commands'));

client.login(token);
<|endoftext|>


<commit_before>const { Int } = require("./utils");
const GM = require("gm");

const pos = (x, y) => ({ x: Int(x), y: Int(y) });
const dim = (x, y) => ({ width: Int(x), height: Int(y) });
const color = "(black|red|orange|yellow|green|blue|indigo|violet)";

const MarkupParser = {
  crop: {
    regex: new RegExp(`crop,(\\d+)x(\\d+),(\\d+)x(\\d+)`),
    t: (m) => ({
      block: "crop",
      instruction: {
        from: pos(m[1], m[2]),
        size: dim(m[3], m[4]),
      },
    }),
  },
  circle: {
    regex: new RegExp(`circle,(\\d+)x(\\d+),(\\d+),${color}`),
    t: (m) => ({
      block: "draw",
      id: "circle",
      instruction: {
        from: pos(m[1], m[2]),
        radius: Int(m[3]),
        color: m[4],
      },
    }),
  },
  rectangle: {
    regex: new RegExp(`rectangle,(\\d+)x(\\d+),(\\d+)x(\\d+),${color}`),
    t: (m) => ({
      block: "draw",
      id: "rectangle",
      instruction: {
        from: pos(m[1], m[2]),
        size: dim(m[3], m[4]),
        color: m[5],
      },
    }),
  },
  line: {
    regex: new RegExp(`(line|arrow|gap),(\\d+)x(\\d+),(\\d+)x(\\d+),${color}`),
    t: (m) => ({
      block: "draw",
      id: m[1],
      instruction: {
        from: pos(m[2], m[3]),
        to: pos(m[4], m[5]),
        color: m[6],
      },
    }),
  },
};

function convertMarkupToJSON(markup, infile, outfile, stroke) {
  return new Promise((resolve) => {
    GMGetSize(infile).then((size) => {
      const json = {
        sourceFile: infile,
        destinationFile: outfile,

        dimensions: size,
        finalDimensions: size,

        instructions: {},
      };

      const instructions = markup
        .trim()
        .split(";")
        .filter((x) => x);

      instructions.forEach((instruction) => {
        const result = parseInstruction(instruction);
        if (result.block === "crop") {
          json["instructions"]["crop"] = result.instruction;
          json["finalDimensions"] = result.instruction["size"];
        }
        if (result.block === "draw") {
          if (!json["instructions"]["draw"]) {
            json["instructions"]["draw"] = [];
          }
          const drawCommand = { [result.id]: result.instruction };
          json["instructions"]["draw"].push(drawCommand);
        }
      });

      if (stroke != null) {
        json.instructions.strokeWidth = stroke;
      }

      resolve(json);
    });
  });
}

function parseInstruction(instruction) {
  for (const { regex, t } of Object.values(MarkupParser)) {
    const match = regex.exec(instruction);
    if (match && match[0] === instruction) {
      return t(match);
    }
  }
  throw `Could not parse markup instruction: '${instruction}'`;
}

function GMGetSize(infile) {
  return new Promise((resolve, reject) => {
    GM(infile).size(function (err, size) {
      if (err) {
        reject(err);
      }
      resolve(size);
    });
  });
}

module.exports = convertMarkupToJSON;
<commit_msg>Do not double nest promises<commit_after>const { Int } = require("./utils");
const GM = require("gm");

const pos = (x, y) => ({ x: Int(x), y: Int(y) });
const dim = (x, y) => ({ width: Int(x), height: Int(y) });
const color = "(black|red|orange|yellow|green|blue|indigo|violet)";

const MarkupParser = {
  crop: {
    regex: new RegExp(`crop,(\\d+)x(\\d+),(\\d+)x(\\d+)`),
    t: (m) => ({
      block: "crop",
      instruction: {
        from: pos(m[1], m[2]),
        size: dim(m[3], m[4]),
      },
    }),
  },
  circle: {
    regex: new RegExp(`circle,(\\d+)x(\\d+),(\\d+),${color}`),
    t: (m) => ({
      block: "draw",
      id: "circle",
      instruction: {
        from: pos(m[1], m[2]),
        radius: Int(m[3]),
        color: m[4],
      },
    }),
  },
  rectangle: {
    regex: new RegExp(`rectangle,(\\d+)x(\\d+),(\\d+)x(\\d+),${color}`),
    t: (m) => ({
      block: "draw",
      id: "rectangle",
      instruction: {
        from: pos(m[1], m[2]),
        size: dim(m[3], m[4]),
        color: m[5],
      },
    }),
  },
  line: {
    regex: new RegExp(`(line|arrow|gap),(\\d+)x(\\d+),(\\d+)x(\\d+),${color}`),
    t: (m) => ({
      block: "draw",
      id: m[1],
      instruction: {
        from: pos(m[2], m[3]),
        to: pos(m[4], m[5]),
        color: m[6],
      },
    }),
  },
};

function convertMarkupToJSON(markup, infile, outfile, stroke) {
    return GMGetSize(infile).then((size) => {
      const json = {
        sourceFile: infile,
        destinationFile: outfile,

        dimensions: size,
        finalDimensions: size,

        instructions: {},
      };

      const instructions = markup
        .trim()
        .split(";")
        .filter((x) => x);

      instructions.forEach((instruction) => {
        const result = parseInstruction(instruction);
        if (result.block === "crop") {
          json["instructions"]["crop"] = result.instruction;
          json["finalDimensions"] = result.instruction["size"];
        }
        if (result.block === "draw") {
          if (!json["instructions"]["draw"]) {
            json["instructions"]["draw"] = [];
          }
          const drawCommand = { [result.id]: result.instruction };
          json["instructions"]["draw"].push(drawCommand);
        }
      });

      if (stroke != null) {
        json.instructions.strokeWidth = stroke;
      }

      return json;
  });
}

function parseInstruction(instruction) {
  for (const { regex, t } of Object.values(MarkupParser)) {
    const match = regex.exec(instruction);
    if (match && match[0] === instruction) {
      return t(match);
    }
  }
  throw `Could not parse markup instruction: '${instruction}'`;
}

function GMGetSize(infile) {
  return new Promise((resolve, reject) => {
    GM(infile).size(function (err, size) {
      if (err) {
        reject(err);
      }
      resolve(size);
    });
  });
}

module.exports = convertMarkupToJSON;
<|endoftext|>


<commit_before>// This is an extremely simple implementation of the 'backtracking' algorithm for
// solving boolean satisfiability problems. It contains no optimizations.

// The input consists of a boolean expression in Conjunctive Normal Form.
// This means it looks something like this:
//
// `(blue OR green) AND (green OR NOT yellow)`
//
// We encode this as an array of strings with a `-` in front for negation:
//
// `[['blue', 'green'], ['green', '-yellow']]`

// ### solve
//
// * `count` is the total number of variables.
// * `clauses` is an array of clauses.
// * `model` is a set of variable assignments.

function solve (variables, clauses, model) {
  model = model || {};

  // If every clause is satisfiable, return the model which worked.
  if (clauses.every(function (c) { return satisfiable(c, model); }))
    return model;

  // If any clause is **exactly** false, return `false`; this model will not
  // work.
  if (clauses.some(function (c) { return satisfiable(c, model) === false; }))
    return false;

  // Choose a new value to test by simply looping over the possible variables
  // and checking to see if the variable has been given a value yet.
  var choice;
  for (var i = 0; i < variables.length; i++) {
    if (model[variables[i]] === undefined) {
      choice = variables[i];
    }
  }

  // If there are no more variables to try, return false.
  if (!choice) return false;

  // Recurse into two cases. The variable we chose will need to be either
  // true or false for the expression to be satisfied.
  return solve(variables, clauses, update(model, choice, true)) ||
         solve(variables, clauses, update(model, choice, false));
}

// ### update
// Copies the model, then sets `choice` = `value` in the model, and returns it.

function update (model, choice, value) {
  var copy = {};
  for (var v in model) copy[v] = model[v];
  copy[choice] = value;
  return copy;
}

// ### satisfiable
// Determines whether a clause is satisfiable given a certain model.

function satisfiable (clause, model) {
  // If every variable is false, then the clause is false.
  if (clause.every(function (v) { return resolve(v, model) === false; }))
    return false;
  // If any variable is true, then the clause is true.
  if (clause.some(function (v) { return resolve(v, model) === true; }))
    return true;

  // Otherwise, we don't know what the clause is.
  return undefined;
}

// ### resolve
// Resolve some variable to its actual value, or undefined.

function resolve (variable, model) {
  if (variable[0] == '-') {
    var value = model[variable.slice(1)];
    return value === undefined? undefined : !value;
  } else return model[variable];
}

var phrase = [
  ['blue', 'green', '-yellow'],
  ['-blue', '-green', 'yellow'],
  ['pink', 'purple', 'green', 'blue', '-yellow']
];

var things = ['blue', 'green', 'yellow', 'pink', 'purple'];

console.log(solve(things, phrase));
<commit_msg>rming test, exporting stuff<commit_after>// This is an extremely simple implementation of the 'backtracking' algorithm for
// solving boolean satisfiability problems. It contains no optimizations.

// The input consists of a boolean expression in Conjunctive Normal Form.
// This means it looks something like this:
//
// `(blue OR green) AND (green OR NOT yellow)`
//
// We encode this as an array of strings with a `-` in front for negation:
//
// `[['blue', 'green'], ['green', '-yellow']]`

// ### solve
//
// * `count` is the total number of variables.
// * `clauses` is an array of clauses.
// * `model` is a set of variable assignments.

function solve (variables, clauses, model) {
  model = model || {};

  // If every clause is satisfiable, return the model which worked.
  if (clauses.every(function (c) { return satisfiable(c, model); }))
    return model;

  // If any clause is **exactly** false, return `false`; this model will not
  // work.
  if (clauses.some(function (c) { return satisfiable(c, model) === false; }))
    return false;

  // Choose a new value to test by simply looping over the possible variables
  // and checking to see if the variable has been given a value yet.
  var choice;
  for (var i = 0; i < variables.length; i++) {
    if (model[variables[i]] === undefined) {
      choice = variables[i];
    }
  }

  // If there are no more variables to try, return false.
  if (!choice) return false;

  // Recurse into two cases. The variable we chose will need to be either
  // true or false for the expression to be satisfied.
  return solve(variables, clauses, update(model, choice, true)) ||
         solve(variables, clauses, update(model, choice, false));
}

// ### update
// Copies the model, then sets `choice` = `value` in the model, and returns it.

function update (model, choice, value) {
  var copy = {};
  for (var v in model) copy[v] = model[v];
  copy[choice] = value;
  return copy;
}

// ### satisfiable
// Determines whether a clause is satisfiable given a certain model.

function satisfiable (clause, model) {
  // If every variable is false, then the clause is false.
  if (clause.every(function (v) { return resolve(v, model) === false; }))
    return false;
  // If any variable is true, then the clause is true.
  if (clause.some(function (v) { return resolve(v, model) === true; }))
    return true;

  // Otherwise, we don't know what the clause is.
  return undefined;
}

// ### resolve
// Resolve some variable to its actual value, or undefined.

function resolve (variable, model) {
  if (variable[0] == '-') {
    var value = model[variable.slice(1)];
    return value === undefined? undefined : !value;
  } else return model[variable];
}

exports.solve = solve;
exports.update = update;
exports.satisfiable = satisfiable;
exports.resolve = resolve;
<|endoftext|>


<commit_before>minispade.register('controllers', function() {

  RiakCsControl.ApplicationController = Ember.Controller.extend();

  RiakCsControl.CreateUserController = Ember.ObjectController.extend({
    enter: function() {
      this.transaction = this.get('store').transaction();
      this.set('content',
        this.transaction.createRecord(RiakCsControl.User, {}));
    },

    createUser: function() {
      this.transaction.commit();
      this.transaction = null;

      this.get('content').addObserver('id', this, 'showUsers');
    },

    exit: function() {
      this.transaction.rollback();
      this.transaction = null;
    },

    viewUsers: function() {
      RiakCsControl.router.send('viewUsers');
    }
  });

  RiakCsControl.UsersController = Ember.ArrayController.extend({
    persistedUsers: function() {
      return this.get('content').filterProperty('isNew', false);
    }.property('content.@each.isNew'),

    enableUser: function(user) {
      this.performUserUpdate(user, function() { user.enable(); });
    },

    disableUser: function(user) {
      this.performUserUpdate(user, function() { user.disable(); });
    },

    revokeCredentials: function(user) {
      this.performUserUpdate(user, function() { user.revoke(); });
    },

    performUserUpdate: function(user, update) {
      var transaction = RiakCsControl.get('store').transaction();

      transaction.add(user);
      update.call(user);
      transaction.commit();
    }
  });

});
<commit_msg>Fix typos.<commit_after>minispade.register('controllers', function() {

  RiakCsControl.ApplicationController = Ember.Controller.extend();

  RiakCsControl.CreateUserController = Ember.ObjectController.extend({
    enter: function() {
      this.transaction = this.get('store').transaction();
      this.set('content',
        this.transaction.createRecord(RiakCsControl.User, {}));
    },

    createUser: function() {
      this.transaction.commit();
      this.transaction = null;

      this.get('content').addObserver('id', this, 'viewUsers');
    },

    exit: function() {
      if(this.transaction) {
        this.transaction.rollback();
      }
      this.transaction = null;
    },

    viewUsers: function() {
      RiakCsControl.router.send('viewUsers');
    }
  });

  RiakCsControl.UsersController = Ember.ArrayController.extend({
    persistedUsers: function() {
      return this.get('content').filterProperty('isNew', false);
    }.property('content.@each.isNew'),

    enableUser: function(user) {
      this.performUserUpdate(user, function() { user.enable(); });
    },

    disableUser: function(user) {
      this.performUserUpdate(user, function() { user.disable(); });
    },

    revokeCredentials: function(user) {
      this.performUserUpdate(user, function() { user.revoke(); });
    },

    performUserUpdate: function(user, update) {
      var transaction = RiakCsControl.get('store').transaction();

      transaction.add(user);
      update.call(user);
      transaction.commit();
    }
  });

});
<|endoftext|>


<commit_before>$(function() {
  "use strict";

  var srcFragment = "try:";
  var $code = $("#code");
  var $play = $("#play");

  var config = {};
  location.search.substr(1).split("&").forEach(function(kv) {
    var items = kv.split("=");
    if (items[0] != "") {
      var key   = items[0];
      var value = items[1];
      if (value == "false") {
        value = false;
      } else if (value == "true" || value == undefined) {
        value = true;
      } else if (!isNaN(+value)) {
        value = +value;
      }
      config[key] = value;
    }
  });

  var cc = window.cc = new CoffeeCollider(config);
  var viewer = new WaveViewer(cc, document.getElementById("canvas"), {
    width:200, height:100, fillStyle:"#2c3e50", strokeStyle:"#f1c40f", lineWidth:2
  });
  var isPlaying = false;
  var prevCode  = null;
  var status = "pause";

  $play.on("click", function(e) {
    if (e.shiftKey) {
      console.log(cc.compiler.toString($code.val().trim()));
      return;
    }

    execute();
    if (status === "pause") {
      cc.play();
      viewer.start();
      $play.addClass("btn-danger").text("Run");
      status = "play";
      isPlaying = true;
    }
  });

  var execute = function() {
    var code = $code.val().trim();
    cc.execute(code, function(res) {
      if (res !== undefined) {
        console.log(res);
      }
    });
    prevCode = code;
  };

  $("#pause").on("click", function() {
    if (status === "play") {
      cc.pause();
      viewer.stop();
      $play.removeClass("btn-danger").text("Play");
      status = "pause";
      isPlaying = false;
    }
  });

  $("#link").on("click", function() {
    var code = $code.val().trim();
    window.location = "#" + srcFragment + encodeURIComponent(code);
  });

  $("a", "#example-list").each(function(i, a) {
    var $a = $(a);
    $a.on("click", function() {
      $.get("./documents/examples/" + $(this).attr("data-path")).then(function(res) {
        $code.val(res);
      });
      return false;
    });
  });

  var hash = decodeURIComponent(location.hash.replace(/^#/, ""));
  if (hash.indexOf(srcFragment) === 0) {
    $code.val(hash.substr(srcFragment.length));
  }

});
<commit_msg>Fixed<commit_after>$(function() {
  "use strict";

  var srcFragment = "try:";
  var $code = $("#code");
  var $play = $("#play");

  var config = {};
  location.search.substr(1).split("&").forEach(function(kv) {
    var items = kv.split("=");
    if (items[0] != "") {
      var key   = items[0];
      var value = items[1];
      if (value == "false") {
        value = false;
      } else if (value == "true" || value == undefined) {
        value = true;
      } else if (!isNaN(+value)) {
        value = +value;
      }
      config[key] = value;
    }
  });

  var cc = window.cc = new CoffeeCollider(config);
  var viewer = new WaveViewer(cc, document.getElementById("canvas"), {
    width:200, height:100, fillStyle:"#2c3e50", strokeStyle:"#f1c40f", lineWidth:2
  });
  var isPlaying = false;
  var prevCode  = null;
  var status = "pause";

  $play.on("click", function(e) {
    if (e.shiftKey) {
      console.log(cc.impl.compiler.toString($code.val().trim()));
      return;
    }

    execute();
    if (status === "pause") {
      cc.play();
      viewer.start();
      $play.addClass("btn-danger").text("Run");
      status = "play";
      isPlaying = true;
    }
  });

  var execute = function() {
    var code = $code.val().trim();
    cc.execute(code, function(res) {
      if (res !== undefined) {
        console.log(res);
      }
    });
    prevCode = code;
  };

  $("#pause").on("click", function() {
    if (status === "play") {
      cc.pause();
      viewer.stop();
      $play.removeClass("btn-danger").text("Play");
      status = "pause";
      isPlaying = false;
    }
  });

  $("#link").on("click", function() {
    var code = $code.val().trim();
    window.location = "#" + srcFragment + encodeURIComponent(code);
  });

  $("a", "#example-list").each(function(i, a) {
    var $a = $(a);
    $a.on("click", function() {
      $.get("./documents/examples/" + $(this).attr("data-path")).then(function(res) {
        $code.val(res);
      });
      return false;
    });
  });

  var hash = decodeURIComponent(location.hash.replace(/^#/, ""));
  if (hash.indexOf(srcFragment) === 0) {
    $code.val(hash.substr(srcFragment.length));
  }

});
<|endoftext|>


<commit_before> define([
    'jquery',
    'gizmo/superdesk',
    config.guiJs('livedesk', 'models/themes'),
    'tmpl!livedesk>configure/themes',
    'tmpl!livedesk>configure/embed'
], function( $, Gizmo) {
   return Gizmo.View.extend({
        tmplData: {},
        events: {
        	'[name="Theme"]': { change: 'change'}
        },
        init: function() {
            var self = this;
            if( !self.collection ) {
                self.collection = new Gizmo.Register.Themes();
            }
            self.collection
                .on('read update', self.render, self)
                .xfilter('Name,URL')
                .sync();
        },
        change: function(evt) {
        	var self = this,
        		el = self.el.find('[name="Theme"]'),
        		theme,
                data,
                blogUrl,
                idLanguage = $('[name="Language"]').val(),
                optionLanguage = $('[name="Language"] [value="'+idLanguage+'"]');
        	if(el.val() == '') {
        		$('#emebed-script').val('');
        		return;
        	}
        	for( var i = 0, count = this.collection._list.length; i < count; i++ ){
        		theme = this.collection._list[i];
        		if(theme.get('Name') === el.val()) {
        			break;
        		}
        	}
            blogUrl = self.theBlog;
            /*!
             * If the blog url doesn't have servername in it add it
             *   else replace with the frontend server.
             */
            if(blogUrl.indexOf(config.api_url) !== -1) {
                blogUrl = blogUrl.replace(config.api_url,$('[name="FrontendServer"]').val());
            } else {
                blogUrl = $('[name="FrontendServer"]').val() + blogUrl;
            }
            data = {
                'Theme': el.val(),
                'ThemePath': theme.get('URL').href.replace('\\','/').replace(config.api_url,$('[name="FrontendServer"]').val()).replace(el.val(),''),
                'TheBlog': blogUrl,
                'GuiLivedeskEmbed': $('[name="FrontendServer"]').val() + '/content/' + config.guiJs('livedesk-embed','core/require.js'),
                'ApiUrl': config.api_url,
                'FrontendServer': $('[name="FrontendServer"]').val(),
                'Language': optionLanguage.attr('data-code')
            }
        	$.tmpl('livedesk>configure/embed',data, function(e,o){
        		$('#emebed-script')
        		.val(o)
        		.focus();
        	});
        },
        addOne: function(evt, model){
            //var blogTypeView = new BlogTypeView({ _parent: this, model: model, tmplData: this.tmplData });
            //this.el.find('.Theme').append(blogTypeView.el)
        },
        addAll: function(evt, data) {
            data = (data === undefined) ? this.collection._list : data;
            for( var i = 0, count = data.length; i < count; i++ ){
                this.addOne(evt, data[i]);
            }
        },
        render: function(evt, data) {
            var self = this,
            	data = { Themes: self.collection.feed() };
            $.extend( data, self.tmplData );
            self.el.tmpl('livedesk>configure/themes', data, function(){
                //self.addAll(evt, data);
                self.el.find('[name="Theme"]').change();
            });
        },
    });
});<commit_msg>LB-652: Embed code isn't rendering well<commit_after> define([
    'jquery',
    'gizmo/superdesk',
    config.guiJs('livedesk', 'models/themes'),
    'tmpl!livedesk>configure/themes',
    'tmpl!livedesk>configure/embed'
], function( $, Gizmo) {
   return Gizmo.View.extend({
        tmplData: {},
        events: {
        	'[name="Theme"]': { change: 'change'}
        },
        init: function() {
            var self = this;
            if( !self.collection ) {
                self.collection = new Gizmo.Register.Themes();
            }
            self.collection
                .on('read update', self.render, self)
                .xfilter('Name,URL')
                .sync();
        },
        change: function(evt) {
        	var self = this,
        		el = self.el.find('[name="Theme"]'),
        		theme,
                themePath,
                themePathArray,
                data,
                blogUrl,
                idLanguage = $('[name="Language"]').val(),
                optionLanguage = $('[name="Language"] [value="'+idLanguage+'"]');
        	if(el.val() == '') {
        		$('#emebed-script').val('');
        		return;
        	}
        	for( var i = 0, count = this.collection._list.length; i < count; i++ ){
        		theme = this.collection._list[i];
        		if(theme.get('Name') === el.val()) {
        			break;
        		}
        	}
            blogUrl = self.theBlog;
            /*!
             * If the blog url doesn't have servername in it add it
             *   else replace with the frontend server.
             */
            if(blogUrl.indexOf(config.api_url) !== -1) {
                blogUrl = blogUrl.replace(config.api_url,$('[name="FrontendServer"]').val());
            } else {
                blogUrl = $('[name="FrontendServer"]').val() + blogUrl;
            }
            themePath = theme.get('URL').href.replace('\\','/').replace(config.api_url,$('[name="FrontendServer"]').val());
            themePathArray = themePath.split('/');
            themePathArray.pop()
            themePath = themePathArray.join('/');
            data = {
                'Theme': el.val(),
                'ThemePath': themePath,
                'TheBlog': blogUrl,
                'GuiLivedeskEmbed': $('[name="FrontendServer"]').val() + '/content/' + config.guiJs('livedesk-embed','core/require.js'),
                'ApiUrl': config.api_url,
                'FrontendServer': $('[name="FrontendServer"]').val(),
                'Language': optionLanguage.attr('data-code')
            }
        	$.tmpl('livedesk>configure/embed',data, function(e,o){
        		$('#emebed-script')
        		.val(o)
        		.focus();
        	});
        },
        addOne: function(evt, model){
            //var blogTypeView = new BlogTypeView({ _parent: this, model: model, tmplData: this.tmplData });
            //this.el.find('.Theme').append(blogTypeView.el)
        },
        addAll: function(evt, data) {
            data = (data === undefined) ? this.collection._list : data;
            for( var i = 0, count = data.length; i < count; i++ ){
                this.addOne(evt, data[i]);
            }
        },
        render: function(evt, data) {
            var self = this,
            	data = { Themes: self.collection.feed() };
            $.extend( data, self.tmplData );
            self.el.tmpl('livedesk>configure/themes', data, function(){
                //self.addAll(evt, data);
                self.el.find('[name="Theme"]').change();
            });
        },
    });
});<|endoftext|>


<commit_before>'use strict';

var chai = require('chai');
var expect = chai.expect;
chai.use(require('chai-things'));
var mockery = require('mockery');
var path = require('path');
var sinon = require('sinon');
var express = require('express');

var Ravel;

describe('Ravel', function() {
  beforeEach(function(done) {
    Ravel = new require('../../lib-cov/ravel')();
    Ravel.Log.setLevel('NONE');
    //mock broadcast, kvstore, authorize, authorizeWithRedirect and db.middleware, since they only get created during Ravel.start
    Ravel.broadcast = {
      emit: function(){}
    };
    Ravel.kvstore = {};
    Ravel.db = {
      middleware: function(){}
    };
    Ravel.authorize = function() {};
    Ravel.authorizeWithRedirect = function() {};

    //enable mockery
    mockery.enable({
      useCleanCache: true,
      warnOnReplace: false,
      warnOnUnregistered: false
    });
    done();
  });

  afterEach(function(done) {
    Ravel = undefined;
    mockery.disable();
    done();
  });

  describe('#resource()', function() {
    it('should allow clients to register resource modules for instantiation in Ravel.start, and assign them a base path', function(done) {
      Ravel.resource('/api/test', './resources/test');
      expect(Ravel._resourceFactories).to.have.property('/api/test');
      expect(Ravel._resourceFactories['/api/test']).to.be.a('function');
      done();
    });

    it('should throw a Ravel.ApplicationError.DuplicateEntry error when clients attempt to register multiple resource modules with the same base path', function(done) {
      try {
        Ravel.resource('/api/test', './resources/test');
        Ravel.resource('/api/test', './resources/test2');
        done(new Error('It should be impossible to register two resource modules with the same base path.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.DuplicateEntry);
        done();
      }
    });

    it('should produce a factory function which can be used to instantiate the specified resource module and perform dependency injection with specific, resource-related services', function(done) {
      var stub = function($E, $L, $KV, $EndpointBuilder, $Rest, $Broadcast, $Private, $PrivateRedirect, $MiddlewareTransaction) {
        expect($E).to.equal(Ravel.ApplicationError);
        expect($L).to.be.an('object');
        expect($L).to.have.property('trace').that.is.a('function');
        expect($L).to.have.property('verbose').that.is.a('function');
        expect($L).to.have.property('debug').that.is.a('function');
        expect($L).to.have.property('info').that.is.a('function');
        expect($L).to.have.property('warn').that.is.a('function');
        expect($L).to.have.property('error').that.is.a('function');
        expect($L).to.have.property('critical').that.is.a('function');
        expect($KV).to.be.ok;
        expect($KV).to.be.an('object');
        expect($KV).to.equal(Ravel.kvstore);
        expect($KV).to.be.ok;
        expect($EndpointBuilder).to.be.an('object');
        expect($EndpointBuilder).to.have.property('getAll').that.is.a('function');
        expect($EndpointBuilder).to.have.property('putAll').that.is.a('function');
        expect($EndpointBuilder).to.have.property('deleteAll').that.is.a('function');
        expect($EndpointBuilder).to.have.property('get').that.is.a('function');
        expect($EndpointBuilder).to.have.property('put').that.is.a('function');
        expect($EndpointBuilder).to.have.property('post').that.is.a('function');
        expect($EndpointBuilder).to.have.property('delete').that.is.a('function');
        expect($Rest).to.be.an('object');
        expect($Rest).to.have.property('buildRestResponse').that.is.a('function');
        expect($Rest).to.have.property('handleRangeGet').that.is.a('function');
        expect($Broadcast).to.equal(Ravel.broadcast);
        expect($Private).to.equal(Ravel.authorize);
        expect($PrivateRedirect).to.equal(Ravel.authorizeWithRedirect);
        expect($MiddlewareTransaction).to.equal(Ravel.db.middleware);
        done();

        return {};
      };
      Ravel.resource('/api/test', 'test');
      mockery.registerMock(path.join(Ravel.cwd, 'test'), stub);
      var app = express();
      Ravel._resourceFactories['/api/test'](app);
    });

    /*it('should facilitate the creation of GET routes via $EndpointBuilder.getAll, but not permit the use of other HTTP verbs', function(done) {
    });

    it('should facilitate the creation of GET routes via $EndpointBuilder.get, but not permit the use of other HTTP verbs', function(done) {
    });

    it('should facilitate the creation of POST routes via $EndpointBuilder.post, but not permit the use of other HTTP verbs', function(done) {
    });

    it('should facilitate the creation of PUT routes via $EndpointBuilder.put, but not permit the use of other HTTP verbs', function(done) {
    });

    it('should facilitate the creation of PUT routes via $EndpointBuilder.putAll, but not permit the use of other HTTP verbs', function(done) {
    });

    it('should facilitate the creation of GET routes via $EndpointBuilder.deleteAll, but not permit the use of other HTTP verbs', function(done) {
    });

    it('should facilitate the creation of GET routes via $EndpointBuilder.delete, but not permit the use of other HTTP verbs', function(done) {
    });

    it('should throw a Ravel.ApplicationError.DuplicateEntry when a method of $EndpointBuilder is used twice', function(done) {
    });*/
  });
});
<commit_msg>Updating test TODOs in test-resource<commit_after>'use strict';

var chai = require('chai');
var expect = chai.expect;
chai.use(require('chai-things'));
var mockery = require('mockery');
var path = require('path');
var sinon = require('sinon');
var express = require('express');

var Ravel;

describe('Ravel', function() {
  beforeEach(function(done) {
    Ravel = new require('../../lib-cov/ravel')();
    Ravel.Log.setLevel('NONE');
    //mock broadcast, kvstore, authorize, authorizeWithRedirect and db.middleware, since they only get created during Ravel.start
    Ravel.broadcast = {
      emit: function(){}
    };
    Ravel.kvstore = {};
    Ravel.db = {
      middleware: function(){}
    };
    Ravel.authorize = function() {};
    Ravel.authorizeWithRedirect = function() {};

    //enable mockery
    mockery.enable({
      useCleanCache: true,
      warnOnReplace: false,
      warnOnUnregistered: false
    });
    done();
  });

  afterEach(function(done) {
    Ravel = undefined;
    mockery.disable();
    done();
  });

  describe('#resource()', function() {
    it('should allow clients to register resource modules for instantiation in Ravel.start, and assign them a base path', function(done) {
      Ravel.resource('/api/test', './resources/test');
      expect(Ravel._resourceFactories).to.have.property('/api/test');
      expect(Ravel._resourceFactories['/api/test']).to.be.a('function');
      done();
    });

    it('should throw a Ravel.ApplicationError.DuplicateEntry error when clients attempt to register multiple resource modules with the same base path', function(done) {
      try {
        Ravel.resource('/api/test', './resources/test');
        Ravel.resource('/api/test', './resources/test2');
        done(new Error('It should be impossible to register two resource modules with the same base path.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.DuplicateEntry);
        done();
      }
    });

    it('should produce a factory function which can be used to instantiate the specified resource module and perform dependency injection with specific, resource-related services', function(done) {
      var stub = function($E, $L, $KV, $EndpointBuilder, $Rest, $Broadcast, $Private, $PrivateRedirect, $MiddlewareTransaction) {
        expect($E).to.equal(Ravel.ApplicationError);
        expect($L).to.be.an('object');
        expect($L).to.have.property('trace').that.is.a('function');
        expect($L).to.have.property('verbose').that.is.a('function');
        expect($L).to.have.property('debug').that.is.a('function');
        expect($L).to.have.property('info').that.is.a('function');
        expect($L).to.have.property('warn').that.is.a('function');
        expect($L).to.have.property('error').that.is.a('function');
        expect($L).to.have.property('critical').that.is.a('function');
        expect($KV).to.be.ok;
        expect($KV).to.be.an('object');
        expect($KV).to.equal(Ravel.kvstore);
        expect($KV).to.be.ok;
        expect($EndpointBuilder).to.be.an('object');
        expect($EndpointBuilder).to.have.property('getAll').that.is.a('function');
        expect($EndpointBuilder).to.have.property('putAll').that.is.a('function');
        expect($EndpointBuilder).to.have.property('deleteAll').that.is.a('function');
        expect($EndpointBuilder).to.have.property('get').that.is.a('function');
        expect($EndpointBuilder).to.have.property('put').that.is.a('function');
        expect($EndpointBuilder).to.have.property('post').that.is.a('function');
        expect($EndpointBuilder).to.have.property('delete').that.is.a('function');
        expect($Rest).to.be.an('object');
        expect($Rest).to.have.property('buildRestResponse').that.is.a('function');
        expect($Rest).to.have.property('handleRangeGet').that.is.a('function');
        expect($Broadcast).to.equal(Ravel.broadcast);
        expect($Private).to.equal(Ravel.authorize);
        expect($PrivateRedirect).to.equal(Ravel.authorizeWithRedirect);
        expect($MiddlewareTransaction).to.equal(Ravel.db.middleware);
        done();

        return {};
      };
      Ravel.resource('/api/test', 'test');
      mockery.registerMock(path.join(Ravel.cwd, 'test'), stub);
      var app = express();
      Ravel._resourceFactories['/api/test'](app);
    });

    /*it('should facilitate the creation of GET routes via $EndpointBuilder.getAll', function(done) {
    });

    it('should facilitate the creation of GET routes via $EndpointBuilder.get', function(done) {
    });

    it('should facilitate the creation of POST routes via $EndpointBuilder.post', function(done) {
    });

    it('should facilitate the creation of PUT routes via $EndpointBuilder.put', function(done) {
    });

    it('should facilitate the creation of PUT routes via $EndpointBuilder.putAll', function(done) {
    });

    it('should facilitate the creation of GET routes via $EndpointBuilder.deleteAll', function(done) {
    });

    it('should facilitate the creation of GET routes via $EndpointBuilder.delete', function(done) {
    });

    it('should throw a Ravel.ApplicationError.DuplicateEntry when a method of $EndpointBuilder is used twice', function(done) {
    });*/
  });
});
<|endoftext|>


<commit_before>const Command = require("./Ship/Command.js");

const START_WELCOME_MESSAGE = `
Hello, I am SailBot, the helpbot for the severnbronies chat.
For a list of my commands try /help.
You can message me either in private or in @severnbronies
`;

function notImplemented(paramaters,message){
  Command.basicReply("Sorry but this method is not yet implemented.",message);
}

module.exports = function(commandSource){
  /*Commands TODO:
    nextMeet
    votes
  */

  //Start command
  new Command("start",commandSource)
    .addHandler(null,null,function(paramaters,message){
      Command.basicReply(START_WELCOME_MESSAGE,message);
    },Command.nArgs(0,1));

  //Upcoming command
  new Command("upcoming",commandSource)
    .addHandler("","Posts the next upcoming meet",notImplemented,Command.nArgs(0))
    .addHandler("_location(s)_","Filters the meets to the given locations",notImplemented,Command.atleastArgs(1));

  //Easter egg
  new Command("⬆⬆⬇⬇⬅➡⬅➡ba",commandSource)
    .addHandler(null,null,function(paramaters,message){
      message.connection.sendPhoto({
        chat_id:message.chat.id,
        photo:"https://derpicdn.net/img/view/2015/7/11/934995.png",
        caption:"Do ships need sails?"
      });
    },Command.nArgs(0));
};
<commit_msg>WIP Add upcoming command<commit_after>const Command = require("./Ship/Command.js");
const Meets = require("./DataAccess/Meets.js");

const START_WELCOME_MESSAGE = `
Hello, I am SailBot, the helpbot for the severnbronies chat.
For a list of my commands try /help.
You can message me either in private or in @severnbronies
`;

function notImplemented(paramaters,message){
  Command.basicReply("Sorry but this method is not yet implemented.",message);
}

module.exports = function(commandSource){
  /*Commands TODO:
    nextMeet
    votes
  */

  //Start command
  new Command("start",commandSource)
    .addHandler(null,null,function(paramaters,message){
      Command.basicReply(START_WELCOME_MESSAGE,message);
    },Command.nArgs(0,1));

  //Upcoming command
  new Command("upcoming",commandSource)
    .addHandler("","Posts the next upcoming meet",notImplemented,Command.nArgs(0))
    .addHandler("_location(s)_","Filters the meets to the given locations",notImplemented,Command.atleastArgs(1));

  //Easter egg
  new Command("⬆⬆⬇⬇⬅➡⬅➡ba",commandSource)
    .addHandler(null,null,function(paramaters,message){
      message.connection.sendPhoto({
        chat_id:message.chat.id,
        photo:"https://derpicdn.net/img/view/2015/7/11/934995.png",
        caption:"Do ships need sails?"
      });
    },Command.nArgs(0));
};
<|endoftext|>