42#include <stringbuffer.h>
46#include <libdap/DDS.h>
47#include <libdap/DMR.h>
48#include <libdap/D4Group.h>
49#include <libdap/D4Attributes.h>
50#include <libdap/DataDDS.h>
52#include "BESContextManager.h"
53#include "BESDapResponseBuilder.h"
54#include "DapFunctionUtils.h"
59#include "FONcBaseType.h"
60#include "FONcTransmitter.h"
61#include "FONcTransform.h"
65#define NEW_LINE ((char)0x0a)
66#define CF_HISTORY_KEY "history"
67#define CF_HISTORY_CONTEXT "cf_history_entry"
68#define HISTORY_JSON_KEY "history_json"
69#define HISTORY_JSON_CONTEXT "history_json_entry"
77#define HISTORY_JSON_DIRECT_TO_NETCDF 0
80#define prolog string("history_utils::").append(__func__).append("() - ")
85namespace fonc_history_util {
96 const struct tm *timeinfo = localtime(&raw_now);
99 strftime(time_str,
sizeof(time_str),
"%Y-%m-%d %H:%M:%S", timeinfo);
100 return string(time_str);
114string create_cf_history_txt(
const string &request_url) {
124 ss << get_time_now() <<
" " <<
"Hyrax" <<
" " << request_url << NEW_LINE;
126 BESDEBUG(MODULE, prolog <<
"New cf history entry: '" << ss.str() <<
"'" << endl);
144template<
typename RJSON_WRITER>
145void create_json_history_obj(
const string &request_url, RJSON_WRITER &writer) {
146 const string schema =
"https://harmony.earthdata.nasa.gov/schemas/history/0.1.0/history-0.1.0.json";
148 writer.StartObject();
149 writer.Key(
"$schema");
150 writer.String(schema.c_str());
151 writer.Key(
"date_time");
152 writer.String(get_time_now().c_str() );
153 writer.Key(
"program");
154 writer.String(
"hyrax");
155 writer.Key(
"version");
156 writer.String(
"1.16.3");
157 writer.Key(
"parameters");
159 writer.StartObject();
160 writer.Key(
"request_url");
161 writer.String(request_url.c_str());
178static string get_cf_history_entry(
const string &request_url) {
179 bool foundIt =
false;
180 string cf_history_entry = BESContextManager::TheManager()->
get_context(CF_HISTORY_CONTEXT, foundIt);
184 cf_history_entry = create_cf_history_txt(request_url);
186 return cf_history_entry;
194static string get_history_json_entry(
const string &request_url) {
195 bool foundIt =
false;
196 string history_json_entry = BESContextManager::TheManager()->
get_context(HISTORY_JSON_CONTEXT, foundIt);
200 rapidjson::Document history_json_doc;
201 history_json_doc.SetObject();
202 rapidjson::StringBuffer buffer;
203 rapidjson::Writer <rapidjson::StringBuffer> writer(buffer);
204 create_json_history_obj(request_url, writer);
205 history_json_entry = buffer.GetString();
208 BESDEBUG(MODULE, prolog <<
"Using history_json_entry: " << history_json_entry << endl);
209 return history_json_entry;
222string json_append_entry_to_array(
const string &source_array_str,
const string &new_entry_str) {
223 rapidjson::Document target_array;
224 target_array.SetArray();
226 target_array.Parse(source_array_str.c_str());
228 rapidjson::Document entry;
229 entry.Parse(new_entry_str.c_str());
231 target_array.PushBack(entry, allocator);
234 rapidjson::StringBuffer buffer;
235 rapidjson::Writer <rapidjson::StringBuffer> writer(buffer);
236 target_array.Accept(writer);
237 return buffer.GetString();
255static void update_history_json_attr(D4Attribute *global_attribute,
const string &request_url) {
257 prolog <<
"Updating history_json entry for global DAP4 attribute: " << global_attribute->name() << endl);
259 string hj_entry_str = get_history_json_entry(request_url);
260 BESDEBUG(MODULE, prolog <<
"hj_entry_str: " << hj_entry_str << endl);
264 D4Attribute *history_json_attr =
nullptr;
265 if (global_attribute->type() == D4AttributeType::attr_container_c) {
266 history_json_attr = global_attribute->attributes()->find(HISTORY_JSON_KEY);
268 else if (global_attribute->name() == HISTORY_JSON_KEY) {
269 history_json_attr = global_attribute;
272 if (!history_json_attr) {
276 prolog <<
"Adding history_json entry to global_attribute " << global_attribute->name() << endl);
277 history_json_attr =
new D4Attribute(HISTORY_JSON_KEY, attr_str_c);
278 global_attribute->attributes()->add_attribute_nocopy(history_json_attr);
281 history_json =
"[" + hj_entry_str +
"]";
282 BESDEBUG(MODULE, prolog <<
"CREATED history_json: " << history_json << endl);
291 history_json = *history_json_attr->value_begin();
300 BESDEBUG(MODULE, prolog <<
"FOUND history_json: " << history_json << endl);
303 history_json = json_append_entry_to_array(history_json, hj_entry_str);
304 BESDEBUG(MODULE, prolog <<
"NEW history_json: " << history_json << endl);
310 vector <string> attr_vals;
311 attr_vals.push_back(history_json);
312 history_json_attr->add_value_vector(attr_vals);
322static string append_cf_history_entry(
string cf_history,
string cf_history_entry) {
324 stringstream cf_hist_new;
325 if (!cf_history.empty()) {
326 cf_hist_new << cf_history;
327 if (cf_history.back() != NEW_LINE)
328 cf_hist_new << NEW_LINE;
331 cf_hist_new << cf_history_entry;
332 if (cf_history_entry.back() != NEW_LINE)
333 cf_hist_new << NEW_LINE;
335 BESDEBUG(MODULE, prolog <<
"Updated cf history: '" << cf_hist_new.str() <<
"'" << endl);
336 return cf_hist_new.str();
356static void update_cf_history_attr(D4Attribute *global_attribute,
const string &request_url) {
358 prolog <<
"Updating cf history entry for global DAP4 attribute: " << global_attribute->name() << endl);
360 string cf_hist_entry = get_cf_history_entry(request_url);
361 BESDEBUG(MODULE, prolog <<
"New cf history entry: " << cf_hist_entry << endl);
364 D4Attribute *history_attr =
nullptr;
365 if (global_attribute->type() == D4AttributeType::attr_container_c) {
366 history_attr = global_attribute->attributes()->find(CF_HISTORY_KEY);
368 else if (global_attribute->name() == CF_HISTORY_KEY) {
369 history_attr = global_attribute;
374 BESDEBUG(MODULE, prolog <<
"Adding history entry to " << global_attribute->name() << endl);
375 history_attr =
new D4Attribute(CF_HISTORY_KEY, attr_str_c);
376 global_attribute->attributes()->add_attribute_nocopy(history_attr);
379 cf_history = history_attr->value(0);
381 cf_history = append_cf_history_entry(cf_history, cf_hist_entry);
383 std::vector <std::string> cf_hist_vec;
384 cf_hist_vec.push_back(cf_history);
385 history_attr->add_value_vector(cf_hist_vec);
394void update_cf_history_attr(AttrTable *global_attr_tbl,
const string &request_url) {
397 prolog <<
"Updating cf history entry for global DAP2 attribute: " << global_attr_tbl->get_name() << endl);
399 string cf_hist_entry = get_cf_history_entry(request_url);
400 BESDEBUG(MODULE, prolog <<
"New cf history entry: '" << cf_hist_entry <<
"'" << endl);
402 string cf_history = global_attr_tbl->get_attr(CF_HISTORY_KEY);
403 BESDEBUG(MODULE, prolog <<
"Previous cf history: '" << cf_history <<
"'" << endl);
405 cf_history = append_cf_history_entry(cf_history, cf_hist_entry);
406 BESDEBUG(MODULE, prolog <<
"Updated cf history: '" << cf_history <<
"'" << endl);
408 global_attr_tbl->del_attr(CF_HISTORY_KEY, -1);
409 int attr_count = global_attr_tbl->append_attr(CF_HISTORY_KEY,
"string", cf_history);
410 BESDEBUG(MODULE, prolog <<
"Found " << attr_count <<
" value(s) for the cf history attribute." << endl);
418void update_history_json_attr(AttrTable *global_attr_tbl,
const string &request_url) {
420 BESDEBUG(MODULE, prolog <<
"Updating history_json entry for global DAP2 attribute: " << global_attr_tbl->get_name()
423 string hj_entry_str = get_history_json_entry(request_url);
424 BESDEBUG(MODULE, prolog <<
"New history_json entry: " << hj_entry_str << endl);
426 string history_json = global_attr_tbl->get_attr(HISTORY_JSON_KEY);
427 BESDEBUG(MODULE, prolog <<
"Previous history_json: " << history_json << endl);
429 if (history_json.empty()) {
432 prolog <<
"Creating new history_json entry to global attribute: " << global_attr_tbl->get_name()
434 history_json =
"[" + hj_entry_str +
"]";
437 history_json = json_append_entry_to_array(history_json, hj_entry_str);
438 global_attr_tbl->del_attr(HISTORY_JSON_KEY, -1);
440 BESDEBUG(MODULE, prolog <<
"New history_json: " << history_json << endl);
441 int attr_count = global_attr_tbl->append_attr(HISTORY_JSON_KEY,
"string", history_json);
442 BESDEBUG(MODULE, prolog <<
"Found " << attr_count <<
" value(s) for the history_json attribute." << endl);
451void updateHistoryAttributes(DDS *dds,
const string &ce) {
452 string request_url = dds->filename();
454 request_url = request_url.substr(request_url.find_last_of(
'/') + 1);
456 request_url = request_url.substr(request_url.find_last_of(
'#') + 1);
457 if (!ce.empty()) request_url +=
"?" + ce;
461 AttrTable &globals = dds->get_attr_table();
468 bool added_history =
false;
470 if (globals.is_global_attribute()) {
473 auto i = globals.attr_begin();
474 auto e = globals.attr_end();
475 for (; i != e; i++) {
476 AttrType attrType = globals.get_attr_type(i);
477 string attr_name = globals.get_name(i);
484 AttrTable *global_attr_tbl = globals.get_attr_table(i);
485 update_cf_history_attr(global_attr_tbl, request_url);
486#if !HISTORY_JSON_DIRECT_TO_NETCDF
489 update_history_json_attr(global_attr_tbl, request_url);
491 added_history =
true;
492 BESDEBUG(MODULE, prolog <<
"Added history entries to " << attr_name << endl);
501 if (!added_history) {
502 auto dap_global_at = globals.append_container(
"DAP_GLOBAL");
503 dap_global_at->set_name(
"DAP_GLOBAL");
504 dap_global_at->set_is_global_attribute(
true);
506 update_cf_history_attr(dap_global_at, request_url);
507#if !HISTORY_JSON_DIRECT_TO_NETCDF
508 update_history_json_attr(dap_global_at, request_url);
510 BESDEBUG(MODULE, prolog <<
"No top level AttributeTable name matched '*_GLOBAL'. "
511 "Created DAP_GLOBAL AttributeTable and added history attributes to it." << endl);
522void updateHistoryAttributes(DMR *dmr,
const string &ce) {
523 string request_url = dmr->filename();
525 request_url = request_url.substr(request_url.find_last_of(
'/') + 1);
527 request_url = request_url.substr(request_url.find_last_of(
'#') + 1);
528 if (!ce.empty()) request_url +=
"?" + ce;
530 bool added_cf_history =
false;
531 bool added_json_history =
false;
532 D4Group *root_grp = dmr->root();
533 D4Attributes *root_attrs = root_grp->attributes();
534 for (
auto attrs = root_attrs->attribute_begin(); attrs != root_attrs->attribute_end(); ++attrs) {
535 string name = (*attrs)->name();
536 BESDEBUG(MODULE, prolog <<
"Attribute name is " << name << endl);
537 if ((*attrs)->type() == D4AttributeType::attr_container_c &&
BESUtil::endsWith(name,
"_GLOBAL")) {
539 update_cf_history_attr(*attrs, request_url);
540 added_cf_history =
true;
543#if !HISTORY_JSON_DIRECT_TO_NETCDF
544 update_history_json_attr(*attrs, request_url);
545 added_json_history =
true;
548 else if (name == CF_HISTORY_KEY) {
549 update_cf_history_attr(*attrs, request_url);
550 added_cf_history =
true;
552#if !HISTORY_JSON_DIRECT_TO_NETCDF
553 else if (name == HISTORY_JSON_KEY) {
554 update_cf_history_attr(*attrs, request_url);
555 added_json_history =
true;
559 if (!added_cf_history || !added_json_history) {
560 auto *dap_global =
new D4Attribute(
"DAP_GLOBAL", attr_container_c);
561 root_attrs->add_attribute_nocopy(dap_global);
563 if (!added_cf_history) {
564 update_cf_history_attr(dap_global, request_url);
567#if !HISTORY_JSON_DIRECT_TO_NETCDF
568 if (!added_json_history) {
569 update_history_json_attr(dap_global, request_url);
virtual std::string get_context(const std::string &name, bool &found)
retrieve the value of the specified context from the BES
static bool endsWith(std::string const &fullString, std::string const &ending)
RAPIDJSON_DEFAULT_ALLOCATOR AllocatorType