Source

infohub/cnObject.js

//
// $Id$
//

import { RestServices, RestResponse } from "../tools/RestServices.js"
import { cnContext } from "./cnContext.js"
import { ApiResponse } from "../tools/ApiResponse.js"

/**
  
  @category COLNEO infohub
   
  @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 
   */
  setFromJson( obj_as_json ) {
    this.#_data = obj_as_json
  }

  /**
   * 
   *  Get object as JSON in COLNEO Generic Object Model format (cnGOM).
   * 
   *  @returns {object} Object as JSON in GOM format (Generic Object Model)
   * 
   */
  getAsJson() {
    return this.#_data
  }

  /**
   *  Print current object to console.
   * 
   *  @returns {void}
   * 
   */
  dump() {
    if (this.#_data) {
      console.log(JSON.stringify(this.#_data, null, 2))
    } else {
      console.log('-empty-')
    }
  }
  
  
  get id() { return this.getId(); }
  get shortid() { return this.getShortId(); }


  /**
   *  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
  }
    
  /**
  *   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 ) {
    return this.#_data?.properties?.[typeid] ?? defaultValue;
  }

  /**
   * 
   * @param {string} typeid 
   * @param {any} value 
   */
  setPropertyValue(typeid, value) {
    this.#_data ??= {};
    this.#_data.properties ??= {};
    this.#_data.properties[typeid] = value;  
  }

}

/**
 *    
 *  @category 
 *    COLNEO infohub
 * 
 *  @classdesc
 *    Service Class for COLNEO infohub objects/nodes
 * 
 *  @since 
 *    1.0, 09.2025, jh
 * 
 */
export class cnObjectServices {

  #_ctx = null 

   /**
   *   
   * @param {cnContext} 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 use project as a 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
   *  @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 ) {

    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(cnContext.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") )
    }

  }

  /**
   * 
   *  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
   * 
   *  @since
   *    1.0, 09.2025, jh
   * 
   */
  async getObjects(query, cb, context_sid = null) {

    let objects = []

    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(cnContext.SERVICE.HUB)}/${this.#_ctx.getScope()}/objects${q}`;

    console.log( `GET : ${url}`)

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////    
    /* RestResponse */ const resp = await RestServices.makeApiCall(this.#_ctx.getToken(), RestServices.METHODS.GET, url)
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    return cb( resp )

  }

}