Skip to content

arloc/light-odata

 
 

Repository files navigation

OData Client

CircleCI codecov Maintainability Rating npm (scoped) Netlify

npm bundle size (scoped) Technical Debt DeepScan grade

Javascript OData Client for OData (v2/v4) Service.

Installation

npm i -S @odata/client

Alternative, in native browser environment, just add unpkg or jsdeliver umd link to your page, and the OData object will be available in window.

<script src="https://cdn.jsdelivr.net/npm/@odata/client/lib/odata-client-umd.js"></script>

Table of Contents

  1. Installation
  2. Table of Contents
  3. OData Client
  4. OData Param
  5. OData Filter
  6. Entity Set
  7. Batch Requests
  8. Server Side Polyfill
  9. Others
  10. CHANGELOG
  11. LICENSE
  12. Thanks JetBrains

ODataClient

How to use @odata/client

Start with a simple query, following code start a GET http request, and asks the server to respond to all customers which phone number equals 030-0074321

import { OData } from "@odata/client"

// odata.org sample odata service
const TestServiceURL = "https://services.odata.org/V2/Northwind/Northwind.svc/$metadata"
const client = OData.New({
  metadataUri: TestServiceURL,
  // variant: "c4c"
})

const runner = async () => {
  
  // Query by filter
  //
  // GET /Customers?$format=json&$filter=Phone eq '030-0074321'
  const filter = client.newFilter().field("Phone").eqString("030-0074321");

  const result = await client.newRequest({ // ODataRequest object
    collection: "Customers", // collection name
    params: client.newParam().filter(filter) // odata param
  })

}
// OData V4 client
const Service = "https://odata-v4-demo-001.herokuapp.com/odata/$metadata"

const client = OData.New4({
  metadataUri: Service,
  variant: "cap"
})

ODataRequest interface

interface ODataRequest<T> {
  collection: string, /** collection name */
  id?: any, /** object key in READ/UPDATE/DELETE, user could set this field with number/string/object type */
  params?: ODataQueryParam, /** params in QUERY */
  /**
   * GET for QUERY/READ; for QUERY, you can use params to control response data
   * PATCH for UPDATE (partial)
   * PUT for UPDATE (overwrite)
   * POST for CREATE
   * DELETE for DELETE
   */
  method?: HTTPMethod,
  entity?: T /** data object in CREATE/UPDATE */
}

ODataResponse interface

interface PlainODataResponse {
  d?: {
    __count?: string; /** $inlinecount values */
    results: any | Array<any>; /** result list/object */
    [key: string]: any;
  };
  error?: { /** if error occurred, node error will have value */
    code: string;
    message: {
      lang: string, 
      value: string /** server error message */
    }
  }
}

ODataParam

use ODataParam to control data size, involved fields and order

How to use ODataParam

pagination

skip first 30 records and top 10 records

// equal to $format=json&$skip=30&$top=10
OData.newParam().skip(30).top(10)

filter

filter data by fields value

// $format=json&$filter=A eq 'test'
OData.newParam().filter(OData.newFilter().field("A").eqString("test"))

inline count

response with all records count, usefully.

also could set with filter, and response with filtered records count.

// equal to $format=json&$inlinecount=allpages
OData.newParam().inlinecount(true).top(1).select("ObjectID")

orderby

sort response data

// result is $format=json&$orderby=CreationDateTime desc
OData.newParam().orderby("CreationDateTime")

// result is $format=json&$orderby=A desc,B asc
OData.newParam().orderby([{ field: "A" }, { field: "B", order: "asc" }])

navigation property

expand association data

// $expand=Customers
OData.newParam().expand("Customers")
// $expand=Customers,Employees
OData.newParam().expand(["Customers", "Employees"])

fields select

remove unused fields from response

// $format=json&$select=ObjectID,Name
OData.newParam().select("ObjectID").select("Name");

full text search (basic query)

search all supported fields with text

SAP systems feature

LOW PERFORMANCE

// fuzzy
// $format=json&$search=%any word%
OData.newParam().search("any word");
// not fuzzy
// $format=json&$search=any word
OData.newParam().search("any word", false);

ODataFilter

use the ODataFilter to filter data

How to use ODataFilter

Most SAP systems only support AND operator between different fields, and OR operator in a same field. (it depends on SAP Netweaver implementation)

So you don't need to specify AND/OR operator between fields, @odata/client will auto process it.

Though C4C only support AND operator in different fields, but for gt/lt/ge/le, you can use AND for filtering period data.

just ref following examples

