//
// $Id$
//
import { RestServices, RestResponse } from "../tools/RestServices.js"
import { Context } from "./../infohub/Context.js"
import { ApiResponse } from "../tools/ApiResponse.js"
/**
*
* @category COLNEO gom <br>
* <font size="6pt">Generic Object Model</font>
*
* @classdesc Base Class for nodes/objects on COLNEO infohub. <br>
Uses the infohub GOM (Generic Object Model)
*/
export class cnObject {
// GOM json data
#_data = null
/**
*
*
* @since 08.2025, jh
*
*/
constructor() {
}
/**
* Set object from JSON in COLNEO Generic Object Model (GOM) format.
*
* @param {object} obj_as_data
* @param {Array|null} members
* Take only members into account, if null use all members. <br>
*
*/
setFromJson( obj_as_json , members = null ) {
if( members == null ) {
this.#_data = obj_as_json
} else {
this.#_data = {}
for( const m of members ) {
this.#_data[m] = obj_as_json[m]
}
}
}
/**
*
* Get object as JSON in COLNEO Generic Object Model format (cnGOM).
*
* @param {Array|null} members
* Take only members into account, if null use all members. <br>
* Example:
* o.getAsJson( [ 'info' , 'properties' ] )
*
* @returns {object} Object as JSON in GOM format (Generic Object Model)
*
*/
getAsJson( members = null ) {
if( members ) {
let retobj = {}
for ( const m of members ) {
retobj[m] = this.#_data[m]
}
return retobj
} else {
return this.#_data
}
}
/**
* Print current object to console.
*
* @returns {void}
*
*/
dump( prefix = '' ) {
if( prefix.length > 0 ) {
prefix = prefix + ' '
}
if (this.#_data) {
console.log(`${prefix}data:`)
console.log(JSON.stringify(this.#_data, null, 2))
} else {
console.log('-empty-')
}
}
/**
* Global unique ID
*/
get id() { return this.getId(); }
/**
* Node ID / Short ID
*/
get shortid() { return this.getShortId(); }
get name() { return this.getName(); }
set name(value) {
this.setName(value)
}
/**
* Get ID
*
* @returns {string|null} Object ID, null if object is empty
*
* @since 1.0, 09.2025, jh
*
*/
getId() {
return this.#_data?.info?.object_id
}
/**
*
* Retrieves the short object ID from the internal json object.
*
* @returns {string|null} The short ID if available, otherwise null.
*
* @since 1.0, 09.2025, jh
*
*/
getShortId() {
return this.#_data?.info?.shortid
}
/**
*
* Get 'info.object_name'
*
* @returns info.object_name
*/
getName() {
return this.#_data?.info?.object_name
}
/**
* Set info.object_name
*
* @param {string} n Set object_name to n
*
*/
setName( n ) {
this.#_data.info.object_name = n
}
/**
* Retrieves a top-level member of the internal #_data object by key.
*
* Common keys include: "info", "properties", "geometry", etc.
*
* @param {string} key - The name of the member to retrieve.
* @param {*} [defaultValue={}] - Value returned if the key does not exist.
* @returns {*} The value of the specified member, or the defaultValue if missing.
*/
get(key, defaultValue = {} ) {
return this.#_data?.[key] ?? defaultValue;
}
/**
*
* Retrieves the value of a property from the internal json object.
*
* @param {string} typeid - The property key to retrieve.
* @param {*} [defaultValue=null] - A fallback value returned if the property does not exist.
*
* @returns {*} The property value if found, otherwise the defaultValue.
*
*/
getPropertyValue( typeid , defaultValue = null ) {
console.log("### <getPropertyValue()>")
// return this.#_data?.properties?.[typeid] ?? defaultValue;
console.log( JSON.stringify(this.#_data) )
console.log( JSON.stringify(this.#_data.properties) )
let v = this.#_data.properties[typeid]
console.log( `return typeid = ${typeid}`)
console.log( `return v = ${v}`)
return v
// return (this.#_data && this.#_data.properties && this.#_data.properties[typeid]) ?? defaultValue;
}
/**
*
* Set the property value to this object.<br>
* Value is not syncronised with infohub.
*
* @param {string} typeid
* @param {any} value
*/
setPropertyValue(typeid, value) {
this.#_data ??= {};
this.#_data.properties ??= {};
this.#_data.properties[typeid] = value;
}
}
// ###################################################################################################
/**
*
* @category COLNEO gom <br>
* <font size="6pt">Generic Object Model</font>
*
* @classdesc
* Service Class for COLNEO infohub objects/nodes
*
* @since
* 1.0, 09.2025, jh
*
*/
export class cnObjectServices {
#_ctx = null
/**
*
* @param {Context} ctx Infohub Context
*
* @since 08.2025, jh
*
*/
constructor( ctx ) {
this.#_ctx = ctx
}
/**
*
* Fetch an object by ID and (optional) context. <br>
* If context node is null the current project as used as context (root node for search).
*
* @param {string} object_id - The ID of the object to retrieve
* @param {function} cb - Callback function that receives the result object,
* {
* data: cnObject
* }
* @param {string} [context_sid=null] - Optional session or context identifier
*
* @returns {Promise<ApiResponse>} Since this uses a callback, the function itself resolves with no value
*
* @since
* 1.0, 09.2025, jh
*
*/
async getObject( object_id , cb , context_sid = null ) {
console.log( '### <cnObjectservices.getObject()> ')
try {
let q = ''
let c = ''
if (context_sid) {
q += `context=$node ~eq~ '${context_sid}'`
} else {
q += `context=$node ~eq~ '${this.#_ctx.getProjectShortId()}'`
}
c = '&'
let f = `$object_id ~eq~ '${object_id}' `
q += c + `filter=${f}`
//
// RESOLVE MEMBERS
const url = `${this.#_ctx.getServiceUrl(Context.SERVICE.HUB)}/${this.#_ctx.getScope()}/objects?${q}`;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* RestResponse */ const resp = await RestServices.makeApiCall( this.#_ctx.getToken() , RestServices.METHODS.GET , url )
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if ( resp.getStatus() < 300 ) {
let data = resp.getData()
if (data.length == 1) {
// console.log("DATA:")
// console.log(JSON.stringify(data))
let obj = new cnObject()
obj.setFromJson( data[0] )
return cb( new ApiResponse( 200 , obj ) )
} else {
return cb( new ApiResponse( 406 , null , `${data.length} objects found with given ID = [${object_id}].`) )
}
} else {
return cb( resp )
}
} catch (error) {
// console.error("getObject failed:", error);
return cb( new ApiResponse( 400 , null , "Unknown error") )
}
}
/**
*
* Delete Object including all its versions in parent object 'parent_sid'.
*
* @param {string} object_id
* @param {string} parent_sid
* @param {function} cb
*
* @returns {Promise<ApiResponse>} Since this uses a callback, the function itself resolves with no value
* @since 1.0, 09.2025, jh
*
*/
async deleteObject( object_id, parent_sid, cb ) {
let q = `object_id=${object_id}`
const url = `${this.#_ctx.getServiceUrl(Context.SERVICE.HUB)}/${this.#_ctx.getScope()}/objects/${parent_sid}/object?${q}`;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* RestResponse */ const resp = await RestServices.makeApiCall(this.#_ctx.getToken(), RestServices.METHODS.DELETE, url)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return cb(resp)
}
/**
* Save object on infohub.
*
* Take 'info' block of object (GOM) into account.
* <br>
* Use object_id to create a new or update an existing object, <br>
* Use parent_shortid as parent to create a new object or identify an existing object. <br>
* If also parent_shortid is missing, use project_shortid or catalogue_shortid as parent. <br>
* In any other cases the object is created in the current project stored in the Context object of this object service.
*
* PUT - /{scope}/objects/{parent}/object
*
* @param {cnObject} cnObj
* @param {Array|null} members
* Save/Write these members which is one of [ 'info' , 'properties' , etc. ] <br>
* If null, write all members
*
* @param {function} cb
*
* @returns {Promise<ApiResponse>} Since this uses a callback, the function itself resolves with no value
* @since 1.0, 09.2025, jh
*
*/
async saveObject( cnObj , members , cb ) {
let parent_sid = cnObj.get('info')['parent_shortid']
if (parent_sid == undefined) {
parent_sid = cnObj.get('info')['project_shortid']
if (parent_sid == undefined) {
parent_sid = cnObj.get('info')['catalogue_shortid']
}
if (parent_sid == undefined) {
parent_sid = this.#_ctx.getProjectShortId()
}
}
// create object/node ...
let object_data = cnObj.getAsJson( members )
const url = `${this.#_ctx.getServiceUrl(Context.SERVICE.HUB)}/${this.#_ctx.getScope()}/objects/${parent_sid}/object`;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* RestResponse */ const resp = await RestServices.makeApiCall(this.#_ctx.getToken(), RestServices.METHODS.PUT, url, object_data )
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// resp.dump("RETURN UPDATE OBJECT")
if( resp.status < 300 ) {
let obj = new cnObject()
obj.setFromJson( resp.data )
return cb(new ApiResponse(resp.status, obj))
} else {
return cb(resp)
}
}
/**
*
* Create new object on infohub.
* <br>
* Create a new object if object_id (in 'info') does not exist, create a new version if object_id exist. <br>
* Function uses endpoint `POST .../{scope}/nodes/{node}` on service 'hub'.
*
* @param {object} object_data
* Object in GOM format. <br>
* Contains object_id in info block.
*
* @param {string} parent_sid
* @param {function} cb Returns AüiResponse object with an cnObject as data if REST call has been sucessful
*
* @returns {Promise<ApiResponse>}
* Since this uses a callback, the function itself resolves with no value
*
* @since 1.0, 09.2025, jh
*
*/
async createObject(object_data, parent_sid, cb) {
// /{scope}/nodes/{node}
const url = `${this.#_ctx.getServiceUrl(Context.SERVICE.HUB)}/${this.#_ctx.getScope()}/nodes/${parent_sid}`;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* RestResponse */ const resp = await RestServices.makeApiCall(this.#_ctx.getToken(), RestServices.METHODS.POST, url, object_data )
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// resp.dump("RETURN CREATE OBJECT")
if( resp.status < 300 ) {
let obj = new cnObject()
obj.setFromJson( resp.data )
return cb(new ApiResponse(resp.status, obj))
} else {
return cb(resp)
}
// return cb(resp)
}
/**
*
* Factory method to instantiate a new object of class cnObject. <br>
* The object is not created or written to infohub. To save the object on infohub
* use method saveObject().
*
* @param {object} node_json Object as json in GOM format
*
* @returns {cnObject} New Object
*
*/
newObjectFromJson( node_json , members = null ) {
let obj = new cnObject()
obj.setFromJson(node_json,members)
return obj
}
/**
*
* Fetch objects by query and (optional) context. <br>
* If context node is null use project as a context (root node for search).
*
* @param {object} query - Query for search
* @example
* {
*
* "object_list" : [ 'objid1' , 'objid2' , ... ] // optional
*
* // parameters of webservice
* "filter" :
* "members" :
* "properties" :
* "geometry" :
* "nodes" :
* "relations" :
* "relations_right" :
* }
*
* @param {function} cb - Callback function that receives the result object
* @param {string} [context_sid=null] - Optional session or context identifier
*
* @returns {Promise<ApiResponse>} Since this uses a callback, the function itself resolves with no value. <br>
* Array<cnObject>
*
* @since
* 1.0, 09.2025, jh
*
*/
async getObjects( query, cb, context_sid = null) {
let q = ''
let c = '?'
if (context_sid) {
q += c + `context=$node ~eq~ '${context_sid}'`
} else {
q += c + `context=$node ~eq~ '${this.#_ctx.getProjectShortId()}'`
}
c = '&'
let filter = ''
if (query['object_list'] && query['object_list'].length > 0) {
filter = "$object_id ~in~ [ "
filter += " '" + query['object_list'][0] + "' "
for (let i = 1; i < query['object_list'].length; i++) {
filter += " , '" + query['object_list'][i] + "' "
}
filter += " ] "
}
if (query['filter']) {
if (filter.length > 0) {
filter += ' $AND '
}
q += c + 'filter=' + encodeURIComponent(filter + query['filter'])
c = '&'
} else {
if (filter.length > 0) {
q += c + 'filter=' + encodeURIComponent(filter)
c = '&'
}
}
if (query['members']) {
let plist = query['members'].join(',')
q += c + 'members=' + encodeURIComponent(plist)
c = '&'
}
const url = `${this.#_ctx.getServiceUrl(Context.SERVICE.HUB)}/${this.#_ctx.getScope()}/objects${q}`;
console.log( `GET : ${url}`)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* RestResponse */ const resp = await RestServices.makeApiCall(this.#_ctx.getToken(), RestServices.METHODS.GET, url)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (resp.status < 300) {
let array = []
for (const obj_json of resp.data) {
let obj = new cnObject()
obj.setFromJson(obj_json)
array.push(obj)
}
return cb(new ApiResponse(resp.status, array))
} else {
return cb( resp )
}
}
/**
*
* Get path of node hierarchy from root to node.
*
* @param {*} node_sid
* @param {*} query
* @param {*} cb
* @returns {Promise<ApiResponse>}
*/
async getNodePath( node_sid , query , cb ) {
let q = ''
let c = '?'
if (query['members']) {
let plist = query['members'].join(',')
q += c + 'members=' + encodeURIComponent(plist)
c = '&'
}
const url = `${this.#_ctx.getServiceUrl(Context.SERVICE.HUB)}/${this.#_ctx.getScope()}/nodes/${node_sid}/path/data${q}`;
console.log( `GET : ${url}`)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* RestResponse */ const resp = await RestServices.makeApiCall(this.#_ctx.getToken(), RestServices.METHODS.GET, url)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (resp.status < 300) {
let array = []
for (const obj_json of resp.data) {
let obj = new cnObject()
obj.setFromJson(obj_json)
array.push(obj)
}
return cb(new ApiResponse(resp.status, array))
} else {
return cb( resp )
}
}
/**
*
* Set property values in propertylist for node (shortid) on infohub. <br>
*
*
* @param {*} node_sid Node ShortID
*
* @param {*} propertylist = [
* {
* 'typeid' : "abc##xs:string",
* 'value' : "123 abc",
* 'comment' : "Gesetzt durch xyz"
* }
* ]
*
* @param {*} cb Call Back {
* ApiResponse<>
* }
*
* @param {*} cmt Comment (optional)
*
* @returns {Promise<ApiResponse>} Since this uses a callback, the function itself resolves with no value. <br>
*
* @since
* 1.0, 09.2025, jh
*
*/
async setPropertyValues( node_sid , propertylist , cb ) {
const url = `${this.#_ctx.getServiceUrl(Context.SERVICE.HUB)}/${this.#_ctx.getScope()}/nodes/${node_sid}/properties`;
// const reqBody = [
// {
// 'type' : typeid,
// 'value' : val,
// 'comment' : cmt
// }
// ]
const reqBody = propertylist
// console.log( `POST : ${url}`)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* RestResponse */ const resp = await RestServices.makeApiCall(this.#_ctx.getToken(), RestServices.METHODS.POST , url , reqBody )
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return cb( resp )
}
}
Source