14.4.1 • Published 2 years ago

@lanetix/list-query-mapper v14.4.1

Weekly downloads
-
License
UNLICENSED
Repository
github
Last release
2 years ago

Circle CI codecov js-standard-style

node-lanetix-list-query-mapper

An adapter that takes an OData URI for querying our stores and maps the payload to a contract.

Operations

Requirements

npm auth token

  • npm login (From any directory)
  • Use your own npmjs login
  • cd ~/ open .npmrc in your favorite text editor
  • copy the token that appears after //registry.npmjs.org/:_authToken= export NPM_TOKEN=<paste token here>
  • You can also write this line to your .bashrc, .bash_profile, .zshrc, etc...
  • npm install

Installation

npm install -S @lanetix/list-query-mapper

Run Tests

npm install

npm run test

Usage

Odata --> pg results

// PG version
import createMapper from '@lanetix/list-query-mapper'
import getType from 'promiseToReturnTypeInformation'
import get from 'thingThatExecutesPgQuery'
import getOrganizationConfig from 'records/src/lib/get-organization-config'

export default function getListView: (uri, ...options) {
  const mapper = createMapper('pg')
  const context = {
    organization_config: getOrganizationConfig(settings.organization_id),
    options.schema_name,
    recordTypeBuilder: getType,
    options.recordType,
    options.typeInformation,
    user  // user object from the route, not the SQL transaction settings
  }

  return mapper.mapFromOdataUri(uri, context)
    .then(get)
    .then(mapper.mapToOdataPayload)
}

Odata --> pg fragment (using $apply)

// PG version
import createMapper from '@lanetix/list-query-mapper'
import getType from 'promiseToReturnTypeInformation'
import get from 'thingThatExecutesPgQuery'
import getOrganizationConfig from 'records/src/lib/get-organization-config'
import should from 'should'

const uri = "$apply=compute(concat( name, 'e') as elephant)"

export default function (options) {
  const mapper = createMapper('pg')
  const context = {
    organization_config: getOrganizationConfig(settings.organization_id),
    options.schema_name,
    recordTypeBuilder: getType,
    options.recordType,
    options.typeInformation,
    user  // user object from the route, not the SQL transaction settings
  }

  const { values, apply: { fragments }} = mapper.mapFromOdataUri(uri, context)
  should(values).deepEqual(['e'])
  const expected = [{
    fragment: '(CONCAT(name, $1::text))',
    alias: 'elephant',
    type: 'string'
  }]
  should(fragments).deepEqual(expected)
}

3 Interfaces: uri -> SQL, ast -> SQL, SQL -> pgResults

mapFromOdataUri

// uri -> SQL
const sqlFromUri = mapper.mapFromOdataUri(uri, context)
const { query, values } = sqlFromUri

mapFromOdataAst

// ast -> SQL
import parser from '@lanetix/odata-parser'
import astTransformer from '@lanetix/odata-ast-transformations'

let ast
try {
  ast = parser.parse(uri)
} catch (e) {
  throw new Error(e)
}

// optional: do stuff to your ast
const transformedAst = astTransformer.intersectFilters(
  ast, ["$expand=favorite($select=id;$filter=id eq 123)","$filter=name ne 'harry'"]
)

// convert into SQL
const sqlFromAst = mapper.mapFromOdataAst(transformedAst, context)
const { query, values } = sqlFromAst

mapToOdataPayload

import get from 'thingThatExecutesPgQuery'

const { rows } = mapper.mapFromOdataUri(uri, context)
  .then(get)
  .then(mapper.mapToOdataPayload)

REPL

npm run repl

Commands in this module are of the format: methodName(ast, context).

The repl may be used by either: 1. Set the ast, set the context, then call the withAst.methodName(). 2. Set the uri, set the context, then call the withUri.methodName().

In order to set the ast, you may use 2 approaches:

  • setAstJSON({"tag":"yes"}) // must be a minified JSON format
  • setAstFILE('./test-ast.json')

In order to set the uri, you may use 2 approaches:

  • setUriTEXT("$select=id,lanetix/archived,lanetix/id&$filter=name eq 'Antwan'")
  • setUriFILE('./test-uri.json')

In order to set the context, you may use 2 approaches:

  • setContextFILE('./test/data/foo-context.json') // common sense way
  • setContextJSON({...crazy huge context file...}) // lunatic way

Result: mapper output is a query (SQL) and the values.

Here is an example, with I/O shown:

Denises-MacBook-Pro:node-lanetix-list-query-mapper$ npm run repl

