diff --git a/.gitlab/common.yml b/.gitlab/common.yml index f24889144..588a1990f 100644 --- a/.gitlab/common.yml +++ b/.gitlab/common.yml @@ -161,41 +161,6 @@ reports: dotenv: build.env -#.image_check: -# tags: -# - docker -# script: -# - | -# BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]') -# docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY_TOKEN}" -# FORCE_BUILD="FALSE" -# set +e -# docker pull --quiet "${REGISTRY}/${PROJECT}/${COMPONENT}-${BRANCH_LOWER}:latest" -# if [ $? -eq 0 ]; then echo "Image exists"; else FORCE_BUILD="TRUE"; fi; -# set -e -# if [ "${BUILD_INTERMEDIATE}" == "TRUE" ] -# then -# set +e -# docker pull --quiet "${REGISTRY}/${PROJECT}/${COMPONENT}-${INTERMEDIATE_LAYER_NAME}-${BRANCH_LOWER}:latest" -# if [ $? -eq 0 ]; then echo "Image exists"; else FORCE_BUILD="TRUE"; fi; -# set -e -# fi -# if [ "$FORCE_BUILD" == "TRUE" ] -# then -# cp .gitlab/build/force_build_${COMPONENT}_image.yml ${COMPONENT}_image.yml -# else -# cp .gitlab/build/build_${COMPONENT}_image.yml ${COMPONENT}_image.yml -# fi -# echo "REGISTRY=${REGISTRY}" >> build.env -# echo "HARBOR_USER=${HARBOR_USER}" >> build.env -# echo "HARBOR_DATAFED_GITLAB_CI_REGISTRY_TOKEN=${HARBOR_DATAFED_GITLAB_CI_REGISTRY_TOKEN}" >> build.env -# sed -i 's/\(HARBOR_USER=.*\)\$/\1$$/g' build.env -# artifacts: -# paths: -# - ${COMPONENT}_image.yml -# reports: -# dotenv: build.env - # The purpose of this anchor is to check that an image has been uploaded correctly # to the registry and if it has not attempt to upload it again. # diff --git a/common/proto3/common/auth/query_update_request.proto b/common/proto3/common/auth/query_update_request.proto index 518a93ce7..01092275d 100644 --- a/common/proto3/common/auth/query_update_request.proto +++ b/common/proto3/common/auth/query_update_request.proto @@ -10,4 +10,5 @@ message QueryUpdateRequest { string id = 1; optional string title = 2; SearchRequest query = 3; + bool replace_query = 4; } diff --git a/common/proto3/common/enums/dependency_dir.proto b/common/proto3/common/enums/dependency_dir.proto index 1cd722921..cf3b2f49a 100644 --- a/common/proto3/common/enums/dependency_dir.proto +++ b/common/proto3/common/enums/dependency_dir.proto @@ -5,6 +5,6 @@ package SDMS; option cc_enable_arenas = true; enum DependencyDir { - DEPENDENCY_DIR_IN = 0; - DEPENDENCY_DIR_OUT = 1; + DIR_IN = 0; + DIR_OUT = 1; } diff --git a/common/proto3/common/enums/dependency_type.proto b/common/proto3/common/enums/dependency_type.proto index 795eeff79..16010e48a 100644 --- a/common/proto3/common/enums/dependency_type.proto +++ b/common/proto3/common/enums/dependency_type.proto @@ -5,7 +5,7 @@ package SDMS; option cc_enable_arenas = true; enum DependencyType { - DEPENDENCY_TYPE_IS_DERIVED_FROM = 0; - DEPENDENCY_TYPE_IS_COMPONENT_OF = 1; - DEPENDENCY_TYPE_IS_NEW_VERSION_OF = 2; + DEP_IS_DERIVED_FROM = 0; + DEP_IS_COMPONENT_OF = 1; + DEP_IS_NEW_VERSION_OF = 2; } diff --git a/common/proto3/common/enums/execution_method.proto b/common/proto3/common/enums/execution_method.proto index dff667350..2cd3ef0b7 100644 --- a/common/proto3/common/enums/execution_method.proto +++ b/common/proto3/common/enums/execution_method.proto @@ -5,7 +5,7 @@ package SDMS; option cc_enable_arenas = true; enum ExecutionMethod { - EXECUTION_METHOD_UNSPECIFIED = 0; - EXECUTION_METHOD_DIRECT = 1; - EXECUTION_METHOD_DEFERRED = 2; + EXEC_UNSPECIFIED = 0; + DIRECT = 1; + DEFERRED = 2; } diff --git a/core/database/foxx/api/acl_router.js b/core/database/foxx/api/acl_router.js index 995b18020..23fad8393 100644 --- a/core/database/foxx/api/acl_router.js +++ b/core/database/foxx/api/acl_router.js @@ -234,7 +234,7 @@ router router .get("/view", function (req, res) { - let rules = null; + let rules = []; try { logger.logRequestStarted({ client: req.queryParams.client, @@ -296,7 +296,7 @@ router .description("View current ACL on an object (data record or collection)"); router .get("/shared/list", function (req, res) { - let result = null; + let result = []; try { const client = g_lib.getUserFromClientID(req.queryParams.client); result = g_lib.getACLOwnersBySubject( diff --git a/core/database/foxx/api/admin_router.js b/core/database/foxx/api/admin_router.js index 8ef49ce9d..6cadf2b57 100644 --- a/core/database/foxx/api/admin_router.js +++ b/core/database/foxx/api/admin_router.js @@ -58,7 +58,7 @@ router let result = null; try { logger.logRequestStarted({ - client: "N/A", + client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], httpVerb: "GET", routePath: basePath + "/test", @@ -82,7 +82,7 @@ router time: (t2 - t1) / 1000, }); logger.logRequestSuccess({ - client: "N/A", + client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], httpVerb: "GET", routePath: basePath + "/test", @@ -92,13 +92,13 @@ router }); } catch (e) { logger.logRequestFailure({ - client: "N/A", + client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], httpVerb: "GET", routePath: basePath + "/test", status: "Failure", description: "Do perf test", - extra: { execution_time_seconds: (t2 - t1) / 1000 }, + extra: { execution_time_seconds: 0 }, error: e, }); g_lib.handleException(e, res); diff --git a/core/database/foxx/api/coll_router.js b/core/database/foxx/api/coll_router.js index ee37f4739..c5ab01b77 100644 --- a/core/database/foxx/api/coll_router.js +++ b/core/database/foxx/api/coll_router.js @@ -43,10 +43,11 @@ router var owner = client, parent_id; + let owner_id = owner._id; if (req.body.parent) { parent_id = g_lib.resolveCollID(req.body.parent, client); - var owner_id = g_db.owner.firstExample({ + owner_id = g_db.owner.firstExample({ _from: parent_id, })._to; if (owner_id != client._id) { diff --git a/core/database/foxx/api/config_router.js b/core/database/foxx/api/config_router.js index 0d94b2eaa..37b4d18e3 100644 --- a/core/database/foxx/api/config_router.js +++ b/core/database/foxx/api/config_router.js @@ -48,7 +48,7 @@ router routePath: basePath + "/msg/daily", status: "Failure", description: "Get message of the day", - extra: (msg.msg || "").substring(0, 10), + extra: ((msg && msg.msg) || "").substring(0, 10), error: e, }); diff --git a/core/database/foxx/api/data_router.js b/core/database/foxx/api/data_router.js index 078448e5a..457a95831 100644 --- a/core/database/foxx/api/data_router.js +++ b/core/database/foxx/api/data_router.js @@ -515,7 +515,8 @@ function recordUpdate(client, record, result) { perms |= permissions.PERM_WR_REC; } - if (data.locked || !g_lib.hasPermissions(client, data, perms)) throw error.ERR_PERM_DENIED; + if (data.locked || !permissions.hasPermissions(client, data, perms)) + throw error.ERR_PERM_DENIED; } var owner_id = g_db.owner.firstExample({ @@ -646,14 +647,14 @@ function recordUpdate(client, record, result) { for (i in data.tags) { tag = data.tags[i]; - if (!(tag in record.tags)) { + if (!record.tags.includes(tag)) { rem_tags.push(tag); } } for (i in record.tags) { tag = record.tags[i]; - if (!(tag in data.tags)) { + if (!data.tags.includes(tag)) { add_tags.push(tag); } } @@ -707,7 +708,7 @@ function recordUpdate(client, record, result) { } } - if (record.deps != undefined && (record.deps_add != undefined || record.deps_rem != undefined)) + if (record.deps != undefined && (record.dep_add != undefined || record.dep_rem != undefined)) throw [error.ERR_INVALID_PARAM, "Cannot use both dependency set and add/remove."]; var dep, @@ -1049,7 +1050,7 @@ router httpVerb: "POST", routePath: basePath + "/update/batch", status: "Failure", - description: `Update a batch of existing data record. RecordIDs: ${displayIds}`, + description: `Update a batch of existing data record. RecordIDs: ${displayedIds}`, extra: { count: totalCount, }, @@ -1515,7 +1516,7 @@ router router .get("/dep/graph/get", function (req, res) { - let result = null; + let result = []; try { logger.logRequestStarted({ client: req.queryParams.client, @@ -1541,7 +1542,6 @@ router notes, gen = 0; - result = []; // Get Ancestors //console.log("get ancestors"); @@ -1825,6 +1825,7 @@ router */ router .get("/path", function (req, res) { + let path = null; try { logger.logRequestStarted({ client: req.queryParams.client, @@ -1856,7 +1857,7 @@ router "Can only access data from '" + repo.domain + "' domain", ]; - var path = g_lib.computeDataPath(loc, true); + path = g_lib.computeDataPath(loc, true); res.send({ path: path, }); @@ -2389,6 +2390,15 @@ router router .post("/delete", function (req, res) { var retry = 10; + let ids = []; + logger.logRequestStarted({ + client: req.queryParams.client, + correlationId: req.headers["x-correlation-id"], + httpVerb: "POST", + routePath: basePath + "/delete", + status: "Started", + description: `Attempting to delete a total of: ${req.body.ids.length}`, + }); for (;;) { try { @@ -2416,10 +2426,11 @@ router }, action: function () { const client = g_lib.getUserFromClientID(req.queryParams.client); - var i, - id, - ids = []; + var i, id; + // Needs to be reinitialized to an empty array to avoid + // accumulating content from retries + ids = []; for (i in req.body.ids) { id = g_lib.resolveDataCollID(req.body.ids[i], client); ids.push(id); @@ -2430,8 +2441,30 @@ router res.send(result); }, }); + const preview = ids.slice(0, 5).join(", "); + const idSummary = ids.length > 5 ? `${preview}, ...` : preview; + logger.logRequestSuccess({ + client: req.queryParams.client, + correlationId: req.headers["x-correlation-id"], + httpVerb: "POST", + routePath: basePath + "/delete", + status: "Success", + description: `Delete data items: ${idSummary}...`, + extra: { count: ids.length }, + }); + break; } catch (e) { + logger.logRequestFailure({ + client: req.queryParams.client, + correlationId: req.headers["x-correlation-id"], + httpVerb: "POST", + routePath: basePath + "/delete", + status: "Failure", + description: `Attempting to delete a total of: ${req.body.ids.length}`, + extra: { retry_attempt: retry }, + error: e, + }); if (--retry == 0 || !e.errorNum || e.errorNum != 1200) { g_lib.handleException(e, res); } diff --git a/core/database/foxx/api/group_router.js b/core/database/foxx/api/group_router.js index 6a49bf3b6..9c80ccbb3 100644 --- a/core/database/foxx/api/group_router.js +++ b/core/database/foxx/api/group_router.js @@ -440,7 +440,6 @@ router extra: logExtra, }); } catch (e) { - res.send(groups); logger.logRequestFailure({ client: client?._id, correlationId: req.headers["x-correlation-id"], diff --git a/core/database/foxx/api/metrics_router.js b/core/database/foxx/api/metrics_router.js index 66c762436..76dc05e68 100644 --- a/core/database/foxx/api/metrics_router.js +++ b/core/database/foxx/api/metrics_router.js @@ -73,7 +73,7 @@ router router .get("/msg_count", function (req, res) { let client = null; - let result = null; + let result = []; try { client = g_lib.getUserFromClientID(req.queryParams.client); logger.logRequestStarted({ @@ -147,7 +147,7 @@ router router .get("/users/active", function (req, res) { let client = null; - let cnt = null; + let cnt = {}; try { client = req.queryParams.client ? g_lib.getUserFromClientID(req.queryParams.client) @@ -161,7 +161,6 @@ router description: "Get recently active users from metrics", }); - cnt = {}; var u, r, qryres = g_db diff --git a/core/database/foxx/api/note_router.js b/core/database/foxx/api/note_router.js index cefbcdff7..3fad32187 100644 --- a/core/database/foxx/api/note_router.js +++ b/core/database/foxx/api/note_router.js @@ -180,9 +180,10 @@ router }), old_state = note.state, old_type = note.type, - doc = g_db._document(ne._from), updates = {}; + doc = g_db._document(ne._from); + /* Permissions to update: Currently any admin of the subject and the creator of the annotation may make edits to the annotation. This approach is optimistic in assuming that conflicts will not arise and all parties are ethical. Eventually a mechanism will be put in place to deal with conflicts and @@ -412,7 +413,7 @@ router req.queryParams.id + " Comment ID:" + req.queryParams.comment_idx, - extra: note.new, + extra: note, error: e, }); g_lib.handleException(e, res); diff --git a/core/database/foxx/api/proj_router.js b/core/database/foxx/api/proj_router.js index 47f3d0d53..b8ccd3a68 100644 --- a/core/database/foxx/api/proj_router.js +++ b/core/database/foxx/api/proj_router.js @@ -468,11 +468,11 @@ router status: "Failure", description: `Update project information. Project ID: ${req.queryParams.id}`, extra: { - owner: proj.new?.owner, - title: proj.new?.title - ? proj.new?.title.length > 15 - ? proj.new?.title.slice(0, 15) + "…" - : proj.new?.title + owner: proj?.new?.owner, + title: proj?.new?.title + ? proj?.new?.title.length > 15 + ? proj?.new?.title.slice(0, 15) + "…" + : proj?.new?.title : undefined, }, @@ -601,208 +601,167 @@ router router .get("/list", function (req, res) { - logger.logRequestStarted({ - client: req.queryParams.client, - correlationId: req.headers["x-correlation-id"], - httpVerb: "GET", - routePath: basePath + "/list", - status: "Started", - description: `List projects`, - }); - - const client = g_lib.getUserFromClientID(req.queryParams.client); - var qry, - result, - count = - (req.queryParams.as_owner ? 1 : 0) + - (req.queryParams.as_admin ? 1 : 0) + - (req.queryParams.as_member ? 1 : 0); - - if (count) { - var comma = false; - - if (count > 1) qry = "for i in union(("; - else qry = ""; - - if (req.queryParams.as_owner) { - qry += "for i in 1..1 inbound @user owner filter IS_SAME_COLLECTION('p',i)"; - if (count > 1) qry += " return { _id: i._id, title: i.title, owner: i.owner }"; - comma = true; - } - - if (!count || req.queryParams.as_admin) { - qry += - (comma ? "),(" : "") + - "for i in 1..1 inbound @user admin filter IS_SAME_COLLECTION('p',i)"; - if (count > 1) - qry += " return { _id: i._id, title: i.title, owner: i.owner, creator: @user }"; - comma = true; - } - - if (req.queryParams.as_member) { - qry += - (comma ? "),(" : "") + - "for i,e,p in 2..2 inbound @user member, outbound owner filter p.vertices[1].gid == 'members'"; - if (count > 1) qry += " return { _id: i._id, title: i.title, owner: i.owner }"; - } - - if (count > 1) qry += "))"; - } else { - qry = "for i in p"; - } - - qry += " sort i."; - - switch (req.queryParams.sort) { - case g_lib.SORT_ID: - qry += "_id"; - break; - case g_lib.SORT_TITLE: - qry += "title"; - break; - case g_lib.SORT_TIME_CREATE: - qry += "ct"; - break; - case g_lib.SORT_TIME_UPDATE: - qry += "ut"; - break; - default: - qry += "_id"; - break; - } - - if (req.queryParams.sort_rev) qry += " desc"; - - var user_id; - if (req.queryParams.subject) { - permissions.ensureAdminPermUser(client, req.queryParams.subject); - } else user_id = client._id; - - if (req.queryParams.offset != undefined && req.queryParams.count != undefined) { - qry += " limit " + req.queryParams.offset + ", " + req.queryParams.count; - qry += " return { id: i._id, title: i.title, owner: i.owner, creator: i.creator }"; - //console.log("proj list qry:",qry); - result = g_db._query( - qry, - count - ? { - user: user_id, - } - : {}, - {}, - { - fullCount: true, - }, - ); - var tot = result.getExtra().stats.fullCount; - result = result.toArray(); - result.push({ - paging: { - off: req.queryParams.offset, - cnt: req.queryParams.count, - tot: tot, - }, - }); - } else { - qry += " return { id: i._id, title: i.title, owner: i.owner, creator: i.creator }"; - //console.log("proj list qry:",qry); - result = g_db._query( - qry, - count - ? { - user: user_id, - } - : {}, - ); - } - - //res.send( g_db._query( qry, { user: client._id })); - res.send(result); - logger.logRequestSuccess({ - client: req.queryParams.client, - correlationId: req.headers["x-correlation-id"], - httpVerb: "GET", - routePath: basePath + "/list", - status: "Success", - description: `List projects`, - extra: { NumOfProjs: tot }, - }); - }) - .queryParam("client", joi.string().required(), "Client ID") - .queryParam("subject", joi.string().optional(), "Subject (user) ID") - .queryParam("as_owner", joi.bool().optional(), "List projects owned by client/subject") - .queryParam("as_admin", joi.bool().optional(), "List projects administered by client/subject") - .queryParam( - "as_member", - joi.bool().optional(), - "List projects where client is a member/subject", - ) - .queryParam("sort", joi.number().optional(), "Sort field (default = id)") - .queryParam("sort_rev", joi.bool().optional(), "Sort in reverse order") - .queryParam("offset", joi.number().optional(), "Offset") - .queryParam("count", joi.number().optional(), "Count") - .summary("List projects") - .description( - "List projects. If no options are provided, lists all projects associated with client.", - ); - -router - .get("/search", function (req, res) { - let result = null; - let extra_log = null; - const rawQuery = typeof req.queryParams.query === "string" ? req.queryParams.query : ""; - const safeQuerySnippet = - rawQuery.length > 200 ? rawQuery.slice(0, 200) + "…[truncated]" : rawQuery; + let tot = null; try { logger.logRequestStarted({ client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], httpVerb: "GET", - routePath: basePath + "/search", + routePath: basePath + "/list", status: "Started", - description: `Find all projects that match query: ${safeQuerySnippet}`, + description: `List projects`, }); - g_lib.getUserFromClientID(req.queryParams.client); + const client = g_lib.getUserFromClientID(req.queryParams.client); + var qry, + result, + count = + (req.queryParams.as_owner ? 1 : 0) + + (req.queryParams.as_admin ? 1 : 0) + + (req.queryParams.as_member ? 1 : 0); + + if (count) { + var comma = false; + + if (count > 1) qry = "for i in union(("; + else qry = ""; + + if (req.queryParams.as_owner) { + qry += "for i in 1..1 inbound @user owner filter IS_SAME_COLLECTION('p',i)"; + if (count > 1) qry += " return { _id: i._id, title: i.title, owner: i.owner }"; + comma = true; + } + + if (!count || req.queryParams.as_admin) { + qry += + (comma ? "),(" : "") + + "for i in 1..1 inbound @user admin filter IS_SAME_COLLECTION('p',i)"; + if (count > 1) + qry += + " return { _id: i._id, title: i.title, owner: i.owner, creator: @user }"; + comma = true; + } - result = g_db._query(req.queryParams.query, {}); - res.send(result); - extra_log = { - documents: result._documents ? result._documents.slice(0, 10) : [], // first 10 IDs only - countTotal: result?._countTotal, - countQuery: result?._countQuery, - skip: result?._skip, - limit: result?._limit, - cached: result?._cached, - }; + if (req.queryParams.as_member) { + qry += + (comma ? "),(" : "") + + "for i,e,p in 2..2 inbound @user member, outbound owner filter p.vertices[1].gid == 'members'"; + if (count > 1) qry += " return { _id: i._id, title: i.title, owner: i.owner }"; + } + if (count > 1) qry += "))"; + } else { + qry = "for i in p"; + } + + qry += " sort i."; + + switch (req.queryParams.sort) { + case g_lib.SORT_ID: + qry += "_id"; + break; + case g_lib.SORT_TITLE: + qry += "title"; + break; + case g_lib.SORT_TIME_CREATE: + qry += "ct"; + break; + case g_lib.SORT_TIME_UPDATE: + qry += "ut"; + break; + default: + qry += "_id"; + break; + } + + if (req.queryParams.sort_rev) qry += " desc"; + + let user_id; + if (req.queryParams.subject) { + permissions.ensureAdminPermUser(client, req.queryParams.subject); + user_id = req.queryParams.subject; + } else user_id = client._id; + + if (req.queryParams.offset != undefined && req.queryParams.count != undefined) { + qry += " limit " + req.queryParams.offset + ", " + req.queryParams.count; + qry += " return { id: i._id, title: i.title, owner: i.owner, creator: i.creator }"; + //console.log("proj list qry:",qry); + result = g_db._query( + qry, + count + ? { + user: user_id, + } + : {}, + {}, + { + fullCount: true, + }, + ); + tot = result.getExtra().stats.fullCount; + result = result.toArray(); + result.push({ + paging: { + off: req.queryParams.offset, + cnt: req.queryParams.count, + tot: tot, + }, + }); + } else { + qry += " return { id: i._id, title: i.title, owner: i.owner, creator: i.creator }"; + //console.log("proj list qry:",qry); + result = g_db._query( + qry, + count + ? { + user: user_id, + } + : {}, + ); + } + + //res.send( g_db._query( qry, { user: client._id })); + res.send(result); logger.logRequestSuccess({ client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], httpVerb: "GET", - routePath: basePath + "/search", + routePath: basePath + "/list", status: "Success", - description: `Find all projects that match query: ${safeQuerySnippet}`, - extra: extra_log, + description: `List projects`, + extra: { NumOfProjs: tot }, }); } catch (e) { logger.logRequestFailure({ client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], httpVerb: "GET", - routePath: basePath + "/search", + routePath: basePath + "/list", status: "Failure", - description: `Find all projects that match query: ${safeQuerySnippet}`, - extra: extra_log, + description: `List projects`, + extra: { NumOfProjs: tot }, error: e, }); g_lib.handleException(e, res); } }) .queryParam("client", joi.string().required(), "Client ID") - .queryParam("query", joi.string().required(), "Query") - .summary("Find all projects that match query") - .description("Find all projects that match query"); + .queryParam("subject", joi.string().optional(), "Subject (user) ID") + .queryParam("as_owner", joi.bool().optional(), "List projects owned by client/subject") + .queryParam("as_admin", joi.bool().optional(), "List projects administered by client/subject") + .queryParam( + "as_member", + joi.bool().optional(), + "List projects where client is a member/subject", + ) + .queryParam("sort", joi.number().optional(), "Sort field (default = id)") + .queryParam("sort_rev", joi.bool().optional(), "Sort in reverse order") + .queryParam("offset", joi.number().optional(), "Offset") + .queryParam("count", joi.number().optional(), "Count") + .summary("List projects") + .description( + "List projects. If no options are provided, lists all projects associated with client.", + ); router .post("/delete", function (req, res) { @@ -826,7 +785,7 @@ router action: function () { const client = g_lib.getUserFromClientID(req.queryParams.client); - var result = g_tasks.taskInitProjDelete(client, req.body.ids); + result = g_tasks.taskInitProjDelete(client, req.body.ids); res.send(result); }, @@ -908,12 +867,12 @@ router extra: { role: role }, }); } catch (e) { - logger.logRequestSuccess({ + logger.logRequestFailure({ client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], httpVerb: "GET", routePath: basePath + "/get_role", - status: "Success", + status: "Failure", description: `Get client/subject project role. ID: ${req.queryParams.id}`, extra: { role: role }, error: e, diff --git a/core/database/foxx/api/query_router.js b/core/database/foxx/api/query_router.js index fb174caee..ff679bb5b 100644 --- a/core/database/foxx/api/query_router.js +++ b/core/database/foxx/api/query_router.js @@ -19,6 +19,14 @@ router .post("/create", function (req, res) { let result = undefined; try { + logger.logRequestStarted({ + client: req.queryParams.client, + correlationId: req.headers["x-correlation-id"], + httpVerb: "POST", + routePath: basePath + "/create", + status: "Started", + description: "Create Query", + }); g_db._executeTransaction({ collections: { read: ["u", "uuid", "accn", "admin"], @@ -26,14 +34,6 @@ router }, action: function () { const client = g_lib.getUserFromClientID(req.queryParams.client); - logger.logRequestStarted({ - client: req.queryParams.client, - correlationId: req.headers["x-correlation-id"], - httpVerb: "POST", - routePath: basePath + "/create", - status: "Started", - description: "Create Query", - }); // Check max number of saved queries if (client.max_sav_qry >= 0) { @@ -82,7 +82,7 @@ router delete qry.qry_end; delete qry.qry_filter; delete qry.params; - delete qry.lmit; + delete qry.limit; result = qry; }, @@ -135,6 +135,15 @@ router .post("/update", function (req, res) { let result = undefined; try { + logger.logRequestStarted({ + client: req.queryParams.client, + correlationId: req.headers["x-correlation-id"], + httpVerb: "POST", + routePath: basePath + "/update", + status: "Started", + description: "Update a saved query", + }); + g_db._executeTransaction({ collections: { read: ["u", "uuid", "accn", "admin"], @@ -142,15 +151,6 @@ router }, action: function () { const client = g_lib.getUserFromClientID(req.queryParams.client); - logger.logRequestStarted({ - client: req.queryParams.client, - correlationId: req.headers["x-correlation-id"], - httpVerb: "POST", - routePath: basePath + "/update", - status: "Started", - description: "Update a saved query", - }); - var qry = g_db.q.document(req.body.id); if (client._id != qry.owner && !client.is_admin) { @@ -189,7 +189,7 @@ router delete qry.qry_end; delete qry.qry_filter; delete qry.params; - delete qry.lmit; + delete qry.limit; result = qry; }, @@ -265,7 +265,7 @@ router delete qry.qry_end; delete qry.qry_filter; delete qry.params; - delete qry.lmit; + delete qry.limit; res.send(qry); logger.logRequestSuccess({ @@ -345,6 +345,7 @@ router extra: req.queryParams.ids[i], }); } + res.send(); } catch (e) { logger.logRequestFailure({ client: req.queryParams.client, @@ -445,6 +446,10 @@ router .description("List client saved queries"); function execQuery(client, mode, published, orig_query) { + // Make sure we are always dealing with strings. + if (typeof mode === "string" && mode in g_lib) { + mode = g_lib[mode]; + } var col_chk = true, ctxt = client._id; let query = { @@ -471,7 +476,7 @@ function execQuery(client, mode, published, orig_query) { }, ) .toArray(); - if (!query.params.cols) { + if (!query.params.cols.length) { throw [ error.ERR_PERM_DENIED, "No access to user '" + query.params.owner + "' data/collections.", @@ -505,7 +510,7 @@ function execQuery(client, mode, published, orig_query) { }, ) .toArray(); - if (!query.params.cols) { + if (!query.params.cols.length) { throw [ error.ERR_PERM_DENIED, "No access to project '" + query.params.owner + "'.", @@ -656,6 +661,15 @@ router var qry = g_db.q.document(req.queryParams.id); + // Legacy query documents may have `params` stored as a JSON string + // rather than an object, because the original schema validation + // (joi.any()) accepted both. New documents are stored as objects + // (joi.object()), but old records remain until migrated. + // TODO: Remove after backfilling existing queries in ArangoDB. + if (typeof qry.params === "string") { + qry.params = JSON.parse(qry.params); + } + if (client._id != qry.owner && !client.is_admin) { throw error.ERR_PERM_DENIED; } @@ -675,7 +689,10 @@ router routePath: basePath + "/exec", status: "Success", description: "Execute specified queries", - extra: results, + extra: { + count: Array.isArray(results) ? results.length : undefined, + query_id: req.queryParams.id, + }, }); } catch (e) { logger.logRequestFailure({ @@ -685,7 +702,10 @@ router routePath: basePath + "/exec", status: "Failure", description: "Execute specified queries", - extra: results, + extra: { + count: Array.isArray(results) ? results.length : undefined, + query_id: req.queryParams.id, + }, error: e, }); g_lib.handleException(e, res); diff --git a/core/database/foxx/api/repo_router.js b/core/database/foxx/api/repo_router.js index 27b8b4d94..64a913490 100644 --- a/core/database/foxx/api/repo_router.js +++ b/core/database/foxx/api/repo_router.js @@ -171,9 +171,9 @@ router status: "Failure", description: `View repo server record: ${req.queryParams.id}`, extra: { - type: repo.type, - capacity: repo.capacity, - admins: repo.admins, + type: repo?.type, + capacity: repo?.capacity, + admins: repo?.admins, }, error: e, }); @@ -917,7 +917,7 @@ router permissions.ensureAdminPermRepo(client, req.queryParams.repo); result = getAllocStats(req.queryParams.repo, req.queryParams.subject); res.send(result); - logger.logRequestStarted({ + logger.logRequestSuccess({ client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], httpVerb: "GET", @@ -1211,7 +1211,7 @@ router if (req.queryParams.subject) { if (req.queryParams.subject.startsWith("p/")) { - if (!g_db._exists(subject_id)) + if (!g_db._exists(req.queryParams.subject)) throw [ error.ERR_NOT_FOUND, "Project, " + req.queryParams.subject + ", not found", diff --git a/core/database/foxx/api/schema_router.js b/core/database/foxx/api/schema_router.js index ee5356207..7dc84fee8 100644 --- a/core/database/foxx/api/schema_router.js +++ b/core/database/foxx/api/schema_router.js @@ -333,11 +333,11 @@ router status: "Failure", description: `Update schema. Schema ID: ${req.queryParams.id}`, extra: { - id: sch_new.id, - own_id: sch_new.own_id, - pub: sch_new.pub, + id: sch_new?.id, + own_id: sch_new?.own_id, + pub: sch_new?.pub, sys: req.body?.sys ?? false, - ver: sch_new.ver, + ver: sch_new?.ver, }, error: e, }); @@ -499,11 +499,11 @@ router status: "Failure", description: `Revise schema. Schema ID: ${req.queryParams.id}`, extra: { - own_id: sch_new.own_id, - own_nm: sch_new.own_nm, - id: sch_new.id, - pub: req.body.pub, - sys: req.body.sys, + own_id: sch_new?.own_id, + own_nm: sch_new?.own_nm, + id: sch_new?.id, + pub: req.body?.pub, + sys: req.body?.sys, }, error: e, }); @@ -608,7 +608,7 @@ router routePath: basePath + "/delete", status: "Failure", description: `Delete schema. Schema ID: ${req.queryParams.id}`, - extra: { deleted: sch_old._id }, + extra: { deleted: sch_old?._id }, }); g_lib.handleException(e, res); } @@ -700,8 +700,8 @@ router status: "Failure", description: `View schema. Schema ID: ${req.queryParams.id}`, extra: { - pub: sch.pub, - sys: sch.sys, + pub: sch?.pub, + sys: sch?.sys, }, }); g_lib.handleException(e, res); diff --git a/core/database/foxx/api/tag_router.js b/core/database/foxx/api/tag_router.js index 575d04f3a..ea30323d9 100644 --- a/core/database/foxx/api/tag_router.js +++ b/core/database/foxx/api/tag_router.js @@ -19,6 +19,7 @@ router let client = null; let result = null; let tot = null; + let name = null; try { client = req.queryParams.client ? g_lib.getUserFromClientID(req.queryParams.client) @@ -31,7 +32,7 @@ router status: "Started", description: `Search for tags by name (${req.queryParams?.name?.trim()})`, }); - var name = req.queryParams.name.trim(); + name = req.queryParams.name.trim(); if (name.length < 3) throw [error.ERR_INVALID_PARAM, "Input is too short for tag search."]; diff --git a/core/database/foxx/api/task_router.js b/core/database/foxx/api/task_router.js index 292beb922..e9ae12dd1 100644 --- a/core/database/foxx/api/task_router.js +++ b/core/database/foxx/api/task_router.js @@ -347,6 +347,7 @@ router throw [error.ERR_IN_USE, "Cannot delete task that is still scheduled."]; g_lib.graph.task.remove(req.queryParams.task_id); + res.send(); logger.logRequestSuccess({ client: req?.queryParams?.task_id, correlationId: req.headers["x-correlation-id"], @@ -357,7 +358,6 @@ router extra: req.queryParams.task_id, }); } catch (e) { - g_lib.handleException(e, res); logger.logRequestFailure({ client: req?.queryParams?.task_id, correlationId: req.headers["x-correlation-id"], @@ -368,6 +368,7 @@ router extra: "undefined", error: e, }); + g_lib.handleException(e, res); } }) .queryParam("task_id", joi.string().required(), "Task ID") @@ -380,7 +381,7 @@ router try { const client = g_lib.getUserFromClientID(req.queryParams.client); logger.logRequestStarted({ - client: req?.queryParams?.task_id, + client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], httpVerb: "GET", routePath: basePath + "/list", @@ -422,7 +423,7 @@ router res.send(result); logger.logRequestSuccess({ - client: req?.queryParams?.task_id, + client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], httpVerb: "GET", routePath: basePath + "/list", @@ -435,7 +436,7 @@ router }); } catch (e) { logger.logRequestFailure({ - client: req?.queryParams?.task_id, + client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], httpVerb: "GET", routePath: basePath + "/list", @@ -471,7 +472,7 @@ router try { result = []; logger.logRequestStarted({ - client: req?.queryParams?.task_id, + client: "system", correlationId: req.headers["x-correlation-id"], httpVerb: "GET", routePath: basePath + "/reload", @@ -495,7 +496,7 @@ router res.send(result); logger.logRequestSuccess({ - client: req?.queryParams?.task_id, + client: "system", correlationId: req.headers["x-correlation-id"], httpVerb: "GET", routePath: basePath + "/reload", @@ -505,7 +506,7 @@ router }); } catch (e) { logger.logRequestFailure({ - client: req?.queryParams?.task_id, + client: "system", correlationId: req.headers["x-correlation-id"], httpVerb: "GET", routePath: basePath + "/reload", @@ -549,6 +550,7 @@ router ); }, }); + res.send(); logger.logRequestSuccess({ client: "undefined", correlationId: req.headers["x-correlation-id"], diff --git a/core/database/foxx/api/topic_router.js b/core/database/foxx/api/topic_router.js index b5b531505..ef284d292 100644 --- a/core/database/foxx/api/topic_router.js +++ b/core/database/foxx/api/topic_router.js @@ -16,9 +16,7 @@ module.exports = router; router .get("/list/topics", function (req, res) { - let client = req.queryParams.client - ? g_lib.getUserFromClientID(req.queryParams.client) - : undefined; + let client = null; let result = null; try { client = g_lib.getUserFromClientID(req.queryParams.client); @@ -109,11 +107,12 @@ router router .get("/view", function (req, res) { - let client = req.queryParams.client - ? g_lib.getUserFromClientID(req.queryParams.client) - : undefined; + let client = null; let topic_extra = undefined; try { + client = req.queryParams.client + ? g_lib.getUserFromClientID(req.queryParams.client) + : undefined; logger.logRequestStarted({ client: client?._id, correlationId: req.headers["x-correlation-id"], @@ -166,13 +165,14 @@ router router .get("/search", function (req, res) { - let client = req.queryParams.client - ? g_lib.getUserFromClientID(req.queryParams.client) - : undefined; - let result = null; + let client = null; + let result = []; const phrase = req.queryParams.phrase; const shortPhrase = phrase.length > 10 ? phrase.slice(0, 10) + "..." : phrase; try { + client = req.queryParams.client + ? g_lib.getUserFromClientID(req.queryParams.client) + : undefined; logger.logRequestStarted({ client: client?._id, correlationId: req.headers["x-correlation-id"], @@ -194,7 +194,6 @@ router path, op = false; - result = []; if (tokens.length == 0) throw [error.ERR_INVALID_PARAM, "Invalid topic search phrase."]; it = 0; diff --git a/core/database/foxx/api/user_router.js b/core/database/foxx/api/user_router.js index fce626e90..4c4b1e031 100644 --- a/core/database/foxx/api/user_router.js +++ b/core/database/foxx/api/user_router.js @@ -291,7 +291,7 @@ router routePath: basePath + "/create", status: "Failure", description: "Create new user entry", - extra: user.new.uid, + extra: user?.new?.uid, error: e, }); g_lib.handleException(e, res); @@ -314,6 +314,7 @@ router router .get("/update", function (req, res) { + let client = null; let result = null; let sub = req.queryParams.subject ? req.queryParams.subject : req.queryParams.client; try { @@ -400,15 +401,6 @@ router delete user.new.refresh; result = [user.new]; - - const { is_admin, max_coll, max_proj, max_sav_qry } = user.new; - - extra_log_info = { - is_admin, - max_coll, - max_proj, - max_sav_qry, - }; }, }); res.send(result); @@ -725,6 +717,7 @@ router router .get("/keys/get", function (req, res) { let sub = req.queryParams.subject ? req.queryParams.subject : req.queryParams.client; + let user = null; try { if (req.queryParams.subject) { if (!g_db.u.exists(req.queryParams.subject)) @@ -733,7 +726,7 @@ router "No such user '" + req.queryParams.subject + "'", ]; - let user = g_db.u.document({ + user = g_db.u.document({ _id: req.queryParams.subject, }); logger.logRequestStarted({ @@ -745,7 +738,7 @@ router description: `Get user public and private keys. ${sub}`, }); } else { - let user = g_lib.getUserFromClientID(req.queryParams.client); + user = g_lib.getUserFromClientID(req.queryParams.client); } if (!user.pub_key || !user.priv_key) { @@ -1036,6 +1029,7 @@ router router .get("/token/get", function (req, res) { let sub = req.queryParams.subject ? req.queryParams.subject : req.queryParams.client; + let user = null; try { const collection_token = UserToken.validateRequestParams(req.queryParams); // TODO: collection type determines logic when mapped vs HA @@ -1048,11 +1042,11 @@ router "No such user '" + req.queryParams.subject + "'", ]; - var user = g_db.u.document({ + user = g_db.u.document({ _id: req.queryParams.subject, }); } else { - var user = g_lib.getUserFromClientID(req.queryParams.client); + user = g_lib.getUserFromClientID(req.queryParams.client); } logger.logRequestStarted({ @@ -1122,6 +1116,7 @@ router router .get("/token/get/access", function (req, res) { let sub = req.queryParams.subject ? req.queryParams.subject : req.queryParams.client; + let user = null; try { if (req.queryParams.subject) { if (!g_db.u.exists(req.queryParams.subject)) @@ -1129,11 +1124,11 @@ router error.ERR_INVALID_PARAM, "No such user '" + req.queryParams.subject + "'", ]; - let user = g_db.u.document({ + user = g_db.u.document({ _id: req.queryParams.subject, }); } else { - let user = g_lib.getUserFromClientID(req.queryParams.client); + user = g_lib.getUserFromClientID(req.queryParams.client); } logger.logRequestStarted({ client: req.queryParams.client, @@ -1229,8 +1224,9 @@ router router .get("/view", function (req, res) { let sub = req.queryParams.subject ? req.queryParams.subject : req.queryParams.client; + let client = null; try { - const client = g_lib.getUserFromClientID_noexcept(req.queryParams.client); + client = g_lib.getUserFromClientID_noexcept(req.queryParams.client); logger.logRequestStarted({ client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], @@ -1329,7 +1325,6 @@ router extra: `uid=${user.uid}, is_admin=${!!client?.is_admin}`, }); //req.queryParams.details ? } catch (e) { - g_lib.handleException(e, res); logger.logRequestFailure({ client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], @@ -1337,9 +1332,10 @@ router routePath: basePath + "/view", status: "Failure", description: `View User Information. Subject: ${sub}`, - extra: `uid=${user.uid}, is_admin=${!!client?.is_admin}`, + extra: `uid=${user?.uid}, is_admin=${!!client?.is_admin}`, error: e, }); + g_lib.handleException(e, res); } }) .queryParam("client", joi.string().required(), "Client ID") @@ -1485,7 +1481,9 @@ Note: must delete ALL data records and projects owned by the user being deleted router .get("/delete", function (req, res) { let user_id = null; + let sub = null; try { + sub = req.queryParams.subject ? req.queryParams.subject : req.queryParams.client; logger.logRequestStarted({ client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], @@ -1610,6 +1608,7 @@ router router .get("/ident/list", function (req, res) { let sub = req.queryParams.subject ? req.queryParams.subject : req.queryParams.client; + let client = null; let extra_log = []; try { client = g_lib.getUserFromClientID(req.queryParams.client); @@ -1680,6 +1679,7 @@ router router .get("/ident/add", function (req, res) { + let client = null; let sub = req.queryParams.subject ? req.queryParams.subject : req.queryParams.client; try { logger.logRequestStarted({ @@ -1713,7 +1713,7 @@ router g_db._exists({ _id: "uuid/" + req.queryParams.ident, }) - ) + ) { logger.logRequestSuccess({ client: req.queryParams.client, correlationId: req.headers["x-correlation-id"], @@ -1723,8 +1723,8 @@ router description: `Add new linked identity. Subject: ${sub}`, extra: req.queryParams.ident, }); - - return; + return; + } id = g_db.uuid.save( { _key: req.queryParams.ident, @@ -1964,7 +1964,7 @@ router correlationId: req.headers["x-correlation-id"], httpVerb: "GET", routePath: basePath + "/ep/set", - status: "Started", + status: "Success", description: "Set recent end-points", extra: client.eps, }); diff --git a/core/database/foxx/tests/proj_router.test.js b/core/database/foxx/tests/proj_router.test.js index 3e854f84c..1d8e46272 100644 --- a/core/database/foxx/tests/proj_router.test.js +++ b/core/database/foxx/tests/proj_router.test.js @@ -367,56 +367,6 @@ describe("unit_proj_router: test project create endpoint", () => { expect(ids).to.include.members(["p/proj1", "p/proj2", "p/proj3"]); }); - it("should search projects using a provided AQL query", () => { - // ------------------------------------------------------------------ - // Arrange - // ------------------------------------------------------------------ - db.u.save({ _key: "search_user", is_admin: false }); - - db.p.save({ - _key: "search_proj1", - title: "Alpha Project", - desc: "First searchable project", - ct: 1, - ut: 1, - owner: "u/search_user", - }); - - db.p.save({ - _key: "search_proj2", - title: "Beta Project", - desc: "Second searchable project", - ct: 1, - ut: 1, - owner: "u/search_user", - }); - - // AQL query passed directly to /search - const aql = "FOR p IN p FILTER p.title LIKE '%Project%' RETURN p._id"; - - const url = - `${proj_base_url}/search` + - `?client=u/search_user` + - `&query=${encodeURIComponent(aql)}`; - - // ------------------------------------------------------------------ - // Act - // ------------------------------------------------------------------ - const response = request.get(url, { - headers: { "x-correlation-id": "test-proj-search" }, - }); - - // ------------------------------------------------------------------ - // Assert - // ------------------------------------------------------------------ - expect(response.status).to.equal(200); - - const body = JSON.parse(response.body); - - expect(body).to.be.an("array"); - expect(body).to.include.members(["p/search_proj1", "p/search_proj2"]); - }); - it("should enqueue a project delete task when client is authorized", () => { // ------------------------------------------------------------------ // Arrange @@ -564,38 +514,4 @@ describe("unit_proj_router: test project create endpoint", () => { body = JSON.parse(response.body); expect(body.role).to.equal(1); // member }); - - it("should handle malformed AQL for /prj/search without crashing and return an error response", () => { - // ------------------------------------------------------------------ - // Arrange - // ------------------------------------------------------------------ - db.u.save({ _key: "search_user_malformed", is_admin: false }); - - const client = "search_user_malformed"; - - // Intentionally malformed AQL (missing RETURN and invalid syntax) - const malformedBody = { - client, - aql: "FOR p IN p FILTER p.title == @title INVALID_SYNTAX", - bindVars: { - title: "Alpha Project", - }, - }; - - // ------------------------------------------------------------------ - // Act - // ------------------------------------------------------------------ - const response = request.get("/prj/search", malformedBody); - - // ------------------------------------------------------------------ - // Assert - // ------------------------------------------------------------------ - // Expect a 400-series error (bad request / invalid query) and a JSON error payload - expect(response.status).to.be.within(400, 499); - - const body = JSON.parse(response.body); - expect(body).to.have.property("error", true); - expect(body).to.have.property("code"); - expect(body).to.have.property("errorMessage"); - }); }); diff --git a/core/server/DatabaseAPI.cpp b/core/server/DatabaseAPI.cpp index 4dd2be945..cdf752e6c 100644 --- a/core/server/DatabaseAPI.cpp +++ b/core/server/DatabaseAPI.cpp @@ -808,16 +808,6 @@ void DatabaseAPI::projGetRole(const SDMS::ProjectGetRoleRequest &a_request, a_reply.set_role((ProjectRole)(unsigned short)obj.getNumber("role")); } -void DatabaseAPI::projSearch(const std::string &a_query, - SDMS::ProjectDataReply &a_reply, - LogContext log_context) { - Value result; - - dbGet("prj/search", {{"query", a_query}}, result, log_context); - - setProjectData(a_reply, result, log_context); -} - void DatabaseAPI::setProjectData(SDMS::ProjectDataReply &a_reply, const Value &a_result, LogContext log_context) { @@ -1760,8 +1750,9 @@ void DatabaseAPI::queryCreate(const SDMS::QueryCreateRequest &a_request, google::protobuf::util::JsonPrintOptions options; string query_json; - options.always_print_enums_as_ints = true; + options.always_print_enums_as_ints = false; options.preserve_proto_field_names = true; + options.always_print_primitive_fields = true; auto stat = google::protobuf::util::MessageToJsonString(a_request.query(), &query_json, options); @@ -1796,19 +1787,45 @@ void DatabaseAPI::queryUpdate(const SDMS::QueryUpdateRequest &a_request, } if (a_request.has_query()) { - string qry_begin, qry_end, qry_filter, params; + SDMS::SearchRequest final_query; + if (a_request.replace_query()) { + // Full replacement — use incoming query as-is + final_query.CopyFrom(a_request.query()); + } else { + // Partial update — merge incoming onto existing + Value existing; + dbGet("qry/view", {{"id", a_request.id()}}, existing, log_context); + + auto parse_stat = google::protobuf::util::JsonStringToMessage( + existing.asObject().getValue("query").toString(), &final_query); + if (!parse_stat.ok()) { + EXCEPT(1, "Failed to parse existing query"); + } + + if (a_request.query().coll_size() > 0) { + final_query.clear_coll(); + } + if (a_request.query().tags_size() > 0) { + final_query.clear_tags(); + } + if (a_request.query().cat_tags_size() > 0) { + final_query.clear_cat_tags(); + } + final_query.MergeFrom(a_request.query()); + } - uint32_t cnt = parseSearchRequest(a_request.query(), qry_begin, qry_end, + // Re-generate AQL from the complete merged query + string qry_begin, qry_end, qry_filter, params; + uint32_t cnt = parseSearchRequest(final_query, qry_begin, qry_end, qry_filter, params, log_context); google::protobuf::util::JsonPrintOptions options; string query_json; - - options.always_print_enums_as_ints = true; + options.always_print_enums_as_ints = false; options.preserve_proto_field_names = true; - + options.always_print_primitive_fields = true; auto stat = google::protobuf::util::MessageToJsonString( - a_request.query(), &query_json, options); + final_query, &query_json, options); if (!stat.ok()) { EXCEPT(1, "Invalid search request"); } @@ -1823,7 +1840,6 @@ void DatabaseAPI::queryUpdate(const SDMS::QueryUpdateRequest &a_request, string body = payload.dump(-1, ' ', true); dbPost("qry/update", {}, &body, result, log_context); - setQueryData(a_reply, result, log_context); } diff --git a/core/server/DatabaseAPI.hpp b/core/server/DatabaseAPI.hpp index f0df10a51..fa9327c50 100644 --- a/core/server/DatabaseAPI.hpp +++ b/core/server/DatabaseAPI.hpp @@ -105,8 +105,6 @@ class DatabaseAPI { SDMS::ProjectDataReply &a_reply, LogContext log_context); void projList(const SDMS::ProjectListRequest &a_request, SDMS::ListingReply &a_reply, LogContext log_context); - void projSearch(const std::string &a_query, SDMS::ProjectDataReply &a_reply, - LogContext log_context); void projGetRole(const SDMS::ProjectGetRoleRequest &a_request, SDMS::ProjectGetRoleReply &a_reply, LogContext log_context); diff --git a/external/DataFedDependencies b/external/DataFedDependencies index fe59a393f..e0319a2f2 160000 --- a/external/DataFedDependencies +++ b/external/DataFedDependencies @@ -1 +1 @@ -Subproject commit fe59a393f54d3aa1b8bf551f97d274b762bf93d2 +Subproject commit e0319a2f2e70d901180c730ebd5b10b10d8fce93 diff --git a/external/globus-connect-server-deploy b/external/globus-connect-server-deploy index 436b396c4..ff7167860 160000 --- a/external/globus-connect-server-deploy +++ b/external/globus-connect-server-deploy @@ -1 +1 @@ -Subproject commit 436b396c4da6d141c9c0534b297b5e43cc9ac35c +Subproject commit ff7167860345e9b994110dfabdb251fe4dea8c00 diff --git a/tests/end-to-end/test_api_query.py b/tests/end-to-end/test_api_query.py index cf1894a09..174d0d4c6 100755 --- a/tests/end-to-end/test_api_query.py +++ b/tests/end-to-end/test_api_query.py @@ -191,6 +191,7 @@ def test_query_create_delete(self): if model.alias.startswith("adamantium"): material = model.alias time.sleep(self._timeout) + count = count + 1 print(f"Query found {material}") diff --git a/web/datafed-ws.js b/web/datafed-ws.js index 6b367565e..e12386156 100755 --- a/web/datafed-ws.js +++ b/web/datafed-ws.js @@ -1020,12 +1020,6 @@ app.get("/api/prj/list", (a_req, a_resp) => { }); }); -app.post("/api/prj/search", (a_req, a_resp) => { - sendMessage("ProjectSearchRequest", a_req.body, a_req, a_resp, function (reply) { - a_resp.send(reply.item ? reply.item : []); - }); -}); - app.get("/api/grp/create", (a_req, a_resp) => { var params = { group: { @@ -1114,7 +1108,7 @@ app.post("/api/query/create", (a_req, a_resp) => { }); app.post("/api/query/update", (a_req, a_resp) => { - var params = { id: a_req.query.id }; + var params = { id: a_req.query.id, replaceQuery: true }; if (a_req.query.title) params.title = a_req.query.title; if (a_req.body) params.query = a_req.body; diff --git a/web/static/util.js b/web/static/util.js index ab19b6973..6cff31350 100644 --- a/web/static/util.js +++ b/web/static/util.js @@ -51,14 +51,11 @@ export function getUpdatedValue(a_new_val, a_old_obj, a_new_obj, a_field) { export function getUpdatedValueJSON(a_new_val, a_old_obj, a_new_obj, a_field) { var tmp = a_new_val.trim(), old = a_old_obj[a_field]; - if (old === undefined && tmp.length) { + if ((!old || old === undefined) && tmp.length) { a_new_obj[a_field] = tmp; } else if (tmp.length) { - // Must compare values - have to restringify both b/c formats may differ with same content - // TODO - This should be a deep compare due to possibly inconsistent object arrangement var oldjs = JSON.stringify(JSON.parse(old)), newjs = JSON.stringify(JSON.parse(tmp)); - if (oldjs != newjs) { a_new_obj[a_field] = tmp; }