Skip to content

Commit

Permalink
refs #3908: fixed serializing DATE and TIME fields in SalesforceRestD…
Browse files Browse the repository at this point in the history
…ataProvider

refs #3909: log output before calling output providers
  • Loading branch information
davidnich committed May 2, 2020
1 parent 880de54 commit 666e164
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 10 deletions.
5 changes: 5 additions & 0 deletions doxygen/lang/900_release_notes.dox.tmpl
Expand Up @@ -18,10 +18,15 @@

@subsection qore_0944_bug_fixes Bug Fixes in Qore
- <a href="../../modules/Mapper/html/index.html">Mapper</a> module updates:
- fixed a bug where mapper output data was not logged in case of an error in an output provider
(<a href="https://github.com/qorelanguage/qore/issues/3909">issue 3909</a>)
- updated mapper key handler calling convertion + docs
(<a href="https://github.com/qorelanguage/qore/issues/3894">issue 3894</a>)
- fixed \c Mapper::mapAuto() to return @ref NOTHING with no input
(<a href="https://github.com/qorelanguage/qore/issues/3872">issue 3872</a>)
- <a href="../../modules/SalesforceRestDataProvider/html/index.html">SalesforceRestDataProvider</a> module updates:
- fixed serializing \c DATE fields
(<a href="https://github.com/qorelanguage/qore/issues/3908">issue 3908</a>)
- fixed a bug in class copy methods with \c private:internal members
(<a href="https://github.com/qorelanguage/qore/issues/3901">issue 3901</a>)
- fixed bugs running \c qdbg
Expand Down
23 changes: 18 additions & 5 deletions examples/test/qlib/SalesforceRestClient/SalesforceRestClient.qtest
Expand Up @@ -117,8 +117,24 @@ class SalesforceTest inherits QUnit::Test {
{
AbstractDataProvider account = dp.getChildProvider("Account");

# create new account
string id = account.createRecord({"Name": "deleteme", "Type": "test-delete"}).id;
string id;
{
# create new account
hash<auto> rec += {
"Name": "deleteme",
"Type": "test-delete",
};
if (account.getRecordType().SLAExpirationDate__c) {
rec += {
"SLAExpirationDate__c": now_us(),
};
}
id = account.createRecord(rec).id;
}
on_exit {
# delete the record
assertEq(1, account.deleteRecords({"Name": "deleteme"}));
}

# search the record
recs = map $1, account.searchRecords({"Id": id}, {"limit": 1});
Expand All @@ -131,9 +147,6 @@ class SalesforceTest inherits QUnit::Test {

# update the record
assertEq(1, account.updateRecords({"Type": "delete"}, {"Id": id}));

# delete the record
assertEq(1, account.deleteRecords({"Id": id}));
}
}

Expand Down
12 changes: 8 additions & 4 deletions qlib/Mapper.qm
Expand Up @@ -303,6 +303,8 @@ m.mapData(input1); # output record hash date_begin = start_date = time
@section mapperrelnotes Release Notes

@subsection mapperv1_5_2 Mapper v1.5.2
- fixed a bug where mapper output data was not logged in case of an error in an output provider
(<a href="https://github.com/qorelanguage/qore/issues/3909">issue 3909</a>)
- added support for mapper context in mapper field key handlers
(<a href="https://github.com/qorelanguage/qore/issues/3893">issue 3893</a>)
- fixed @ref Mapper::Mapper::mapAuto() "Mapper::mapAuto()" to return @ref NOTHING with no input
Expand Down Expand Up @@ -1870,9 +1872,7 @@ Mapper mapv(DataMap);
- uses mapDataIntern() to map the data, then logOutput() is called for each output row
*/
hash<auto> mapData(hash<auto> rec) {
hash<auto> h = mapDataIntern(rec);
logOutput(h);
return h;
return mapDataIntern(rec, True);
}

#! maps all input record(s) automatically and returns the mapped data
Expand Down Expand Up @@ -2033,7 +2033,7 @@ Mapper mapv(DataMap);
- each time this method is executed successfully, the record count is updated (see @ref getCount() and @ref resetCount())
- this is the same as mapData() except no output logging is performed
*/
private hash<auto> mapDataIntern(hash<auto> rec) {
private hash<auto> mapDataIntern(hash<auto> rec, *bool do_log_output) {
*hash<auto> old_ctx = swapMapperThreadContext(mapper_thread_context);
on_exit swapMapperThreadContext(old_ctx);

Expand All @@ -2055,6 +2055,10 @@ Mapper mapv(DataMap);
# iterate through dynamic target fields
map mapFieldIntern(\h, $1, rec, False, 0), keys mapd;

if (do_log_output) {
logOutput(h);
}

# write to putput provider, if any
if (output_provider_bulk_operation) {
output_provider_bulk_operation.queueData(h);
Expand Down
Expand Up @@ -37,7 +37,7 @@
%requires(reexport) SalesforceRestClient

module SalesforceRestDataProvider {
version = "1.0";
version = "1.0.1";
desc = "user module providing a data provider API for Salesforce instances through the REST API";
author = "David Nichols <david@qore.org>";
url = "http://qore.org";
Expand Down Expand Up @@ -89,6 +89,10 @@ module SalesforceRestDataProvider {

@section salesforcerestdataprovider_relnotes Release Notes

@subsection salesforcerestdataprovider_v1_0 SalesforceRestDataProvider v1.0.1
- fixed serializing \c DATE fields
(<a href="https://github.com/qorelanguage/qore/issues/3908">issue 3908</a>)

@subsection salesforcerestdataprovider_v1_0 SalesforceRestDataProvider v1.0
- initial release of the module
*/
Expand Down
Expand Up @@ -98,6 +98,7 @@ public class SalesforceRestObjectDataProvider inherits AbstractDataProvider {
@see requestSearchRecordsImpl()
*/
private AbstractDataProviderRecordIterator searchRecordsImpl(*hash<auto> where_cond, *hash<auto> search_options) {
where_cond = fixSalesforceRecord(where_cond);
return new SalesforceRestRecordIterator(rest, name, record_info, where_cond, search_options);
}

Expand Down Expand Up @@ -183,6 +184,7 @@ public class SalesforceRestObjectDataProvider inherits AbstractDataProvider {
@return the data written to the data provider with the \c "id" field of the new record
*/
private *hash<auto> createRecordImpl(hash<auto> rec, *hash<auto> create_options) {
rec = fixSalesforceRecord(rec);
string url = sprintf("sobjects/%s", name);
hash<auto> info;
# the id is returned in the "id" field in the response
Expand Down Expand Up @@ -214,6 +216,7 @@ public class SalesforceRestObjectDataProvider inherits AbstractDataProvider {
throw "UPSERT-ERROR", sprintf("upsert request requires a value for the %y field, but none was given",
extid);
}
rec = fixSalesforceRecord(rec);
string url = sprintf("sobjects/%s/%s/%s", name, extid, extid_value);
hash<auto> info;
# the id is returned in the "id" field in the response
Expand Down Expand Up @@ -247,6 +250,7 @@ public class SalesforceRestObjectDataProvider inherits AbstractDataProvider {
updateSingleRecord(where_cond.Id, set);
return 1;
}
where_cond = fixSalesforceRecord(where_cond);
AbstractDataProviderRecordIterator i = searchRecords(where_cond, search_options);
int count = 0;
map (updateSingleRecord(i.getValue().Id, set), ++count), i;
Expand All @@ -265,6 +269,7 @@ public class SalesforceRestObjectDataProvider inherits AbstractDataProvider {
deleteSingleRecord(where_cond.Id);
return 1;
}
where_cond = fixSalesforceRecord(where_cond);
AbstractDataProviderRecordIterator i = searchRecords(where_cond, search_options);
int count;
map (deleteSingleRecord(i.getValue().Id), ++count), i;
Expand Down Expand Up @@ -374,5 +379,26 @@ public class SalesforceRestObjectDataProvider inherits AbstractDataProvider {
record_info.record_type{field.name} = new QoreDataField(field.name, field.label, type);
}
}

#! Fix salesforce records for serialization
/** Ensure that:
- \c DATE fields are serialized as YYYY-MM-DD strings
- \c TIME fields are serialized as HH:mm:SS.xx strings
*/
private *hash<auto> fixSalesforceRecord(*hash<auto> rec) {
hash<auto> rv;
foreach hash<auto> i in (rec.pairIterator()) {
if (i.value.typeCode() == NT_DATE) {
if (record_info.field_types{i.key} == "date") {
rv{i.key} = i.value.format("YYYY-MM-DD");
} else if (record_info.field_types{i.key} == "time") {
rv{i.key} = i.value.format("HH:mm:SS.xxZ");
}
} else {
rv{i.key} = i.value;
}
}
return rv;
}
}
}

0 comments on commit 666e164

Please sign in to comment.