> @lanetix/list-query-mapper@13.3.0 repl /Users/denise/env/dev/node-lanetix-list-query-mapper
> babel-node repl.js

Welcome to the Lanetix odata-ast-transformations REPL.
Please refer to the README.md for the appropriate usage.

> setContextFILE('./test/data/foo-context.json')
Context is set.

> setUriTEXT("$select=id,trash,bass,fess,nullable_string,name,lanetix/archived,lanetix/id&$filter=name eq 'Antwan'")
Set URI:
 $select=id,trash,bass,fess,nullable_string,name,lanetix/archived,lanetix/id&$filter=name eq 'Antwan'

> withUri.mapFromOdataUri()

> SQL:
SELECT id, trash, bass, fess, nullable_string, name, json_build_object('archived', (lanetix).archived, 'id', (lanetix).id)::jsonb as lanetix
                   FROM records_2731111."view.foo"  WHERE (lower(name) COLLATE "C") = ($1::text)
VALUES:
["antwan"]
> q
Denises-MacBook-Pro:node-lanetix-list-query-mapper$

Concept

  • Takes a URI that follows OData 4.0 URI Specification and returns data from PG.
  • Converts the Abstract Syntax Tree (AST) to the final form will require using visitors and mapping.

File Structure

  • src/mappers entry point into the mappers.
  • src/lib/ the mapper files for each SQL dialect.

OData Structure

  • lx has not implemented all of the odata spec. Here is a summary of what is implemented.
  • GET recordType?$queryOption&$queryOption
  • query options:
    • $search = search feature. odata node type 'functioncall' with func 'substringof'. creates pg sql for LIKE.
    • $select = a list of fieldNames
      $select=name,close_date,chance_to_win,contract_length_years
    • $apply = for set transformations. Also has the compute transformation (for any commonExpn).
      recordType?$apply=concat(set_1, set_2)
      recordType?$apply=compute(commonExpn as aliasName)
      recordType?$apply=compute(concat(name, "is a frog") as aliasName)
      • this query option can not be used in combination with any other query option.
    • $filter = boolean expressions, connected by and|or.
      • currently we support:
        • math operators add|sub|mul
        • functions listed in src/lib/pg/operators/functioncall
        • comparison operators eq|ne|lt|gt|le|ge
        • conditional operators and|or
      • limitations:
        • no operator precedence. use parans instead. (pegjs)
        • no mod. no div. (divide-by-zero issue)
        • the use of and|or is limited (pegjs, operator precedence). Instead use either:
          ( exp AND exp AND exp) OR ( exp AND exp AND exp)
          ( exp OR exp OR exp) AND ( exp OR exp OR exp)
    • $expand = related fields. Will then include it's $select.
      &$expand=project_account_id($select=lanetix/id;$expand=owner($select=lanetix/id,first_name))
    • $orderby = orderby. Takes list of properties and asc|desc.
      &$orderby=name asc
    • $count
    • $skip = offset. Takes integer. Used in pagination.
    • $top = limit. Takes integer. Used in pagination.
      &$top=20
  • use of parans and whitespace:

Parallel Paths through the mapper

Differences from OData specs

  • We use the parameter-alias as a Lanetix lx-parameter-alias. Uses similar syntax for the identifier (@lx_myTeam), but the user cannot provide an expression which the identifier is equal to (unlike the OData spec).
    $filter=nullable_integer eq @lx_myUser_Id and nullable_integer eq @lx_myTeam and
    nullable_integer eq @lx_myOrg_Id and nullable_string eq @lx_myUser_Timezone
  • We use the route (api access) url a bit different from the spec.

    // per odata spec
    // http://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html
    http://localhost/Products?$orderby=Name
    
    // Lx
    http://localhost/v1/insights/o?recordType=products&odata=$orderby=name asc
  • The odata spec has an implied SELECT * if no $select query option is provided. vs. Lanetix requires a $select query option. Because returning all relation attributes is too large. In aggregations, we also require an explicit select, by use of a final compute transformation. $apply=transformation1()/transformation2()/compute(path as alias1, path as alias2)

  • When referring to related fields (related/related/field), for both the $select&$filter&expand and the $apply, Lanetix requires that an expand be explicitly stated. The OASIS spec is unclear (for all circumstances) as to whether or not this is a typically requirement, but Lx definitely requires it.
  • The $apply SPEC also includes other deviations. See here.

Arrows vs Fibers (types of JOINs)

Currently supported dialects.

14.4.5-beta.7

2 years ago

14.4.5-beta.8

2 years ago

14.4.5-beta.9

2 years ago

14.4.5-beta.11

