@sap_oss/odata-library
Advanced tools
Comparing version 0.9.8 to 0.9.9
@@ -5,2 +5,12 @@ # Changelog | ||
* 202907a - [FIX] Resolve OData types for procedure imports - Norbert Volf | ||
* 91179a9 - [FEATURE] Use base type to define EntityType key - Norbert Volf | ||
* 1d518c2 - [FIX] NavProp resets EntitySet request too - Norbert Volf | ||
* ccf6f0e - [FIX] Return correct context for chaining - Michal Kozubik | ||
* 27e90e2 - [FEATURE] Read stream by single entity - Norbert Volf | ||
* 53d1f46 - [FEATURE] Support services without CSRF token implementation - Norbert Volf | ||
* ef67a88 - [FIX] Corretly determine 1:1 navigation property - Norbert Volf | ||
# 0.9.8 | ||
* 3147731 - [FEATURE] Bound action implementation - Norbert Volf | ||
@@ -7,0 +17,0 @@ |
@@ -233,2 +233,7 @@ # Read entity from EntitySet | ||
### Read stream by navigation property | ||
Common use of navigation property with relation 1:1 is producing | ||
stream. | ||
```javascript | ||
@@ -245,1 +250,26 @@ service.Items.key({ | ||
``` | ||
### Read stream from by single entity | ||
Read stream entity with entity definiton. The example | ||
```javascript | ||
service.C_AllocationCycleTP.filter("IsActiveEntity eq true") | ||
.top(1) | ||
.get() | ||
.then((result) => { | ||
return service.AllocExcelTemplateSet.key({ | ||
cycle: [ | ||
result[0].AllocationType, | ||
result[0].AllocationCycle, | ||
new Date(parseInt(result[0].AllocationCycleStartDate.match(/\/Date\((\d+)\)\//)[1], 10)) | ||
.toISOString() | ||
.replace(/(-|T.*)/g, "") | ||
].join(",") | ||
}).get(); | ||
}) | ||
.then((result) => { | ||
assert.ok(result instanceof Buffer); | ||
}); | ||
``` |
@@ -60,3 +60,3 @@ /* vim: set noai ts=4 sw=4 expandtab eol nobinary: */ | ||
}, | ||
series(taskLocalPrettify, taskLocalUnit, taskLocalLint) | ||
series(taskLocalUnit, taskLocalLint) | ||
); | ||
@@ -110,2 +110,3 @@ done(); | ||
exports.validate = series(taskPrettifyCI, taskLocalLint, taskLocalUnit); | ||
exports.prettier = series(taskLocalPrettify); | ||
exports.watch = taskWatch; | ||
@@ -112,0 +113,0 @@ exports.default = series( |
@@ -441,7 +441,13 @@ "use strict"; | ||
"/$batch", | ||
{ | ||
"x-csrf-token": csrfToken, | ||
"Content-Type": `multipart/mixed;boundary=${batchNormalized.boundary()}`, | ||
Accept: "multipart/mixed", | ||
}, | ||
_.assign( | ||
{ | ||
"Content-Type": `multipart/mixed;boundary=${batchNormalized.boundary()}`, | ||
Accept: "multipart/mixed", | ||
}, | ||
csrfToken | ||
? { | ||
"x-csrf-token": csrfToken, | ||
} | ||
: {} | ||
), | ||
payload, | ||
@@ -721,4 +727,14 @@ true | ||
getResultPath(isList) { | ||
return isList ? this._listResultPath : this._instanceResultPath; | ||
/** | ||
* Determine path to result content | ||
* | ||
* @param {Boolean} isList true if result is array | ||
* @param {IncomingMessage} result object with response from backend | ||
* | ||
* @return {String} path with dot notation to content of response | ||
*/ | ||
getResultPath(isList, result) { | ||
return isList && _.has(result, this._listResultPath) | ||
? this._listResultPath | ||
: this._instanceResultPath; | ||
} | ||
@@ -725,0 +741,0 @@ |
@@ -89,6 +89,8 @@ "use strict"; | ||
body.length > 0 | ||
? { | ||
"x-csrf-token": `${csrfToken}`, | ||
"Content-Length": _.get(body, 0).length, | ||
} | ||
? _.assign( | ||
{ | ||
"Content-Length": _.get(body, 0).length, | ||
}, | ||
csrfToken ? { "x-csrf-token": `${csrfToken}` } : {} | ||
) | ||
: {}, | ||
@@ -95,0 +97,0 @@ this.headers |
@@ -122,3 +122,5 @@ "use strict"; | ||
.then((csrfToken) => { | ||
this.header("x-csrf-token", csrfToken); | ||
if (csrfToken) { | ||
this.header("x-csrf-token", csrfToken); | ||
} | ||
this.header("Content-type", "application/json"); | ||
@@ -125,0 +127,0 @@ this.header("Accept", "application/json"); |
@@ -48,2 +48,15 @@ "use strict"; | ||
/** | ||
* Resets default request of the navigation property | ||
* and "parent" entity set also | ||
* | ||
* @memberof NavigationProperty | ||
* | ||
* @protected | ||
*/ | ||
reset() { | ||
QueryableResource.prototype.reset.call(this); | ||
this.source.reset(); | ||
} | ||
/** | ||
* Indicates of the NavigationProperty Multiplicity is multiple or not | ||
@@ -101,3 +114,3 @@ * @returns {Boolean} true if the multiplicity is * | ||
* | ||
* @return {RequestDefinition} itself for the chaining | ||
* @return {NavigationProperty} itself for the chaining | ||
* | ||
@@ -107,5 +120,9 @@ * @memberof NavigationProperty | ||
key() { | ||
return this.isMultiple() | ||
? super.key.apply(this, arguments) | ||
: this.defaultRequest.registerAssociations(); | ||
if (this.isMultiple()) { | ||
super.key.apply(this, arguments); | ||
} else { | ||
this.defaultRequest.registerAssociations(); | ||
} | ||
return this; | ||
} | ||
@@ -112,0 +129,0 @@ } |
@@ -41,3 +41,4 @@ "use strict"; | ||
top(top) { | ||
return this.defaultRequest.top(top); | ||
this.defaultRequest.top(top); | ||
return this; | ||
} | ||
@@ -47,3 +48,4 @@ | ||
select(...args) { | ||
return this.defaultRequest.select(...args); | ||
this.defaultRequest.select(...args); | ||
return this; | ||
} | ||
@@ -53,3 +55,4 @@ | ||
skip(skip) { | ||
return this.defaultRequest.skip(skip); | ||
this.defaultRequest.skip(skip); | ||
return this; | ||
} | ||
@@ -59,3 +62,4 @@ | ||
filter(filter) { | ||
return this.defaultRequest.filter(filter); | ||
this.defaultRequest.filter(filter); | ||
return this; | ||
} | ||
@@ -65,3 +69,4 @@ | ||
orderby(...args) { | ||
return this.defaultRequest.orderby(...args); | ||
this.defaultRequest.orderby(...args); | ||
return this; | ||
} | ||
@@ -71,3 +76,4 @@ | ||
expand(...args) { | ||
return this.defaultRequest.expand(...args); | ||
this.defaultRequest.expand(...args); | ||
return this; | ||
} | ||
@@ -77,3 +83,4 @@ | ||
search(pattern) { | ||
return this.defaultRequest.search(pattern); | ||
this.defaultRequest.search(pattern); | ||
return this; | ||
} | ||
@@ -83,3 +90,4 @@ | ||
key(entityKey) { | ||
return this.defaultRequest.key(entityKey); | ||
this.defaultRequest.key(entityKey); | ||
return this; | ||
} | ||
@@ -621,3 +629,5 @@ | ||
request.header("x-csrf-token", token); | ||
if (token) { | ||
request.header("x-csrf-token", token); | ||
} | ||
if (!hasStream) { | ||
@@ -649,3 +659,3 @@ request.header("Accept", "application/json"); | ||
} else { | ||
resultPath = this.agent.getResultPath(request._isList); | ||
resultPath = this.agent.getResultPath(request._isList, response); | ||
value = this._unwrapNestedProperties( | ||
@@ -652,0 +662,0 @@ _.get(response, resultPath, response.ok) |
@@ -481,2 +481,4 @@ "use strict"; | ||
this._path = `/${this._resource.getListResourcePath()}?${urlQuery}`; | ||
} else if (this._resource.entityTypeModel.hasStream && this._isEntity) { | ||
this._path = `/${this._resource.getSingleResourcePath()}/\$value`; | ||
} else if (this._resource.entityTypeModel.hasStream) { | ||
@@ -501,9 +503,12 @@ this._path = `/${this._resource.getListResourcePath()}/\$value`; | ||
get _isList() { | ||
return this._resource.isParameterized || !this._isEntity; | ||
} | ||
get _isEntity() { | ||
// Navigation property: If the multiplicity is 1..1 then no key is required and there will always be a single entity result | ||
let isNavPropSingle = | ||
this._resource.hasOwnProperty("isMultiple") && | ||
!this._resource.isMultiple(); | ||
let isEntity = | ||
_.has(this, "_keyValue") || _.has(this, "_payload") || isNavPropSingle; | ||
return this._resource.isParameterized || !isEntity; | ||
_.isFunction(this._resource.isMultiple) && !this._resource.isMultiple(); | ||
return ( | ||
_.has(this, "_keyValue") || _.has(this, "_payload") || isNavPropSingle | ||
); | ||
} | ||
@@ -510,0 +515,0 @@ |
@@ -125,3 +125,4 @@ "use strict"; | ||
parameter(parameterName, parameterValue) { | ||
return this.defaultRequest.parameter(parameterName, parameterValue); | ||
this.defaultRequest.parameter(parameterName, parameterValue); | ||
return this; | ||
} | ||
@@ -131,3 +132,4 @@ | ||
parameters(parameters) { | ||
return this.defaultRequest.parameters(parameters); | ||
this.defaultRequest.parameters(parameters); | ||
return this; | ||
} | ||
@@ -151,3 +153,3 @@ | ||
* | ||
* @return {RequestDefinition} itself for the chaining | ||
* @return {Resource} itself for the chaining | ||
* | ||
@@ -157,3 +159,4 @@ * @memberof Resource | ||
header(key, value) { | ||
return this.defaultRequest.header(key, value); | ||
this.defaultRequest.header(key, value); | ||
return this; | ||
} | ||
@@ -198,3 +201,3 @@ | ||
* | ||
* @return {RequestDefinition} himself for the chaining | ||
* @return {Resource} itself for the chaining | ||
* | ||
@@ -204,3 +207,4 @@ * @memberof Resource | ||
setQueryParameter(name, value) { | ||
return this.defaultRequest.setQueryParameter(name, value); | ||
this.defaultRequest.setQueryParameter(name, value); | ||
return this; | ||
} | ||
@@ -216,3 +220,3 @@ | ||
* | ||
* @return {RequestDefinition} itself for the chaining | ||
* @return {Resource} itself for the chaining | ||
* | ||
@@ -219,0 +223,0 @@ * @memberof Resource |
@@ -47,3 +47,3 @@ "use strict"; | ||
let schemas = _.isArray(service.Schema) | ||
? service.Schema.map((s) => new CsdlSchema(s, settings)) | ||
? service.Schema.map((s) => new CsdlSchema(s, settings, this)) | ||
: []; | ||
@@ -50,0 +50,0 @@ |
@@ -9,3 +9,3 @@ "use strict"; | ||
const EntityType = require("./schema/EntityType"); | ||
const Function = require("./schema/Function"); | ||
const FunctionType = require("./schema/Function"); | ||
const EntityContainer = require("./dataSource/EntityContainer"); | ||
@@ -48,3 +48,3 @@ const EnumType = require("./schema/EnumType"); | ||
sourceElement: "Function", | ||
Class: Function, | ||
Class: FunctionType, | ||
}, | ||
@@ -58,63 +58,2 @@ { | ||
function initChildProperties(schema) { | ||
childCollections.forEach((collection) => { | ||
let child = _.get(schema.raw, collection.sourceElement, []).map( | ||
(t) => new collection.Class(t) | ||
); | ||
Object.defineProperty(schema, collection.name, { | ||
get: () => child, | ||
}); | ||
}); | ||
} | ||
function initSchemaDependentProperties(schema) { | ||
childCollections.forEach((collection) => { | ||
let items = schema[collection.name]; | ||
if (items.length > 0 && items[0].initSchemaDependentProperties) { | ||
items.forEach((item) => item.initSchemaDependentProperties(schema)); | ||
} | ||
}); | ||
} | ||
function matchPath(regex, path, name) { | ||
let matches = regex.exec(path); | ||
if (!matches) { | ||
throw new Error(`Unknown ${name} format: ${path}`); | ||
} | ||
return matches; | ||
} | ||
function parseTypePath(targetPath) { | ||
let collectionMatches = /Collection\((.+)\)/.exec(targetPath); | ||
let matches = matchPath( | ||
/([a-z0-9_\.]+)\.([a-z0-9_]+)$/i, | ||
collectionMatches ? collectionMatches[1] : targetPath, | ||
"type path" | ||
); | ||
return { | ||
path: matches[0], | ||
namespace: matches[1], | ||
name: matches[2], | ||
isCollection: !!collectionMatches, | ||
}; | ||
} | ||
function parseModelPath(targetPath) { | ||
let matches = matchPath( | ||
/^([a-z0-9_\.]+)\.([a-z0-9_]+)(\/|\/([a-z0-9_]+))?$/i, | ||
targetPath, | ||
"model path" | ||
); | ||
return { | ||
path: matches[0], | ||
namespace: matches[1], | ||
element: matches[2], | ||
subElement: matches[4], | ||
}; | ||
} | ||
/** | ||
@@ -132,5 +71,7 @@ * Top-level conceptual schema definition language (CSDL) construct that allows creation of a namespace. | ||
* @param {Object} [settings] (normalized) settings for the metadata, e.g. { strict: false } to ignore non critical errors | ||
* @param {EdmxModel} metaModel root point of metadata in-memory structure | ||
* | ||
* @memberof CsdlSchema | ||
*/ | ||
constructor(rawMetadata, settings) { | ||
constructor(rawMetadata, settings, metaModel) { | ||
Object.defineProperty(this, "raw", { | ||
@@ -157,4 +98,4 @@ get: () => rawMetadata, | ||
initChildProperties(this); | ||
initSchemaDependentProperties(this); | ||
CsdlSchema.initChildProperties(this, metaModel); | ||
CsdlSchema.initSchemaDependentProperties(this); | ||
this.applyAnnotations(rawMetadata.Annotations); | ||
@@ -201,3 +142,3 @@ } | ||
getType(name) { | ||
let target = parseTypePath(name); | ||
let target = CsdlSchema.parseTypePath(name); | ||
let type = this._getTypeCollections(target.namespace) | ||
@@ -232,3 +173,3 @@ .map((types) => types.find((t) => t.name === target.name)) | ||
let target = this._parseModelPath(path); | ||
var child = childCollections | ||
let child = childCollections | ||
.map((collection) => | ||
@@ -295,3 +236,3 @@ this[collection.name].find((item) => item.name === target.element) | ||
_parseModelPath(path) { | ||
let target = parseModelPath(path); | ||
let target = CsdlSchema.parseModelPath(path); | ||
@@ -367,4 +308,75 @@ if ( | ||
} | ||
/** | ||
* Initialize metadata collections (EntityType, Action,...) | ||
* | ||
* @param {CsdlSchema} schema - instance of the schema to append collections | ||
* @param {EdmxModel} metaModel - instance of metadata model class. The instance | ||
* is passed to child instances for cross reference usage like basetype | ||
* and entity types from differnet schemas | ||
*/ | ||
static initChildProperties(schema, metaModel) { | ||
childCollections.forEach((collection) => { | ||
let child = _.get(schema.raw, collection.sourceElement, []).map( | ||
(typeRawMetadata) => { | ||
return new collection.Class(typeRawMetadata, metaModel); | ||
} | ||
); | ||
Object.defineProperty(schema, collection.name, { | ||
get: () => child, | ||
}); | ||
}); | ||
} | ||
static initSchemaDependentProperties(schema) { | ||
childCollections.forEach((collection) => { | ||
let items = schema[collection.name]; | ||
if (items.length > 0 && items[0].initSchemaDependentProperties) { | ||
items.forEach((item) => item.initSchemaDependentProperties(schema)); | ||
} | ||
}); | ||
} | ||
static matchPath(regex, path, name) { | ||
let matches = regex.exec(path); | ||
if (!matches) { | ||
throw new Error(`Unknown ${name} format: ${path}`); | ||
} | ||
return matches; | ||
} | ||
static parseTypePath(targetPath) { | ||
let collectionMatches = /Collection\((.+)\)/.exec(targetPath); | ||
let matches = CsdlSchema.matchPath( | ||
/([a-z0-9_\.]+)\.([a-z0-9_]+)$/i, | ||
collectionMatches ? collectionMatches[1] : targetPath, | ||
"type path" | ||
); | ||
return { | ||
path: matches[0], | ||
namespace: matches[1], | ||
name: matches[2], | ||
isCollection: !!collectionMatches, | ||
}; | ||
} | ||
static parseModelPath(targetPath) { | ||
let matches = CsdlSchema.matchPath( | ||
/^([a-z0-9_\.]+)\.([a-z0-9_]+)(\/|\/([a-z0-9_]+))?$/i, | ||
targetPath, | ||
"model path" | ||
); | ||
return { | ||
path: matches[0], | ||
namespace: matches[1], | ||
element: matches[2], | ||
subElement: matches[4], | ||
}; | ||
} | ||
} | ||
module.exports = CsdlSchema; |
@@ -92,4 +92,14 @@ "use strict"; | ||
} | ||
/** | ||
* Resolves model path within this type. | ||
* | ||
* @returns {Object} itself | ||
* @memberof Function | ||
*/ | ||
resolveModelPath() { | ||
return this; | ||
} | ||
} | ||
module.exports = Action; |
@@ -17,11 +17,19 @@ "use strict"; | ||
* Creates an instance of EntityType. | ||
* | ||
* @param {Object} rawMetadata raw metadata object for complex type | ||
* @param {EdmxModel} metaModel root point of metadata in-memory structure | ||
* | ||
* @memberof EntityType | ||
*/ | ||
constructor(rawMetadata) { | ||
constructor(rawMetadata, metaModel) { | ||
super(rawMetadata); | ||
let key = this._createKey(); | ||
let key; | ||
Object.defineProperty(this, "key", { | ||
get: () => key, | ||
get: () => { | ||
if (!key) { | ||
key = this._createKey(metaModel); | ||
} | ||
return key; | ||
}, | ||
}); | ||
@@ -46,2 +54,3 @@ } | ||
* @returns {Property[]} array of properties that defines entity key | ||
* @param {EdmxModel} metaModel root point of metadata in-memory structure | ||
* | ||
@@ -52,8 +61,8 @@ * @memberof EntityType | ||
*/ | ||
_createKey() { | ||
if ( | ||
!_.has(this.raw, "Key") || | ||
!_.isArray(this.raw.Key) || | ||
this.raw.Key.length !== 1 | ||
) { | ||
_createKey(metaModel) { | ||
let key; | ||
let baseType = _.get(this, "raw.$.BaseType"); | ||
let keyDefinition = _.get(this, "raw.Key"); | ||
if (!this.isValidKeyDefinition(baseType, keyDefinition)) { | ||
// actually this is true only for base types, derived types inherits the key and can't define key entity | ||
@@ -65,6 +74,33 @@ throw new Error( | ||
return this.raw.Key[0].PropertyRef.map((pr) => this.getProperty(pr.$.Name)); | ||
if (_.isString(baseType)) { | ||
key = metaModel.resolveModelPath(baseType).key; | ||
} else { | ||
key = keyDefinition[0].PropertyRef.map((pr) => | ||
this.getProperty(pr.$.Name) | ||
); | ||
} | ||
return key; | ||
} | ||
/** | ||
* Check if key parameters are correct to build key | ||
* | ||
* @param {String} baseType name of OData service base type | ||
* @param {Object[]} keyDefinition raw key definition | ||
* | ||
* @returns {Boolean} returns true for valid parameters | ||
* | ||
* @memberof EntityType | ||
* | ||
* @private | ||
*/ | ||
isValidKeyDefinition(baseType, keyDefinition) { | ||
return ( | ||
_.isString(baseType) || | ||
(_.isArray(keyDefinition) && keyDefinition.length === 1) | ||
); | ||
} | ||
} | ||
module.exports = EntityType; |
@@ -75,4 +75,14 @@ "use strict"; | ||
} | ||
/** | ||
* Resolves model path within this type. | ||
* | ||
* @returns {Object} itself | ||
* @memberof Function | ||
*/ | ||
resolveModelPath() { | ||
return this; | ||
} | ||
} | ||
module.exports = Function; |
{ | ||
"name": "@sap_oss/odata-library", | ||
"version": "0.9.8", | ||
"version": "0.9.9", | ||
"description": "OData client for testing Netweawer OData services.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
371956
101
9778