//
// $Id: Context.js 9183 2025-11-01 14:14:54Z jochen.hanff $$Id: Context.js 9183 2025-11-01 14:14:54Z jochen.hanff $
// @ts-check
"use strict";
import { RestServices } from "../tools/RestServices.js";
import { ApiResponse } from "../tools/ApiResponse.js";
// import { ProjectServices } from "../infohub/Project.js";
// Note: Avoid static import of Project/ProjectServices to prevent circular deps
/**
@category COLNEO infohub
@classdesc Class to configure access to COLNEO infohub. <br>
Keeps track of user identity, project/scope, and base URLs for services.
*/
export class Context extends EventTarget {
#_projectSrvs = null;
/**
* Supported web services.
*
* HUB | CONFIG | USR | IDP | IAM | RENDER | RPC | TYPES
*
* @example
* ...
* const url = `${ctx.getServiceUrl(cnContext.SERVICE.IDP)}/users/${userid}`;
* @readonly
*
*/
static SERVICE = {
HUB: "hub",
CONFIG: "config",
ADMIN: "admin",
USR: "usr",
IDP: "idp",
IAM: "iam",
RENDER: "render",
RPC: "rpc",
TYPES: "types",
};
static #_servicesnames_ = [
this.SERVICE.HUB,
this.SERVICE.CONFIG,
this.SERVICE.ADMIN,
this.SERVICE.USR,
this.SERVICE.IDP,
this.SERVICE.IAM,
this.SERVICE.RENDER,
this.SERVICE.RPC,
this.SERVICE.TYPES,
];
/**
Get list of names of supported web services.
@example
let names = Infohub.getServiceNames()
@returns {string[]} Array of names as string
@since 08.2025, jh
*/
static getServiceNames() {
return [...this.#_servicesnames_];
}
/**
*
* Create an COLNEO infohub context with service endpoints set to null and empty user state.
* @example
*
* // create context
* const ctx = new cnContext();
*
* ctx.setServiceUrls({
* hub : "https://app.colneo.services/hub",
* admin : "https://app.colneo.services/admin",
* usr : "https://app.colneo.services/usr",
* idp : "https://idp.colneo.services/service"
* });
*
* ctx.setUserAndToken( "user@example.com" , "... jwt_token ...");
* ctx.setScopeAndProject( "cn_c00", "proj_123");
*
* ctx.getScope(); // => "project"
* ctx.getProjectShortId(); // => "proj_123"
* ctx.getUser(); // => "user@example.com"
* ctx.getToken(); // => "jwt_token"
*
* console.log(cntx.getUser()); // => "user@example.com"
*
* @since 08.2025, jh
*
*/
constructor() {
super();
this._userid = null;
this._token = null;
this._serviceurl = {};
for (const srv of Context.getServiceNames()) {
this._serviceurl[srv] = null;
}
this._scope = null;
this._projectShortId = null;
// project object
this._project = null;
}
/**
* Print current context to the console (for debugging).
* @returns {void}
*/
dump() {
console.log("Context: {");
console.log(` USER: ${this._userid}`);
console.log(` TOKEN: ${this._token}`);
console.log(` SCOPE: ${this._scope}`);
console.log(` PROJECT SID : ${this._projectShortId}`);
console.log(` PROJECT: ${this._project}`);
for (const srv of Context.getServiceNames()) {
console.log(` ${srv} = ${this.getServiceUrl(srv)} `);
}
console.log("} Context");
}
/**
* Reset scope and project to null.
* @returns {void}
*/
resetProjectAndScope() {
const changed = this._scope !== null || this._projectShortId !== null ;
this._scope = null;
this._projectShortId = null;
if (changed) {
this.#emitChange({ projectShortId: null, scope: null });
}
}
saveToLocalStorage( cb ) {
return cb({
'status' : 501
})
}
restoreFromLocalStorage( cb ) {
return cb({
'status' : 501
})
}
resetLocalStorage( cb ) {
return cb({
'status' : 501
})
}
/**
*
* Set current user id and authentication token.
*
* @param {string|null} userid
* @param {string|null} token
*
* @returns {void}
*
*/
setUserAndToken(userid, token) {
const changed = this._userid !== userid || this._token !== token;
this._userid = userid;
this._token = token;
if (changed) this.#emitChange({ userid: userid, token: token });
}
/**
*
* Set scope and project short id.
*
* @param {string} scope
* @param {string} project_sid
*
* @returns {void}
*
*/
setScopeAndProjectShortId(scope, project_sid) {
const changed = this._projectShortId !== project_sid || this._scope !== scope;
this._projectShortId = project_sid;
this._scope = scope;
this._project = null; //aab
if (changed) {
this.#emitChange({ projectShortId: project_sid, scope: scope }); //aab
}
}
/**
*
* Get current user id.
*
* @returns {string} User ID (Email)
*
*/
getUserId() {
return this._userid ?? "";
}
/**
*
* Get current access token.
*
* @returns {string}
*
*/
getToken() {
return this._token ?? "";
}
/**
* Get current scope.
* @returns {string}
*/
getScope() {
return this._scope;
}
/**
* Set current scope.
* @param {string} scope
* @returns {void}
*/
setScope(scope) {
if (this._scope === scope) return;
this._scope = scope;
this.#emitChange({ scope: scope }); // no need to check if changed, setScope is only called if scope has changed
}
/**
*
* Set multiple service URLs at once.
*
* @example
*
* const ctx = new cnContext();
*
* ctx.setServiceUrls({
* hub : "https://app.colneo.services/hub",
* admin : "https://app.colneo.services/admin",
* usr : "https://app.colneo.services/usr"
* });
*
* console.log(ctx.getServiceUrl("hub")); // https://app.colneo.services/hub
* console.log(ctx.getServiceUrl("config")); // null (not set yet)
*
* @param {Record<string, string>} cfg - Object mapping service names to URLs
*
* @throws {TypeError} If cfg is not a non-null object
*/
setServiceUrls(cfg) {
if (typeof cfg !== "object" || cfg === null) {
throw new TypeError("cfg must be a non-null object");
}
let changed = false;
for (const [srv, url] of Object.entries(cfg)) {
if (srv in this._serviceurl) {
if (this._serviceurl[srv] !== url) {
this._serviceurl[srv] = url;
changed = true;
}
} else {
console.warn(`Unknown service name: ${srv}`);
}
}
if (changed) this.#emitChange({ serviceurl: cfg });
}
/**
* Set default values for services urls
*/
setServiceUrlsDefault() {
/** @type {Record<string, string>} */
const srv = {}
srv[Context.SERVICE.HUB] = "https://app.colneo.services/hub"
srv[Context.SERVICE.RENDER] = "https://app.colneo.services/render"
srv[Context.SERVICE.ADMIN] = "https://app.colneo.services/admin"
srv[Context.SERVICE.USR] = "https://app.colneo.services/usr"
srv[Context.SERVICE.IDP] = "https://idp.colneo.services/service"
srv[Context.SERVICE.IAM] = "https://app.colneo.services/iam"
this.setServiceUrls(srv)
}
/**
*
* Get URL of supported webservice.
*
* @param {string} servicename
*
* @returns {string | null} Url of service, null if not found
*
* @since 08.2025, jh
*/
getServiceUrl(servicename) {
if (servicename in this._serviceurl) {
return this._serviceurl[servicename];
}
return null;
}
/**
* Set current project short id.
* @param {string} sid
* @returns {void}
*/
setProjectShortId(sid) {
if (this._projectShortId == sid) return;
// console.log( `### /// Context.setProjectShortId( ${sid} )` )
this._projectShortId = sid;
this._project = null; //aab
this.#emitChange({ projectShortId: sid });
}
/**
*
* @returns Project ShortId
* @since 1.0, 09.2025, jh
*/
getProjectShortId() {
return this._projectShortId;
}
/**
* api call to get project data from infohub by short id
* @returns {Promise<object|null>} Returns a Project object or null
*
*/
async getProject() {
const sid = this.getProjectShortId();
if (sid != null) {
if (this._project) {
return this._project
} else {
if( this.#_projectSrvs == null ) {
const { ProjectServices } = await import("./Project.js");
this.#_projectSrvs = new ProjectServices(this);
}
// MUST wrap callback in Promise so await actually waits!
await new Promise((resolve) => {
this.#_projectSrvs.getProjectByShortId(sid, (res) => {
if (res.status < 300) {
this._project = res.getData();
} else {
this._project = null
}
resolve(undefined); // Resolve Promise after callback finishes
});
});
return this._project;
}
} else {
return null;
}
}
/**
* Internal: emit a change notification to contextChanged event. Details are optional.
* @param {object} [details]
* @param {string|null} [details.userid]
* @param {string|null} [details.token]
* @param {string|null} [details.scope]
* @param {string|null} [details.projectShortId]
* @param {Record<string, string>} [details.serviceurl]
* @param {object|null} [details.project] - Project object or null
* @fires Context.contextChanged
* @returns {void}
*/
#emitChange(details_) {
// this.dispatchEvent((new CustomEvent('contextChanged', { detail: details }))); //aab
let e = new CustomEvent("contextChanged", {
detail /** @type {any} */: details_,
});
this.dispatchEvent(e); //aab
}
}
Source