2 years ago

14.4.5-beta.10

2 years ago

14.4.5-beta.5

3 years ago

14.4.5-beta.6

3 years ago

14.4.5-beta.4

3 years ago

14.4.5-beta.3

3 years ago

14.4.5-beta.2

3 years ago

14.4.5-beta.1

3 years ago

14.4.5-beta.0

3 years ago

14.4.4

3 years ago

14.4.3

3 years ago

14.4.2

3 years ago

14.4.1

5 years ago

14.4.0

6 years ago

15.0.0

6 years ago

14.3.2

6 years ago

14.3.1

6 years ago

14.3.0

6 years ago

14.2.0

6 years ago

14.1.0

6 years ago

14.0.4

7 years ago

14.0.3

7 years ago

14.0.2

7 years ago

13.14.2

7 years ago

14.0.1

7 years ago

14.0.0

7 years ago

13.14.1

7 years ago

13.14.0

7 years ago

13.13.1

7 years ago

13.13.0

7 years ago

13.12.5

7 years ago

13.12.4

7 years ago

13.12.3

7 years ago

13.12.2

7 years ago

13.12.1

7 years ago

13.12.0

7 years ago

13.11.5

7 years ago

13.11.4

7 years ago

13.11.3

7 years ago

13.11.1

7 years ago

13.11.0

7 years ago

13.10.1

7 years ago

13.10.0

7 years ago

13.9.0

7 years ago

13.8.0

7 years ago

13.7.0

7 years ago

13.6.0

7 years ago

13.5.0

7 years ago

13.4.0

7 years ago

13.3.0

7 years ago

13.2.0

7 years ago

13.1.2

7 years ago

13.1.1

7 years ago

13.1.0

7 years ago

13.0.0

7 years ago

11.10.0

7 years ago

11.9.0

7 years ago

11.8.0

7 years ago

11.7.0

7 years ago

11.6.1

7 years ago

11.6.0

7 years ago

11.5.0

7 years ago

11.4.3

7 years ago

12.2.0

7 years ago

12.0.0

7 years ago

11.4.2

7 years ago

11.4.1

7 years ago

11.4.0

7 years ago

11.3.0

7 years ago

11.2.3

7 years ago

11.2.2

7 years ago

11.2.1

7 years ago

11.2.0

7 years ago

11.1.0

7 years ago

11.0.0

7 years ago

10.1.0

7 years ago

10.0.3

7 years ago

10.0.2

7 years ago

10.0.1

7 years ago

10.0.0

7 years ago

9.4.1

7 years ago

9.4.0

8 years ago

9.3.3

8 years ago

9.3.2

8 years ago

9.3.1

8 years ago

9.3.0

8 years ago

9.2.0

8 years ago

9.1.0

8 years ago

9.0.0

8 years ago

8.3.0

8 years ago

8.2.0

8 years ago

8.1.4

8 years ago

8.1.3

8 years ago

8.1.2

8 years ago

8.1.1

8 years ago

8.1.0

8 years ago

8.0.0

8 years ago

7.0.3

8 years ago

7.0.2

8 years ago

7.0.1

8 years ago

7.0.0

8 years ago

6.3.1

8 years ago

6.3.0

8 years ago

6.2.0

8 years ago

6.1.0

8 years ago

6.0.1

8 years ago

6.0.0

8 years ago

5.7.0

8 years ago

5.6.0

8 years ago

5.5.0

8 years ago

5.4.0

8 years ago

5.3.0

8 years ago

5.2.0

8 years ago

5.1.0

8 years ago

5.0.5

8 years ago

5.0.4

8 years ago

5.0.3

8 years ago

5.0.2

8 years ago

5.0.1

8 years ago

5.0.0

8 years ago

4.2.1

8 years ago

4.2.0

8 years ago

4.1.0

8 years ago

4.0.0

8 years ago

3.1.0

8 years ago

3.0.0

8 years ago

2.6.0

8 years ago

2.5.2

8 years ago

2.5.1

8 years ago

2.5.0

8 years ago

2.4.0

8 years ago

2.3.4

8 years ago

2.3.3

8 years ago

2.3.2

8 years ago

2.3.1

8 years ago

2.3.0

8 years ago

2.2.1

9 years ago

2.2.0

9 years ago

2.1.1

9 years ago

2.1.0

9 years ago

2.0.2

9 years ago

2.0.1

9 years ago

2.0.0

9 years ago

1.2.1

9 years ago

1.2.0

9 years ago

1.1.0

9 years ago

1.0.1

9 years ago

1.0.0

9 years ago