42 #include <stringbuffer.h>
46 #include <libdap/D4Group.h>
47 #include <libdap/D4Attributes.h>
48 #include <libdap/DataDDS.h>
50 #include "BESContextManager.h"
51 #include "BESDapResponseBuilder.h"
52 #include "DapFunctionUtils.h"
57 #include "FONcBaseType.h"
58 #include "FONcTransmitter.h"
59 #include "FONcTransform.h"
64 #define NEW_LINE ((char)0x0a)
65 #define CF_HISTORY_KEY "history"
66 #define CF_HISTORY_CONTEXT "cf_history_entry"
67 #define HISTORY_JSON_KEY "history_json"
68 #define HISTORY_JSON_CONTEXT "history_json_entry"
71 #define prolog string("history_utils::").append(__func__).append("() - ")
74 void appendHistoryJson(vector<string> *global_attr, vector<string> jsonNew)
77 const char *oldJson = global_attr->at(0).c_str();
78 const char *newJson = jsonNew.at(0).c_str();
82 docNew.
Parse(newJson);
84 docOld.
Parse(oldJson);
85 docNew.PushBack(docOld, allocator);
90 docNew.Accept(writer);
92 global_attr->push_back(buffer.GetString());
105 string create_cf_history_txt(
const string &request_url)
115 string cf_history_entry;
116 std::stringstream ss;
120 timeinfo = localtime(&raw_now);
123 strftime(time_str, 100,
"%Y-%m-%d %H:%M:%S", timeinfo);
125 ss << time_str <<
" " <<
"Hyrax" <<
" " << request_url <<
'\n';
126 cf_history_entry = ss.str();
127 BESDEBUG(MODULE, prolog <<
"New cf history entry: '" << cf_history_entry <<
"'" << endl);
128 return cf_history_entry;
138 template <
typename Writer>
139 void create_json_history_obj(
const string &request_url,
Writer& writer)
151 string schema =
"https://harmony.earthdata.nasa.gov/schemas/history/0.1.0/history-0.1.0.json";
156 timeinfo = localtime(&raw_now);
158 strftime(time_str, 100,
"%Y-%m-%dT%H:%M:%S", timeinfo);
160 writer.StartObject();
161 writer.Key(
"$schema");
162 writer.String(schema.c_str());
163 writer.Key(
"date_time");
164 writer.String(time_str);
165 writer.Key(
"program");
166 writer.String(
"hyrax");
167 writer.Key(
"version");
168 writer.String(
"1.16.3");
169 writer.Key(
"parameters");
171 writer.StartObject();
172 writer.Key(
"request_url");
173 writer.String(request_url.c_str());
184 string get_cf_history_entry (
const string &request_url)
186 bool foundIt =
false;
187 string cf_history_entry = BESContextManager::TheManager()->
get_context(CF_HISTORY_CONTEXT, foundIt);
191 cf_history_entry = create_cf_history_txt(request_url);
193 return cf_history_entry;
202 vector<string> get_history_json_entry (
const string &request_url)
204 vector<string> history_json_entry_vec;
205 bool foundIt =
false;
206 string history_json_entry = BESContextManager::TheManager()->
get_context(
"history_json_entry", foundIt);
212 history_json_doc.SetObject();
215 create_json_history_obj(request_url, writer);
216 history_json_entry = buffer.GetString();
219 BESDEBUG(MODULE,prolog <<
"Using history_json_entry: " << history_json_entry << endl);
221 history_json_entry_vec.push_back(history_json_entry);
222 return history_json_entry_vec;
233 string get_history_json_entry (
const string &request_url)
235 bool foundIt =
false;
236 string history_json_entry = BESContextManager::TheManager()->
get_context(HISTORY_JSON_CONTEXT, foundIt);
241 history_json_doc.SetObject();
244 create_json_history_obj(request_url, writer);
245 history_json_entry = buffer.GetString();
248 BESDEBUG(MODULE,prolog <<
"Using history_json_entry: " << history_json_entry << endl);
249 return history_json_entry;
260 string json_append_entry_to_array(
const string& source_array_str,
const string& new_entry_str)
263 target_array.SetArray();
265 target_array.
Parse(source_array_str.c_str());
268 entry.
Parse(new_entry_str.c_str());
270 target_array.PushBack(entry, allocator);
275 target_array.Accept(writer);
276 return buffer.GetString();
284 void update_history_json_attr(D4Attribute *global_attribute,
const string &request_url)
286 BESDEBUG(MODULE,prolog <<
"Updating history_json entry for global DAP4 attribute: " << global_attribute->name() << endl);
288 string hj_entry_str = get_history_json_entry(request_url);
289 BESDEBUG(MODULE,prolog <<
"hj_entry_str: " << hj_entry_str << endl);
293 D4Attribute *history_json_attr =
nullptr;
294 if(global_attribute->type() == D4AttributeType::attr_container_c){
295 history_json_attr = global_attribute->attributes()->find(HISTORY_JSON_KEY);
297 else if( global_attribute->name() == HISTORY_JSON_KEY){
298 history_json_attr = global_attribute;
301 if (!history_json_attr) {
304 BESDEBUG(MODULE, prolog <<
"Adding history_json entry to global_attribute " << global_attribute->name() << endl);
305 history_json_attr =
new D4Attribute(HISTORY_JSON_KEY, attr_str_c);
306 global_attribute->attributes()->add_attribute_nocopy(history_json_attr);
309 history_json =
"[" + hj_entry_str +
"]";
310 BESDEBUG(MODULE,prolog <<
"CREATED history_json: " << history_json << endl);
318 history_json = *history_json_attr->value_begin();
319 history_json=R
"([{"$schema":"https:\/\/harmony.earthdata.nasa.gov\/schemas\/history\/0.1.0\/history-0.1.0.json","date_time":"2021-06-25T13:28:48.951+0000","program":"hyrax","version":"@HyraxVersion@","parameters":[{"request_url":"http:\/\/localhost:8080\/opendap\/hj\/coads_climatology.nc.dap.nc4?GEN1"}]}])";
320 BESDEBUG(MODULE,prolog << "FOUND history_json: " << history_json << endl);
323 history_json = json_append_entry_to_array(history_json, hj_entry_str);
324 BESDEBUG(MODULE,prolog <<
"NEW history_json: " << history_json << endl);
330 vector<string> attr_vals;
331 attr_vals.push_back(history_json);
332 history_json_attr->add_value_vector(attr_vals);
341 string append_cf_history_entry(
string cf_history,
string cf_history_entry){
343 stringstream cf_hist_new;
344 if(!cf_history.empty()){
345 cf_hist_new << cf_history;
346 if(cf_history.back() != NEW_LINE)
347 cf_hist_new << NEW_LINE;
349 cf_hist_new << cf_history_entry;
350 if(cf_history_entry.back() != NEW_LINE)
351 cf_hist_new << NEW_LINE;
353 BESDEBUG(MODULE, prolog <<
"Updated cf history: '" << cf_hist_new.str() <<
"'" << endl);
354 return cf_hist_new.str();
362 void update_cf_history_attr(D4Attribute *global_attribute,
const string &request_url){
363 BESDEBUG(MODULE,prolog <<
"Updating cf history entry for global DAP4 attribute: " << global_attribute->name() << endl);
365 string cf_hist_entry = get_cf_history_entry(request_url);
366 BESDEBUG(MODULE, prolog <<
"New cf history entry: " << cf_hist_entry << endl);
369 D4Attribute *history_attr =
nullptr;
370 if(global_attribute->type() == D4AttributeType::attr_container_c){
371 history_attr = global_attribute->attributes()->find(CF_HISTORY_KEY);
373 else if( global_attribute->name() == CF_HISTORY_KEY){
374 history_attr = global_attribute;
379 BESDEBUG(MODULE, prolog <<
"Adding history entry to " << global_attribute->name() << endl);
380 history_attr =
new D4Attribute(CF_HISTORY_KEY, attr_str_c);
381 global_attribute->attributes()->add_attribute_nocopy(history_attr);
384 cf_history = history_attr->value(0);
386 cf_history = append_cf_history_entry(cf_history,cf_hist_entry);
388 std::vector<std::string> cf_hist_vec;
389 cf_hist_vec.push_back(cf_history);
390 history_attr->add_value_vector(cf_hist_vec);
399 void update_cf_history_attr(AttrTable *global_attr_tbl,
const string &request_url) {
401 BESDEBUG(MODULE,prolog <<
"Updating cf history entry for global DAP2 attribute: " << global_attr_tbl->get_name() << endl);
403 string cf_hist_entry = get_cf_history_entry(request_url);
404 BESDEBUG(MODULE,prolog <<
"New cf history entry: '" << cf_hist_entry <<
"'" <<endl);
406 string cf_history = global_attr_tbl->get_attr(CF_HISTORY_KEY);
407 BESDEBUG(MODULE,prolog <<
"Previous cf history: '" << cf_history <<
"'" << endl);
409 cf_history = append_cf_history_entry(cf_history,cf_hist_entry);
410 BESDEBUG(MODULE,prolog <<
"Updated cf history: '" << cf_history <<
"'" << endl);
412 global_attr_tbl->del_attr(CF_HISTORY_KEY, -1);
413 int attr_count = global_attr_tbl->append_attr(CF_HISTORY_KEY,
"string", cf_history);
414 BESDEBUG(MODULE,prolog <<
"Found " << attr_count <<
" value(s) for the cf history attribute." << endl);
422 void update_history_json_attr(AttrTable *global_attr_tbl,
const string &request_url) {
424 BESDEBUG(MODULE,prolog <<
"Updating history_json entry for global DAP2 attribute: " << global_attr_tbl->get_name() << endl);
426 string hj_entry_str = get_history_json_entry(request_url);
427 BESDEBUG(MODULE,prolog <<
"New history_json entry: " << hj_entry_str << endl);
429 string history_json = global_attr_tbl->get_attr(HISTORY_JSON_KEY);
430 BESDEBUG(MODULE,prolog <<
"Previous history_json: " << history_json << endl);
432 if (history_json.empty()) {
434 BESDEBUG(MODULE, prolog <<
"Creating new history_json entry to global attribute: " << global_attr_tbl->get_name() << endl);
435 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);
453 void updateHistoryAttributes(DDS *dds,
const string &ce)
455 string request_url = dds->filename();
457 request_url = request_url.substr(request_url.find_last_of(
'/')+1);
459 request_url = request_url.substr(request_url.find_last_of(
'#')+1);
460 if(!ce.empty()) request_url +=
"?" + ce;
464 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 update_history_json_attr(global_attr_tbl,request_url);
487 added_history =
true;
488 BESDEBUG(MODULE, prolog <<
"Added history entries to " << attr_name << endl);
492 auto dap_global_at = globals.append_container(
"DAP_GLOBAL");
493 dap_global_at->set_name(
"DAP_GLOBAL");
494 dap_global_at->set_is_global_attribute(
true);
496 update_cf_history_attr(dap_global_at,request_url);
497 update_history_json_attr(dap_global_at,request_url);
498 BESDEBUG(MODULE, prolog <<
"No top level AttributeTable name matched '*_GLOBAL'. "
499 "Created DAP_GLOBAL AttributeTable and added history attributes to it." << endl);
511 void updateHistoryAttributes(DMR *dmr,
const string &ce)
513 string request_url = dmr->filename();
515 request_url = request_url.substr(request_url.find_last_of(
'/')+1);
517 request_url = request_url.substr(request_url.find_last_of(
'#')+1);
518 if(!ce.empty()) request_url +=
"?" + ce;
520 bool added_cf_history =
false;
521 bool added_json_history =
false;
522 D4Group* root_grp = dmr->root();
523 D4Attributes *root_attrs = root_grp->attributes();
524 for (
auto attrs = root_attrs->attribute_begin(); attrs != root_attrs->attribute_end(); ++attrs) {
525 string name = (*attrs)->name();
526 BESDEBUG(MODULE, prolog <<
"Attribute name is "<< name << endl);
527 if ((*attrs)->type() == D4AttributeType::attr_container_c &&
BESUtil::endsWith(name,
"_GLOBAL")) {
529 update_cf_history_attr(*attrs, request_url);
530 added_cf_history =
true;
533 update_history_json_attr(*attrs, request_url);
534 added_json_history =
true;
536 else if(name == CF_HISTORY_KEY){
537 update_cf_history_attr(*attrs, request_url);
538 added_cf_history =
true;
540 else if( name == HISTORY_JSON_KEY){
541 update_cf_history_attr(*attrs, request_url);
542 added_json_history =
true;
545 if(!added_cf_history || !added_json_history){
546 auto *dap_global =
new D4Attribute(
"DAP_GLOBAL",attr_container_c);
547 root_attrs->add_attribute_nocopy(dap_global);
549 if(!added_cf_history){
550 update_cf_history_attr(dap_global, request_url);
553 if(!added_json_history){
554 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)
A document for parsing JSON text as DOM.
Allocator & GetAllocator()
Get the allocator of this document.
GenericDocument & Parse(const typename SourceEncoding::Ch *str)
Parse JSON text from a read-only string (with Encoding conversion)
Represents an in-memory output stream.
Concept for allocating, resizing and freeing memory block.