Socket
Socket
Sign inDemoInstall

sql2json

Package Overview
Dependencies
Maintainers
1
Versions
61
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sql2json - npm Package Compare versions

Comparing version 2.0.2 to 2.0.3

.idea/encodings.xml

6

CHANGELOG.md

@@ -0,1 +1,7 @@

# 2.0.3
- Add support for `NOT LIKE` clauses
- Add support for `DOT` tokens
- Update `sql-parser` - now `rw-sql-parser`
- Add tests for `WHERE x LIKE y` clauses containing string wildcards
# 2.0.2

@@ -2,0 +8,0 @@ - Table names not surrounded by quotes now support slashes in them

224

lib/json2sql.js

@@ -1,2 +0,3 @@

function json2sql() {}
function json2sql() {
}

@@ -85,3 +86,8 @@

case 'function':
responses.push(parseFunction(node));
if (select[i - 1] && select[i - 1].type === 'dot') {
const functionObject = responses.pop();
responses.push(`${functionObject}${parseFunction(node)}`);
} else {
responses.push(parseFunction(node));
}
break;

@@ -94,2 +100,6 @@ case 'number':

break;
case 'dot':
const literalValue = responses.pop();
responses.push(`${literalValue}.`);
break;
case 'distinct':

@@ -110,99 +120,141 @@ responses.push(`DISTINCT ${parseSelect(node.arguments)}`);