filter by single field value

// Name eq 'test string'
OData.newFilter().field("Name").eqString("test string")

// ID lt '1024'
OData.newFilter().field("ID").lt("'1024'")

// also support eq/ne/le/lt/gt/ge ...

filter by multi fields

// Name eq 'test string1' and Name2 eq 'test string2'
OData
  .newFilter()
  .field("Name").eq("'test string1'")
  .field("Name2").eqString("test string2")

filter by one field but multi values

// Name eq 'test string1' and (Name2 eq 'test string1' or Name2 eq 'test string2')
OData.newFilter()
  .field("Name").eq("'test string1'")
  .field("Name2").in(["test string3", "test string2"])

filter by date

Depends on field type, use betweenDateTime or betweenDateTimeOffset to filter date。

Please provide Date object in this api.

// Name eq 'test string1' and (CreationDateTime gt datetime'2018-01-24T12:43:31.839Z' and CreationDateTime lt datetime'2018-05-24T12:43:31.839Z')
OData
  .newFilter()
  .field("Name").eq("'test string1'")
  .field("CreationDateTime").betweenDateTime(
    new Date("2018-01-24T12:43:31.839Z"),
    new Date("2018-05-24T12:43:31.839Z"),
    false
  )
  .build()

// > include boundary value

// (CreationDateTime ge datetime'2018-01-24T12:43:31.839Z' and CreationDateTime le datetime'2018-05-24T12:43:31.839Z')
OData
  .newFilter()
  .field("CreationDateTime").betweenDateTime(
    new Date("2018-01-24T12:43:31.839Z"),
    new Date("2018-05-24T12:43:31.839Z")
  )
  .build()

filter by function

// CompagnyName contains 'testName' case sensitive
OData
  .newFilter()
  .filter("indexof(CompanyName, 'testName')").gt(-1)

// CompagnyName has legth 8
OData
  .newFilter()
  .filter("length(CompanyName)").eq(8)

// CompagnyName has substring 'test'
OData
  .newFilter()
  .filter("substringof('test', CompanyName)").eq(true)

see more possiblility at 4.5. Filter System Query Option ($filter) function odata v2 documentation

EntitySet

use EntitySet to CRUD entity

How to use EntitySet
const runner = async () => {

  // odata client
  const client = createClient()
  const es = client.getEntitySet<CapDemoPeople>("Peoples")

  // CREATE instnace
  const res0 = await es.create({
    UserName: name
  })

  expect(res0.UserName).toEqual(name)
  expect(res0.ID).not.toBeUndefined()

  // QUERY by name, quick find
  const res = await es.find({ UserName: name })

  expect(res).not.toBeUndefined()
  expect(res.length > 0).toBeTruthy()

  const id = res[0].ID
  expect(id).not.toBeUndefined()

  // RETRIEVE by id
  const res2 = await es.retrieve(id)
  expect(res2.UserName).toEqual(name)

  // UPDATE
  const firstName = Random.name();
  await es.update(id, { Name_FirstName: firstName })

  // DELETE
  await es.delete(id)

}

Batch requests

use odata $batch api for operating multi entities in single HTTP request, it will save a lot of time between client & server (In the case of processing a large number of requests).

How to send batch request
const runner = async () => {

  const odata = OData.New({
    metadataUri: `https://services.odata.org/V2/(S(${v4()}))/OData/OData.svc/$metadata`,
  })
  const testDesc1 = v4(); // a generated uuid
  const testDesc2 = v4();

  // execute reqeusts and return mocked responses
  const result = await odata.execBatchRequests([
    odata.newBatchRequest({
      collection: "Products",
      entity: {
        ID: 100009,
        Description: testDesc1,
      },
      method: "POST",
      // withContentLength: true, for SAP OData, please set this flag as true
    }),
    odata.newBatchRequest({
      collection: "Products",
      entity: {
        ID: 100012,
        Description: testDesc2,
      },
      method: "POST",
      // withContentLength: true, for SAP OData, please set this flag as true
    })
  ])

  result.map(r => expect(r.status).toEqual(201)) // Created

}

Server Side Polyfill

Use polyfill for your server-side application.

// import "@odata/client/lib/polyfill"
require("@odata/client/lib/polyfill")

// use OData

Others

Use the go tool 'markdown-toc' to generate table of contents, with following commands:

markdown-toc --replace --inline --depth 2 --header "## Table of Contents" --skip-headers=1  README.md

Thanks JetBrains

About

OData(V2/V4) Client

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 96.3%
  • JavaScript 3.7%