const parseNodeWhere = (node) => {
let args = [];
switch (node.type) {
case 'literal':
case 'number':
return node.value;
case 'string':
return `'${node.value}'`;
case 'operator':
return `${parseNodeWhere(node.left)} ${node.value} ${parseNodeWhere(node.right)}`;
case 'conditional':
return `${parseNodeWhere(node.left)} ${node.value} ${parseNodeWhere(node.right)}`;
case 'bracket':
return `(${parseNodeWhere(node.value)})`;
case 'in':
args = [];
if (node.arguments) {
for (let i = 0, length = node.arguments.length; i < length; i++) {
args.push(parseNodeWhere(node.arguments[i]));
}
const parseWhere = (where) => {
const responses = [];
if (where) {
if (!Array.isArray(where)) {
where = [where];
}
for (let i = 0, length = where.length; i < length; i++) {
let args = [];
const node = where[i];
switch (node.type) {
case 'literal':
case 'number':
responses.push(node.value);
break;
case 'string':
responses.push(`'${node.value}'`);
break;
case 'operator':
responses.push(`${parseWhere(node.left)} ${node.value} ${parseWhere(node.right)}`);
break;
case 'conditional':
responses.push(`${parseWhere(node.left)} ${node.value} ${parseWhere(node.right)}`);
break;
case 'bracket':
responses.push(`(${parseWhere(node.value)})`);
break;
case 'in':
args = [];
if (node.arguments) {
for (let i = 0, length = node.arguments.length; i < length; i++) {
args.push(parseWhere(node.arguments[i]));
}
}
responses.push(`${node.value} IN (${args.join(', ')})`);
break;
case 'between':
args = [];
if (node.arguments) {
for (let i = 0, length = node.arguments.length; i < length; i++) {
args.push(parseWhere(node.arguments[i]));
}
}
responses.push(`${node.value} BETWEEN ${parseWhere(node.arguments[0])} AND ${parseWhere(node.arguments[1])}`);
break;
case 'function':
args = [];
if (node.arguments) {
for (let i = 0, length = node.arguments.length; i < length; i++) {
args.push(parseWhere(node.arguments[i]));
}
}
responses.push(`${node.value}(${args.join(', ')})`);
break;
case 'dot':
responses.push('.');
break;
default:
responses.push(node.value);
break;
}
return `${node.value} IN (${args.join(', ')})`;
case 'between':
args = [];
if (node.arguments) {
for (let i = 0, length = node.arguments.length; i < length; i++) {
args.push(parseNodeWhere(node.arguments[i]));
}
}
return `${node.value} BETWEEN ${parseNodeWhere(node.arguments[0])} AND ${parseNodeWhere(node.arguments[1])}`;
case 'function':
args = [];
if (node.arguments) {
for (let i = 0, length = node.arguments.length; i < length; i++) {
args.push(parseNodeWhere(node.arguments[i]));
}
}
return `${node.value}(${args.join(', ')})`;
default:
return node.value;
}
}
return responses.join('');
};
const parseWhere = (node) => {
if (node) {
return `WHERE ${parseNodeWhere(node)}`;
const parseOrderBy = (orderBy) => {
if (!orderBy) {
return '';
}
return '';
};
const parseOrderBy = (orderBy) => {
if (orderBy) {
const responses = [];
for (let i = 0, length = orderBy.length; i < length; i++) {
switch (orderBy[i].type) {
case 'function':
responses.push(`${parseFunction(orderBy[i])}${orderBy[i].direction ? ` ${orderBy[i].direction}` : ''}`);
break;
case 'literal':
responses.push(`${orderBy[i].value}${orderBy[i].direction ? ` ${orderBy[i].direction}` : ''}`);
break;
default:
responses.push(`${orderBy[i].value}${orderBy[i].direction ? ` ${orderBy[i].direction}` : ''}`);
}
const responses = [];
for (let i = 0, length = orderBy.length; i < length; i++) {
const node = orderBy[i];
switch (node.type) {
case 'dot':
const literalValue = responses.pop();
responses.push(`${literalValue}.`);
break;
case 'function':
const ascDesc = `${node.direction ? ` ${node.direction}` : ''}`;
if (orderBy[i - 1] && orderBy[i - 1].type === 'dot') {
const functionObject = responses.pop();
responses.push(`${functionObject}${parseFunction(node)}${ascDesc}`);
} else {
responses.push(`${parseFunction(node)}${ascDesc}`);
}
break;
case 'literal':
const directionString = `${node.direction ? ` ${node.direction}` : ''}`;
if (orderBy[i - 1] && orderBy[i - 1].type === 'dot') {
responses.push(`${responses.pop()}${node.value}${directionString}`);
} else {
responses.push(`${node.value}${directionString}`);
}
break;
default:
responses.push(`${node.value}${node.direction ? ` ${node.direction}` : ''}`);
}
return `ORDER BY ${responses.join(', ')}`;
}
return '';
return `ORDER BY ${responses.join(', ')}`;
};
const parseGroupBy = (groupBy) => {
if (!groupBy) {
return '';
}
const parseGroupBy = (group) => {
if (group) {
const result = [];
for (let i = 0, length = group.length; i < length; i++) {
const node = group[i];
switch (node.type) {
const result = [];
for (let i = 0, length = groupBy.length; i < length; i++) {
const node = groupBy[i];
switch (node.type) {
case 'literal':
case 'literal':
if (groupBy[i - 1] && groupBy[i - 1].type === 'dot') {
result.push(`${result.pop()}${node.value}`);
} else {
result.push(`${node.value}`);
break;
case 'number':
result.push(`${node.value}`);
break;
case 'function':
}
break;
case 'number':
result.push(`${node.value}`);
break;
case 'dot':
const literalValue = result.pop();
result.push(`${literalValue}.`);
break;
case 'function':
if (groupBy[i - 1] && groupBy[i - 1].type === 'dot') {
const functionObject = result.pop();
result.push(`${functionObject}${parseFunction(node)}`);
} else {
result.push(parseFunction(node));
break;
default:
break;
}
}
break;
default:
break;
}
return `GROUP BY ${result.join(', ')}`;
}
return '';
return `GROUP BY ${result.join(', ')}`;
};

@@ -215,10 +267,8 @@

if (!data.delete) {
return `SELECT ${parseSelect(data.select)} FROM ${data.from}${data.where ? ` ${parseWhere(data.where)}` : ''}${data.group ? ` ${parseGroupBy(data.group)}` : ''}${data.orderBy ? ` ${parseOrderBy(data.orderBy)}` : ''}${data.limit ? ` LIMIT ${data.limit}` : ''}${data.offset ? ` OFFSET ${data.offset}` : ''}`.trim();
return `SELECT ${parseSelect(data.select)} FROM ${data.from}${data.where ? ` WHERE ${parseWhere(data.where)}` : ''}${data.group ? ` ${parseGroupBy(data.group)}` : ''}${data.orderBy ? ` ${parseOrderBy(data.orderBy)}` : ''}${data.limit ? ` LIMIT ${data.limit}` : ''}${data.offset ? ` OFFSET ${data.offset}` : ''}`.trim();
} else {
return `DELETE FROM ${data.from}${data.where ? ` ${parseWhere(data.where)}` : ''}${data.group ? ` ${parseGroupBy(data.group)}` : ''}${data.orderBy ? ` ${parseOrderBy(data.orderBy)}` : ''}${data.limit ? ` LIMIT ${data.limit}` : ''}${data.offset ? ` OFFSET ${data.offset}` : ''}`.trim();
return `DELETE FROM ${data.from}${data.where ? ` WHERE ${parseWhere(data.where)}` : ''}${data.group ? ` ${parseGroupBy(data.group)}` : ''}${data.orderBy ? ` ${parseOrderBy(data.orderBy)}` : ''}${data.limit ? ` LIMIT ${data.limit}` : ''}${data.offset ? ` OFFSET ${data.offset}` : ''}`.trim();
}
};
json2sql.parseNodeWhere = parseNodeWhere;
json2sql.parseFunction = parseFunction;
module.exports = json2sql;

@@ -1,2 +0,2 @@

const lexer = require('sql-parser').lexer;
const lexer = require('rw-sql-parser').lexer;

@@ -144,6 +144,27 @@ const between = /^between$/gi;

sql2json.prototype.parseCast = function (literal) {
while (this.hasNext()) {
const token = this.next();
switch (obtainType(token, this.lookAhead())) {
case 'LITERAL':
case 'FUNCTION':
case 'DBLSTRING':
return {
type: 'cast',
literal: literal,
target: token[1]
};
break;
default:
throw new Error(`Invalid type ${tokenType} for cast target`)
}
}
};
sql2json.prototype.parseSelectExperimental = function () {
this.parsed.select = [];
let lastParen = false;
let prependSpace = false;
while (this.hasNext()) {

@@ -166,9 +187,9 @@ const token = this.next();

}
if (!lastParen && token[1] !== '(' && token[1] !== ')' && token[1] !== ',' && token[1] !== '.' && token[1] !== '*') {
if (!prependSpace && token[1] !== '(' && token[1] !== ')' && token[1] !== ',' && token[1] !== '.' && token[1] !== '*') {
el.value += ' ' + token[1];
} else {
if (token[1] === '(') {
lastParen = true;
if (token[1] === '(' || token[1] === '.') {
prependSpace = true;
} else {
lastParen = false;
prependSpace = false;
}

@@ -251,2 +272,7 @@ el.value += token[1];

break;
case 'DOT':
this.parsed.select.push({
type: 'dot'
});
break;
case 'AS':

@@ -471,2 +497,3 @@ containAs = true;

sql2json.prototype.parseWhere = function () {
const nodeTypeStopList = ['in', 'between', 'operator', 'operatorPlaceholder', 'conditional', 'bracket'];
const stack = [];

@@ -494,6 +521,25 @@ let operator = null;

break;
case 'DOT':
stack.push({
type: 'dot'
});
break;
case 'RIGHT_PAREN':
if (operator && stack.length >= 2) {
const right = stack.pop();
const left = stack.pop();
const right = [];
const left = [];
while (stack.length && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
right.unshift(stack.pop());
}
// unstack operatorPlaceholder
stack.pop();
left.unshift(stack.pop());
while (stack.length && stack[stack.length - 1].type !== left[0].type && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
left.unshift(stack.pop());
}
stack.push({

@@ -508,4 +554,16 @@ type: 'operator',

if (stack.length >= 2 && conditional) {
const right = stack.pop();
const left = stack.pop();
const right = [];
const left = [];
right.unshift(stack.pop());
while (stack.length && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
right.unshift(stack.pop());
}
left.unshift(stack.pop());
while (stack.length && stack[stack.length - 1].type !== left[0].type && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
left.unshift(stack.pop());
}
stack.push({

@@ -522,2 +580,6 @@ type: 'conditional',

break;
case 'CAST':
const literal = stack.pop().value;
stack.push(this.parseCast(literal));
break;
case 'LITERAL':

@@ -549,2 +611,6 @@ case 'BOOLEAN':

case 'OPERATOR':
stack.push({
type: 'operatorPlaceholder',
value: token[1]
});
operator = token[1];

@@ -566,4 +632,21 @@ break;

if (operator && stack.length >= 2) {
const right = stack.pop();
const left = stack.pop();
const right = [];
const left = [];
right.unshift(stack.pop());
while (stack.length && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
right.unshift(stack.pop());
}
// unstack operatorPlaceholder
stack.pop();
left.unshift(stack.pop());
while (stack.length && stack[stack.length - 1].type !== left[0].type && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
left.unshift(stack.pop());
}
stack.push({

@@ -578,4 +661,17 @@ type: 'operator',

if (stack.length >= 2 && conditional) {
const right = stack.pop();
const left = stack.pop();
const right = [];
const left = [];
right.unshift(stack.pop());
while (stack.length && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
right.unshift(stack.pop());
}
left.unshift(stack.pop());
while (stack.length && stack[stack.length - 1].type !== left[0].type && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
left.unshift(stack.pop());
}
stack.push({

@@ -594,4 +690,18 @@ type: 'conditional',

if (stack.length >= 2 && operator) {
const right = stack.pop();
const left = stack.pop();
const right = [];
const left = [];
while (stack.length && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
right.unshift(stack.pop());
}
// unstack operatorPlaceholder
stack.pop();
left.unshift(stack.pop());
while (stack.length && stack[stack.length - 1].type !== left[0].type && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
left.unshift(stack.pop());
}
stack.push({

@@ -606,4 +716,17 @@ type: 'operator',

if (stack.length >= 2 && conditional) {
const right = stack.pop();
const left = stack.pop();
const right = [];
const left = [];
right.unshift(stack.pop());
while (stack.length && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
right.unshift(stack.pop());
}
left.unshift(stack.pop());
while (stack.length && stack[stack.length - 1].type !== left[0].type && !nodeTypeStopList.includes(stack[stack.length - 1].type)) {
left.unshift(stack.pop());
}
stack.push({

@@ -618,3 +741,3 @@ type: 'conditional',

}
this.parsed.where = stack.pop();
this.parsed.where = stack;
this.index--;

@@ -626,2 +749,3 @@ return;

};
sql2json.prototype.parseWhereExperimental = function () {

@@ -702,2 +826,7 @@ const stack = [];

break;
case 'DOT':
stack.push({
type: 'dot'
});
break;
case 'NUMBER':

@@ -823,2 +952,7 @@ stack.push({

break;
case 'DOT':
stack.push({
type: 'dot'
});
break;
case 'FUNCTION':

@@ -825,0 +959,0 @@ stack.push(this.parseFunction(token[1]));

{
"name": "sql2json",
"version": "2.0.2",
"version": "2.0.3",
"description": "PostgreSQL to/from JSON conversion lib",

@@ -31,3 +31,3 @@ "main": "index.js",

"dependencies": {
"sql-parser": "git+http://github.com/rrequero/sql-parser.git#master"
"rw-sql-parser": "1.0.1"
},

@@ -34,0 +34,0 @@ "devDependencies": {

@@ -7,3 +7,3 @@ const assert = require('assert');

describe('JSON to SQL - Delete', () => {
it('basic delete', () => {
it('basic DELETE', () => {
const data = {

@@ -18,3 +18,3 @@ delete: true,

it('with where', () => {
it('With DELETE with WHERE', () => {
const data = {

@@ -40,2 +40,31 @@ delete: true,

});
it('With DELETE with WHERE and table name on condition', () => {
const data = {
delete: true,
from: 'tablename',
where: [
{
type: 'operator',
left: [{
type: 'literal',
value: 'tablename'
},
{
type: 'dot'
}, {
value: 'id',
type: 'literal'
}],
value: '>',
right: [{
value: 2,
type: 'number'
}]
}]
};
const response = 'DELETE FROM tablename WHERE tablename.id > 2';
Json2sql.toSQL(data).should.deepEqual(response);
});
});

@@ -96,3 +96,3 @@ const assert = require('assert');

it('Group with function', () => {
it('With GROUP BY with function as value', () => {
const data = {

@@ -124,2 +124,80 @@ select: [{

it('With GROUP BY with function call on column value', () => {
const data = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
group: [{
type: 'literal',
alias: null,
value: 'col1'
}, {
type: 'dot',
}, {
value: 'ST_GeoHash',
type: 'function',
alias: null,
arguments: [
{
value: 'the_geom_point',
type: 'literal',
alias: null
}, {
value: '8',
type: 'number',
alias: null
}
]
}]
};
const response = 'SELECT * FROM tablename GROUP BY col1.ST_GeoHash(the_geom_point,8)';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('Group with BY with function call on column value with table name', () => {
const data = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
group: [{
type: 'literal',
alias: null,
value: 'tablename'
}, {
type: 'dot',
}, {
type: 'literal',
alias: null,
value: 'col1'
}, {
type: 'dot',
}, {
value: 'ST_GeoHash',
type: 'function',
alias: null,
arguments: [
{
value: 'the_geom_point',
type: 'literal',
alias: null
}, {
value: '8',
type: 'number',
alias: null
}
]
}]
};
const response = 'SELECT * FROM tablename GROUP BY tablename.col1.ST_GeoHash(the_geom_point,8)';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('Group with function with column name (double quotes) and constant as arguments', () => {

@@ -126,0 +204,0 @@ const data = {

@@ -1,2 +0,1 @@

const assert = require('assert');
const Json2sql = require('../../index').json2sql;

@@ -7,3 +6,3 @@ require('should');

describe('JSON to SQL - OrderBy', () => {
it('SQL with orderby', () => {
it('SQL with ORDER BY', () => {
const data = {

@@ -27,3 +26,3 @@ select: [{

it('SQL with number of name of columns', () => {
it('SQL with ORDER BY with number of name of columns', () => {
const data = {

@@ -48,3 +47,3 @@ select: [{

it('SQL with orderby with double quotes (literal)', () => {
it('SQL with ORDER BY with double quotes (literal)', () => {
const data = {

@@ -70,3 +69,3 @@ select: [{

it('SQL with order by with quotes', () => {
it('SQL with ORDER BY with quotes', () => {
const data = {

@@ -91,3 +90,3 @@ select: [{

it('SQL with orderby and direction', () => {
it('SQL with ORDER BY and direction', () => {
const data = {

@@ -110,3 +109,3 @@ select: [{

it('SQL with several orderby and direction', () => {
it('SQL with several ORDER BY and direction', () => {
const data = {

@@ -132,3 +131,3 @@ select: [{

it('SQL with several orderby and direction 2', () => {
it('SQL with several ORDER BY and direction 2', () => {
const data = {

@@ -154,3 +153,3 @@ select: [{

it('SQL with order by and function', () => {
it('With ORDER BY with function as value', () => {
const data = {

@@ -164,10 +163,12 @@ select: [{

orderBy: [{
value: 'avg',
type: 'function',
direction: null,
value: 'avg',
alias: null,
arguments: [{
type: 'literal',
value: 'name'
}]
arguments: [
{
value: 'name',
type: 'literal',
alias: null
}
]
}]

@@ -180,3 +181,3 @@ };

it('SQL with order by and several functions', () => {
it('With ORDER BY with function as value with direction', () => {
const data = {

@@ -190,4 +191,31 @@ select: [{

orderBy: [{
value: 'avg',
type: 'function',
alias: null,
direction: 'asc',
arguments: [
{
value: 'name',
type: 'literal',
alias: null
}
]
}]
};
const response = 'SELECT * FROM tablename ORDER BY avg(name) asc';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('With ORDER BY and several functions', () => {
const data = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
orderBy: [{
type: 'function',
direction: 'asc',
value: 'avg',

@@ -214,2 +242,140 @@ alias: null,

});
it('With ORDER BY with function call on value', () => {
const data = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
orderBy: [
{
type: 'literal',
alias: null,
value: 'col1'
}, {
type: 'dot',
}, {
value: 'avg',
type: 'function',
alias: null,
arguments: [
{
value: 'name',
type: 'literal',
alias: null
}
]
}]
};
const response = 'SELECT * FROM tablename ORDER BY col1.avg(name)';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('With ORDER BY with function call and column name', () => {
const data = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
orderBy: [{
value: 'avg',
type: 'function',
alias: null,
arguments: [
{
value: 'name',
type: 'literal',
alias: null
}
]
}, {
value: 'name',
type: 'literal',
alias: null,
direction: 'asc'
}]
};
const response = 'SELECT * FROM tablename ORDER BY avg(name), name asc';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('With ORDER BY with function call and column name with table', () => {
const data = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
orderBy: [{
value: 'avg',
type: 'function',
alias: null,
arguments: [
{
value: 'name',
type: 'literal',
alias: null
}
]
}, {
value: 'tablename',
type: 'literal'
}, {
type: 'dot'
}, {
value: 'name',
type: 'literal',
alias: null,
direction: 'asc'
}]
};
const response = 'SELECT * FROM tablename ORDER BY avg(name), tablename.name asc';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('With ORDER BY with function call on column name with table name', () => {
const data = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
orderBy: [{
value: 'tablename',
type: 'literal'
}, {
type: 'dot'
}, {
value: 'name',
type: 'literal',
alias: null
}, {
type: 'dot'
}, {
value: 'avg',
type: 'function',
alias: null,
direction: 'asc',
arguments: [
{
value: 'name',
type: 'literal',
alias: null
}
]
}]
};
const response = 'SELECT * FROM tablename ORDER BY tablename.name.avg(name) asc';
Json2sql.toSQL(data).should.deepEqual(response);
});
});

@@ -51,9 +51,16 @@ const assert = require('assert');

it('SQL with function shape', () => {
it('SQL with function call on column', () => {
const data = {
select: [{
value: 'Shape.STLength',
type: 'literal',
alias: null,
type: 'function'
value: 'Shape'
}, {
type: 'dot',
}, {
type: 'function',
alias: null,
value: 'STLength',
arguments: []
}, {
value: 'x',

@@ -70,2 +77,22 @@ alias: null,

it('SQL with function call as value', () => {
const data = {
select: [
{
type: 'function',
alias: null,
value: 'STLength',
arguments: []
}, {
value: 'x',
alias: null,
type: 'literal'
}],
from: 'tablename'
};
const response = 'SELECT STLength(), x FROM tablename';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('SQL with static columns', () => {

@@ -203,3 +230,3 @@ const data = {

it('SQL with gee function', () => {
it('SQL with GEE function', () => {
const data = {

@@ -219,2 +246,29 @@ select: [{

it('SQL with GEE function with arguments', () => {
const data = {
select: [{
value: 'ST_Intersects',
alias: null,
type: 'function',
arguments: [
{
value: 'the_geom',
alias: null,
type: 'literal'
}, {
value: '{}',
alias: null,
type: 'string'
}
]
}],
from: 'tablename'
};
const response = 'SELECT ST_Intersects(the_geom,\'{}\') FROM tablename';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('SQL with distinct', () => {

@@ -221,0 +275,0 @@ const data = {

@@ -121,2 +121,44 @@ const assert = require('assert');

it('With WHERE with function call on value', () => {
const data = {
select: [{
value: 'col1',
alias: null,
type: 'literal'
}, {
value: 'col2',
alias: null,
type: 'literal'
}],
from: 'tablename',
where: [
{
type: 'literal',
alias: null,
value: 'col1'
}, {
type: 'dot',
}, {
value: 'ST_Intersects',
alias: null,
type: 'function',
arguments: [
{
value: 'the_geom',
alias: null,
type: 'literal'
}, {
value: '{}',
alias: null,
type: 'string'
}
]
}]
};
const response = 'SELECT col1, col2 FROM tablename WHERE col1.ST_Intersects(the_geom, \'{}\')';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('With and', () => {

@@ -297,3 +339,3 @@ const data = {

it('With betweens', () => {
it('With WHERE with BETWEEN', () => {
const data = {

@@ -323,3 +365,3 @@ select: [{

it('With equality', () => {
it('With WHERE with equality', () => {
const data = {

@@ -350,3 +392,3 @@ select: [{

it('With equality with single quotes', () => {
it('With WHERE with equality and single quotes', () => {
const data = {

@@ -377,3 +419,3 @@ select: [{

it('With cast', () => {
it('With WHERE with cast', () => {
const data = {

@@ -404,3 +446,3 @@ select: [{

it('With cast', () => {
it('With WHERE x LIKE', () => {
const data = {

@@ -416,9 +458,9 @@ select: [{

left: {
value: 'day::int',
value: 'country_iso',
type: 'literal'
},
value: '>',
value: 'LIKE',
right: {
value: 2,
type: 'number'
value: 'BRA',
type: 'string'
}

@@ -428,7 +470,8 @@ }

const response = 'SELECT * FROM tablename WHERE day::int > 2';
const response = 'SELECT * FROM tablename WHERE country_iso LIKE \'BRA\'';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('Where with false name column', () => {
it('With WHERE x NOT LIKE', () => {
const data = {

@@ -444,2 +487,54 @@ select: [{

left: {
value: 'country_iso',
type: 'literal'
},
value: 'NOT LIKE',
right: {
value: 'BRA',
type: 'string'
}
}
};
const response = 'SELECT * FROM tablename WHERE country_iso NOT LIKE \'BRA\'';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('With WHERE like and string wildcard', () => {
const data = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
where: {
type: 'operator',
left: {
value: 'country_iso',
type: 'literal'
},
value: 'LIKE',
right: {
value: '%BRA%',
type: 'string'
}
}
};
const response = 'SELECT * FROM tablename WHERE country_iso LIKE \'%BRA%\'';
Json2sql.toSQL(data).should.deepEqual(response);
});
it('With WHERE with false name column', () => {
const data = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
where: {
type: 'operator',
left: {
value: 'false',

@@ -460,3 +555,2 @@ type: 'literal'

});
});

@@ -16,19 +16,19 @@ const assert = require('assert');

where: {
where: [{
type: 'conditional',
value: 'and',
left: {
left: [{
type: 'operator',
left: {
left: [{
value: 'a',
type: 'literal'
},
}],
value: '>',
right: {
right: [{
value: 2,
type: 'number'
}
},
right: {
}]
}],
right: [{
type: 'bracket',

@@ -38,29 +38,29 @@ value: {

value: 'or',
left: {
left: [{
type: 'operator',
left: {
left: [{
value: 'c',
type: 'literal'
},
}],
value: '>',
right: {
right: [{
value: 2,
type: 'number'
}
},
right: {
}]
}],
right: [{
type: 'operator',
left: {
left: [{
value: 'c',
type: 'literal'
},
}],
value: '<',
right: {
right: [{
value: 0,
type: 'number'
}
}
}]
}]
}
}
}
}]
}]
};

@@ -82,6 +82,6 @@

where: {
where: [{
type: 'conditional',
value: 'and',
left: {
left: [{
type: 'bracket',

@@ -91,29 +91,29 @@ value: {

value: 'or',
left: {
left: [{
type: 'operator',
left: {
left: [{
value: 'a',
type: 'literal'
},
}] ,
value: '>',
right: {
right: [{
value: 2,
type: 'number'
}
},
right: {
}]
}],
right: [{
type: 'operator',
left: {
left: [{
value: 'c',
type: 'literal'
},
}],
value: '<',
right: {
right: [{
value: 1,
type: 'number'
}
}
}]
}]
}
},
right: {
}],
right: [{
type: 'bracket',

@@ -123,29 +123,29 @@ value: {

value: 'or',
left: {
left: [{
type: 'operator',
left: {
left: [{
value: 'c',
type: 'literal'
},
}],
value: '>',
right: {
right: [{
value: 2,
type: 'number'
}
},
right: {
}]
}],
right: [{
type: 'operator',
left: {
left: [{
value: 'c',
type: 'literal'
},
}],
value: '<',
right: {
right: [{
value: 0,
type: 'number'
}
}
}]
}]
}
}
}
}]
}]
};

@@ -152,0 +152,0 @@

@@ -55,3 +55,3 @@ const assert = require('assert');

}],
where: {
where: [{
type: 'between',

@@ -66,3 +66,3 @@ value: 'data',

}]
},
}],
limit: 1,

@@ -69,0 +69,0 @@ orderBy: [{

@@ -18,18 +18,18 @@ const assert = require('assert');

it('with where', () => {
it('with WHERE', () => {
const response = {
delete: true,
from: 'tablename',
where: {
where: [{
type: 'operator',
left: {
left: [{
value: 'id',
type: 'literal'
},
}],
value: '>',
right: {
right: [{
value: 2,
type: 'number'
}
}
}]
}]
};

@@ -41,3 +41,37 @@

});
it('With DELETE with WHERE and table name on condition', () => {
const response = {
delete: true,
from: 'tablename',
where: [
{
type: 'operator',
left: [
{
type: 'literal',
value: 'tablename'
}, {
type: 'dot'
}, {
value: 'id',
type: 'literal'
}
],
value: '>',
right: [
{
value: 2,
type: 'number'
}
]
}]
};
const obj = new Sql2json('DELETE FROM tablename WHERE tablename.id > 2');
const json = obj.toJSON();
json.should.deepEqual(response);
});
});

@@ -47,3 +47,3 @@ const assert = require('assert');

it('Check throw error if using wrong escaping', () => {
it('Check throw error if using wrong escaping - case 1', () => {
try {

@@ -58,5 +58,5 @@ const obj = new Sql2json('SELECT \'a from foo');

it('Check throw error if using wrong escaping', () => {
it('Check throw error if using wrong escaping - case 2', () => {
try {
const obj = new Sql2json('select first(b1) as x from \'users/resourcewatch_wri/foo_024_vegetation_health_index\' where system:time_start >= 1533448800000 and ST_INTERSECTS(ST_SetSRID(ST_GeomFromGeoJSON(\'{/"type/":/"Point/",/"coordinates/":[18.632812500000004,21.289374355860424]}),4326), the_geom)');
const obj = new Sql2json('SELECT first(b1) as x FROM \'users/resourcewatch_wri/foo_024_vegetation_health_index\' WHERE system:time_start >= 1533448800000 and ST_INTERSECTS(ST_SetSRID(ST_GeomFromGeoJSON(\'{/"type/":/"Point/",/"coordinates/":[18.632812500000004,21.289374355860424]}),4326), the_geom)');
obj.toJSON();

@@ -63,0 +63,0 @@ assert(false, 'Expected throw error');

@@ -67,2 +67,34 @@ const assert = require('assert');

it('Group by several fields', () => {
const response = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
group: [{
type: 'literal',
value: 'tablename'
}, {
type: 'dot'
}, {
type: 'literal',
value: 'name'
}, {
type: 'literal',
value: 'tablename'
}, {
type: 'dot'
}, {
type: 'literal',
value: 'surname'
}]
};
const obj = new Sql2json('select * from tablename group by tablename.name, tablename.surname');
const json = obj.toJSON();
json.should.deepEqual(response);
});
it('Group with where', () => {

@@ -83,3 +115,3 @@ const response = {

}],
where: {
where: [{
type: 'between',

@@ -94,3 +126,3 @@ value: 'data',

}]
}
}]
};

@@ -103,3 +135,3 @@

it('Group with function', () => {
it('Group with function call as value', () => {
const response = {

@@ -132,2 +164,73 @@ select: [{

it('Group with function call on column value', () => {
const response = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
group: [{
type: 'literal',
value: 'foo'
}, {
type: 'dot'
}, {
type: 'function',
value: 'ST_GeoHash',
alias: null,
arguments: [{
type: 'literal',
value: 'the_geom_point'
}, {
type: 'number',
value: 8
}]
}],
};
const obj = new Sql2json('select * from tablename group by foo.ST_GeoHash(the_geom_point, 8)');
const json = obj.toJSON();
json.should.deepEqual(response);
});
it('Group with function call on table.column value', () => {
const response = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
group: [{
type: 'literal',
value: 'tablename'
}, {
type: 'dot'
}, {
type: 'literal',
value: 'foo'
}, {
type: 'dot'
}, {
type: 'function',
value: 'ST_GeoHash',
alias: null,
arguments: [{
type: 'literal',
value: 'the_geom_point'
}, {
type: 'number',
value: 8
}]
}],
};
const obj = new Sql2json('select * from tablename group by tablename.foo.ST_GeoHash(the_geom_point, 8)');
const json = obj.toJSON();
json.should.deepEqual(response);
});
it('Group with function with constant as argument', () => {

@@ -284,14 +387,14 @@ const response = {

from: 'tablename',
where: {
where: [{
type: 'operator',
left: {
left: [{
value: 'false',
type: 'literal'
},
}],
value: '>',
right: {
right: [{
value: 2,
type: 'number'
}
}
}]
}]

@@ -306,3 +409,2 @@ };

it('Group with function with column name (double quotes) and constant as arguments', () => {

@@ -309,0 +411,0 @@ const response = {

@@ -1,2 +0,1 @@

const assert = require('assert');
const Sql2json = require('../../').sql2json;

@@ -7,3 +6,3 @@ require('should');

describe('SQL to JSON - Order By', () => {
it('SQL with orderby', () => {
it('SQL with ORDER BY', () => {
const response = {

@@ -24,3 +23,3 @@ select: [{

const obj = new Sql2json('select * from tablename order by name');
const obj = new Sql2json('select * from tablename ORDER BY name');
const json = obj.toJSON();

@@ -45,3 +44,3 @@ json.should.deepEqual(response);

const obj = new Sql2json('select 123 from tablename order by 123');
const obj = new Sql2json('select 123 from tablename ORDER BY 123');
const json = obj.toJSON();

@@ -51,3 +50,3 @@ json.should.deepEqual(response);

it('SQL with orderby with quotes', () => {
it('SQL with ORDER BY with quotes', () => {
const response = {

@@ -68,3 +67,3 @@ select: [{

const obj = new Sql2json('select * from tablename order by "name"');
const obj = new Sql2json('select * from tablename ORDER BY "name"');
const json = obj.toJSON();

@@ -74,3 +73,3 @@ json.should.deepEqual(response);

it('SQL with orderby and direction', () => {
it('SQL with ORDER BY with table and column name', () => {
const response = {

@@ -84,4 +83,32 @@ select: [{

orderBy: [{
value: 'tablename',
type: 'literal',
alias: null,
direction: null
}, {
type: 'dot'
}, {
value: 'name',
alias: null,
type: 'string',
direction: null
}]
};
const obj = new Sql2json('select * from tablename ORDER BY tablename."name"');
const json = obj.toJSON();
json.should.deepEqual(response);
});
it('SQL with ORDER BY and direction', () => {
const response = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
orderBy: [{
value: 'name',
alias: null,
type: 'literal',

@@ -92,3 +119,3 @@ direction: 'asc'

const obj = new Sql2json('select * from tablename order by name asc');
const obj = new Sql2json('select * from tablename ORDER BY name asc');
const json = obj.toJSON();

@@ -98,3 +125,3 @@ json.should.deepEqual(response);

it('SQL with several orderby and direction', () => {
it('SQL with ORDER BY with table and column name with direction', () => {
const response = {

@@ -108,4 +135,32 @@ select: [{

orderBy: [{
value: 'tablename',
type: 'literal',
alias: null,
direction: null
}, {
type: 'dot'
}, {
value: 'name',
alias: null,
type: 'string',
direction: 'asc'
}]
};
const obj = new Sql2json('select * from tablename ORDER BY tablename."name" asc');
const json = obj.toJSON();
json.should.deepEqual(response);
});
it('SQL with several ORDER BY and direction', () => {
const response = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
orderBy: [{
value: 'name',
alias: null,
type: 'literal',

@@ -121,3 +176,3 @@ direction: 'asc'

const obj = new Sql2json('select * from tablename order by name asc, createdAt desc');
const obj = new Sql2json('select * from tablename ORDER BY name asc, createdAt desc');
const json = obj.toJSON();

@@ -127,3 +182,3 @@ json.should.deepEqual(response);

it('SQL with several orderby and direction 2', () => {
it('SQL with several ORDER BY and direction 2', () => {
const response = {

@@ -149,3 +204,3 @@ select: [{

const obj = new Sql2json('select * from tablename order by name asc, createdAt');
const obj = new Sql2json('select * from tablename ORDER BY name asc, createdAt');
const json = obj.toJSON();

@@ -155,3 +210,3 @@ json.should.deepEqual(response);

it('SQL with order by and function', () => {
it('SQL with ORDER BY and function', () => {
const response = {

@@ -176,3 +231,3 @@ select: [{

const obj = new Sql2json('select * from tablename order by avg(name)');
const obj = new Sql2json('select * from tablename ORDER BY avg(name)');
const json = obj.toJSON();

@@ -182,3 +237,3 @@ json.should.deepEqual(response);

it('SQL with order by and serveral functions', () => {
it('SQL with ORDER BY and several functions', () => {
const response = {

@@ -212,7 +267,63 @@ select: [{

const obj = new Sql2json('select * from tablename order by avg(name) asc, sum(num)');
const obj = new Sql2json('select * from tablename ORDER BY avg(name) asc, sum(num)');
const json = obj.toJSON();
json.should.deepEqual(response);
});
it('SQL with ORDER BY and several functions called on column values', () => {
const response = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
orderBy: [
{
value: 'foo',
alias: null,
type: 'literal',
direction: null
}, {
type: 'dot'
}, {
type: 'function',
direction: 'asc',
value: 'avg',
alias: null,
arguments: [{
type: 'literal',
value: 'name'
}]
}, {
value: 'tablename',
alias: null,
type: 'literal',
direction: null
}, {
type: 'dot'
}, {
value: 'foo',
alias: null,
type: 'literal',
direction: null
}, {
type: 'dot'
}, {
type: 'function',
direction: null,
value: 'sum',
alias: null,
arguments: [{
type: 'literal',
value: 'num'
}]
}]
};
const obj = new Sql2json('select * from tablename ORDER BY foo.avg(name) asc, tablename.foo.sum(num)');
const json = obj.toJSON();
json.should.deepEqual(response);
});
});

@@ -70,9 +70,14 @@ const assert = require('assert');

it('SQL with function shape', () => {
it('SQL with function call on column', () => {
const response = {
select: [{
type: 'literal',
alias: null,
value: 'Shape'
}, {
type: 'dot',
}, {
type: 'function',
alias: null,
value: 'Shape.STLength',
value: 'STLength',
arguments: []

@@ -92,2 +97,54 @@ }, {

it('SQL with function call on column', () => {
const response = {
select: [{
type: 'literal',
alias: null,
value: 'tablename'
}, {
type: 'dot',
},{
type: 'literal',
alias: null,
value: 'columnname'
}, {
type: 'dot',
}, {
type: 'function',
alias: null,
value: 'STLength',
arguments: []
}, {
value: 'x',
alias: null,
type: 'literal'
}],
from: 'tablename'
};
const obj = new Sql2json('SELECT tablename.columnname.STLength(), x from tablename');
const json = obj.toJSON();
json.should.deepEqual(response);
});
it('SQL with function call as value', () => {
const response = {
select: [{
type: 'function',
alias: null,
value: 'STLength',
arguments: []
}, {
value: 'x',
alias: null,
type: 'literal'
}],
from: 'tablename'
};
const obj = new Sql2json('SELECT STLength(), x from tablename');
const json = obj.toJSON();
json.should.deepEqual(response);
});
it('SQL with static columns', () => {

@@ -94,0 +151,0 @@ const response = {

@@ -16,14 +16,14 @@ const assert = require('assert');

from: 'tablename',
where: {
where: [{
type: 'operator',
left: {
left: [{
value: 'id',
type: 'literal'
},
}],
value: '>',
right: {
right: [{
value: 2,
type: 'number'
}
}
}]
}]
};

@@ -44,14 +44,14 @@

from: 'tablename',
where: {
where: [{
type: 'operator',
left: {
left: [{
value: 'id',
type: 'literal'
},
}],
value: '>',
right: {
right: [{
value: -2,
type: 'number'
}
}
}]
}]
};

@@ -72,5 +72,5 @@

from: 'tablename',
where: {
where: [{
type: 'operator',
left: {
left: [{
value: 'sum',

@@ -83,9 +83,9 @@ type: 'function',

}]
},
}],
value: '>',
right: {
right: [{
value: 2,
type: 'number'
}
}
}]
}]
};

@@ -106,3 +106,3 @@

from: 'tablename',
where: {
where: [{
value: 'ST_Intersects',

@@ -118,3 +118,3 @@ type: 'function',

}]
}
}]
};

@@ -136,3 +136,3 @@

from: 'tablename',
where: {
where: [{
value: 'ST_Intersects',

@@ -153,3 +153,3 @@ type: 'function',

}]
}
}]
};

@@ -171,33 +171,33 @@

from: 'tablename',
where: {
where: [{
type: 'conditional',
value: 'and',
left: {
left: [{
type: 'operator',
left: {
left: [{
value: 'id',
type: 'literal'
},
}],
value: '>',
right: {
right: [{
value: 2,
type: 'number'
}
},
right: {
}]
}],
right: [{
type: 'operator',
left: {
value: 'id',
left: [{
value: 'name',
type: 'literal'
},
value: '<',
right: {
value: 2,
}],
value: '=',
right: [{
value: 4,
type: 'number'
}
}
}
}]
}]
}]
};
const obj = new Sql2json('select * from tablename where id > 2 and id < 2');
const obj = new Sql2json('select * from tablename where id > 2 and name = 4');
const json = obj.toJSON();

@@ -215,46 +215,46 @@ json.should.deepEqual(response);

from: 'tablename',
where: {
where: [{
type: 'conditional',
value: 'or',
left: {
left: [{
type: 'conditional',
value: 'and',
left: {
left: [{
type: 'operator',
value: '>',
left: {
left: [{
value: 'a',
type: 'literal'
},
right: {
}],
right: [{
value: 2,
type: 'number'
}
},
right: {
}]
}],
right: [{
type: 'operator',
value: '<',
left: {
left: [{
value: 'b',
type: 'literal'
},
right: {
}],
right: [{
value: 3,
type: 'number'
}
}
},
right: {
}]
}]
}],
right: [{
type: 'operator',
value: '=',
left: {
left: [{
value: 'c',
type: 'literal'
},
right: {
}],
right: [{
value: 2,
type: 'number'
}
}
}
}]
}]
}]
};

@@ -275,3 +275,3 @@

from: 'tablename',
where: {
where: [{
type: 'in',

@@ -286,3 +286,3 @@ value: 'data',

}]
}
}]
};

@@ -303,3 +303,3 @@

from: 'tablename',
where: {
where: [{
type: 'in',

@@ -314,3 +314,3 @@ value: 'data',

}]
}
}]
};

@@ -331,3 +331,3 @@

from: 'tablename',
where: {
where: [{
type: 'in',

@@ -342,3 +342,3 @@ value: 'data',

}]
}
}]
};

@@ -359,3 +359,3 @@

from: 'tablename',
where: {
where: [{
type: 'between',

@@ -370,3 +370,3 @@ value: 'data',

}]
}
}]
};

@@ -387,17 +387,17 @@

from: 'tablename',
where: {
where: [{
type: 'operator',
left: {
left: [{
value: 'country_iso',
type: 'literal'
},
}],
value: '=',
right: {
right: [{
value: 'BRA',
type: 'string'
}
}
}]
}]
};
const obj = new Sql2json('select * from tablename where country_iso=\'BRA\'');
const obj = new Sql2json('select * from tablename where country_iso = \'BRA\'');
const json = obj.toJSON();

@@ -407,3 +407,3 @@ json.should.deepEqual(response);

it('With equality', () => {
it('With like', () => {
const response = {

@@ -416,17 +416,17 @@ select: [{

from: 'tablename',
where: {
where: [{
type: 'operator',
left: {
left: [{
value: 'country_iso',
type: 'literal'
},
value: '=',
right: {
}],
value: 'LIKE',
right: [{
value: 'BRA',
type: 'string'
}
}
}]
}]
};
const obj = new Sql2json('select * from tablename where country_iso = \'BRA\'');
const obj = new Sql2json('select * from tablename where country_iso LIKE \'BRA\'');
const json = obj.toJSON();

@@ -436,3 +436,3 @@ json.should.deepEqual(response);

it('With like', () => {
it('With not like', () => {
const response = {

@@ -445,17 +445,17 @@ select: [{

from: 'tablename',
where: {
where: [{
type: 'operator',
left: {
left: [{
value: 'country_iso',
type: 'literal'
},
value: 'LIKE',
right: {
}],
value: 'NOT LIKE',
right: [{
value: 'BRA',
type: 'string'
}
}
}]
}]
};
const obj = new Sql2json('select * from tablename where country_iso LIKE \'BRA\'');
const obj = new Sql2json('select * from tablename where country_iso NOT LIKE \'BRA\'');
const json = obj.toJSON();

@@ -465,2 +465,29 @@ json.should.deepEqual(response);

it('With like and wildcards', () => {
const response = {
select: [{
value: '*',
alias: null,
type: 'wildcard'
}],
from: 'tablename',
where: [{
type: 'operator',
left: [{
value: 'country_iso',
type: 'literal'
}],
value: 'LIKE',
right: [{
value: '%BRA%',
type: 'string'
}]
}]
};
const obj = new Sql2json('select * from tablename where country_iso LIKE \'%BRA%\'');
const json = obj.toJSON();
json.should.deepEqual(response);
});
it('With cast', () => {

@@ -474,14 +501,15 @@ const response = {

from: 'tablename',
where: {
where: [{
type: 'operator',
left: {
value: 'day::int',
type: 'literal'
},
left: [{
type: 'cast',
literal: 'day',
target: 'int'
}],
value: '>',
right: {
right: [{
value: 2,
type: 'number'
}
}
}]
}]
};

@@ -517,6 +545,6 @@

from: 'table',
where: {
where: [{
type: 'conditional',
value: 'AND',
left: {
left: [{
type: 'in',

@@ -531,4 +559,4 @@ value: 'confidence',

}]
},
right: {
}],
right: [{
type: 'between',

@@ -543,7 +571,7 @@ value: 'bright_ti5',

}]
}
}
}]
}]
};
const obj = new Sql2json('select * from table where confidence in (\'nominal\',\'0\') AND bright_ti5 between 1 and 4');
const obj = new Sql2json('SELECT * FROM table WHERE confidence IN (\'nominal\',\'0\') AND bright_ti5 BETWEEN 1 AND 4');
const json = obj.toJSON();

@@ -550,0 +578,0 @@ json.should.deepEqual(response);

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc