Parsing data with e.parameters - google-apps-script

I'm receiving the data from the JavaScript client in the format of:
type ContactDetails struct {
Email string
Subject string
Message string
Color1 []string
Color2 []string
}
The data had been sent from the PostForm as:
Post from website! r.PostFrom = map[color1:[my color 1 - 1 my color 1 - 2] color2:[my color 2 - 1 my color 2 - 2] email:[my email] message:[my message] subject:[my subject]]
So, I thought the best way to handle it the Google Apps script is using e.parameters not e.parameter, pls correct me if this is wrong approach.
In my GAS web app, only first field in both color1 and color2 had been posted in the related spreadsheet columns, though I expected to see array of 2 elements in each.
Sure, "Result of the code" is what I see after executed the codes in the question, "Expected result" is what I was expecting the code in the question will give as an output. The "Targeted final result" is the final result I'm working to approach, I think if I fixed the code to give the "Expected result", then it should not be difficult to tune it to generate the final output as needed in the format of "Targeted final result"
My GAS script is:
function doPost(e){
output = handleResponse(e)
}
function handleResponse(e) {
var result, message;
// Prevent concurrent access overwriting data
// we want a public lock, one that locks for all invocations
var lock = LockService.getPublicLock();
lock.waitLock(1000); // wait 1 seconds before conceding defeat.
// As we are passing JSON in the body, we need to unpairse it
var jsonString = e.postData.getDataAsString();
e.parameters = JSON.parse(jsonString);
try {
// next set where we write the data - you could write to multiple/alternate destinations
var doc = SpreadsheetApp.openById(FILE_Id);
var sheet = doc.getSheetByName(DATA_SHEET);
// we'll assume header is in row 1 but you can override with header_row in GET/POST data
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var lastRow = sheet.getLastRow()
var nextRow = lastRow + 1; // get next row
var row = [];
if(lastRow < 10){
RefID = "PRF.00" + lastRow
} else {
if(lastRow < 100){
RefID = "PRF.0" + lastRow
} else {
RefID = "PRF." + lastRow
}
}
// loop through the header columns
for (i in headers){
if (headers[i] == "Ref"){ // special case if you include a 'Timestamp' column
row.push(RefID);
} else { // else use header name to get data
if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
row.push(new Date());
} else { // else use header name to get data
row.push(e.parameters[headers[i]]);
}
}
}
// more efficient to set values as [][] array than individually
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// return json success results
result = true;
message = link;
} catch(e){
// if error return this
result = false;
message = e;
} finally { //release lock
lock.releaseLock();
var output = JSON.stringify({"result":result, "message": message});
}
return output;
}
I'm publishing my GAS a:
If info about my client side is required, here the details:
I'm using GO lang as below;
// go build -ldflags "-H=windowsgui"
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"text/template"
"github.com/zserge/lorca"
)
// ContactDetails ...
type ContactDetails struct {
Email string
Subject string
Message string
Color1 []string
Color2 []string
}
// ReturnedResult ...
type ReturnedResult struct {
Result bool `json:"result"`
Message string `json:"message"`
}
func index(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFiles("forms.html"))
if r.Method != http.MethodPost {
tmpl.Execute(w, nil)
return
}
r.ParseForm()
details := ContactDetails{
Email: r.FormValue("email"),
Subject: r.FormValue("subject"),
Message: r.FormValue("message"),
Color1: r.Form["color1"], // as "color1" is array,
Color2: r.Form["color2"], // as "color2" is array,
}
fmt.Printf("Post from website! r.PostFrom = %v\n", r.PostForm)
sheetID := "AKfycbxfMucXOzX15tfU4errRSAa9IzuTRbHzvUdRxzzeYnNA8Ynz8LJuBuaMA/exec"
url := "https://script.google.com/macros/s/" + sheetID + "/exec"
bytesRepresentation, err := json.Marshal(details)
if err != nil {
log.Fatalln(err)
}
resp, err := http.Post(url, "application/json", bytes.NewBuffer(bytesRepresentation))
if err != nil {
log.Fatalln(err)
}
// read all response body
data, _ := ioutil.ReadAll(resp.Body)
// close response body
resp.Body.Close()
webReturn := ReturnedResult{}
if err := json.Unmarshal([]byte(data), &webReturn); err != nil {
panic(err)
}
fmt.Println(webReturn.Message)
//tmpl.Execute(w, struct{ Success bool }{webReturn.Result})
tmpl.Execute(w, webReturn)
}
func main() {
// Start Host goroutine
go func() {
http.HandleFunc("/", index)
http.ListenAndServe(":8090", nil)
}()
// Start UI
ui, err := lorca.New("http://localhost:8090/index", "", 480, 320)
if err != nil {
log.Fatal(err)
}
defer ui.Close()
<-ui.Done()
}
With the html in the below template forms.html
<title>Form Submittal</title>
<h1>Contact</h1>
<form method="POST">
<label>Email:</label><br />
<input type="text" name="email"><br />
<label>Subject:</label><br />
<input type="text" name="subject"><br />
<label>Message:</label><br />
<textarea name="message"></textarea><br />
<table>
<tr>
<td><input type="text" name="color1" /></td>
<td><input type="text" name="color2" /></td>
</tr>
<tr>
<td><input type="text" name="color1" /></td>
<td><input type="text" name="color2" /></td>
</tr>
<table>
<input type="submit">
</form>
{{if .Result}}
<div id='foo'>
<a href={{.Message}}>Download PDF file</a>
</div>
<h1></h1>
<script>
// setTimeout(function () {document.querySelector('#foo').style.display='none'}, 5000);
</script>
{{end}}
The console output data is:
Post from website! r.PostFrom = map[color1:[my color 1 - 1 my color 1 - 2] color2:[my color 2 - 1 my color 2 - 2] email:[my email] message:[my message] subject:[my subject]]
Details = {my email my subject my message [my color 1 - 1 my color 1 - 2] [my color 2 - 1 my color 2 - 2]}
bytesRepresentation = [123 34 69 109 97 105 108 34 58 34 109 121 32 101 109 97 105 108 34 44 34 83 117 98 106 101 99 116 34 58 34 109 121 32 115 117 98 106 101 99 116 34 44 34 77 101 115 115 97 103 101 34 58 34 109 121 32 109 101 115 115 97 103 101 34 44 34 67 111 108 111 114 49 34 58 91 34 109 121 32 99 111 108 111 114 32 49 32 45 32 49 34 44 34 109 121 32 99 111 108 111 114 32 49 32 45 32 50 34 93 44 34 67 111 108 111 114 50 34 58 91 34 109 121 32 99 111 108 111 114 32 50 32 45 32 49 34 44 34 109 121 32 99 111 108 111 114 32 50 32 45 32 50 34 93 125]

In order to achieve your goal of "Expected result", how about the following modification? In this case, please modify handleResponse() of your Google Apps Script as follows.
From:
row.push(e.parameters[headers[i]]);
To:
var temp = e.parameters[headers[i]];
row.push(Array.isArray(temp) ? temp.join(",") : temp);
// Or if you want to ensure empty objects are excluded from the .join(), you can use:
// row.push(Array.isArray(temp) ? (temp.filter(value => Object.keys(value).length !== 0)).join(",") : temp);
Note:
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.

Related

iOS 15 Crash when initialize NSAttributedString with HTML string

I have an extension to convert HTML string to NSAttributedString.
After iOS 15 is released, I've seen a lot of crashes from my user. All those crashes are on iOS 15 and occur on the main thread.
Here is a typical crash report from Crashlytics.
Crashed: com.apple.main-thread
0 libsystem_platform.dylib 0x1f29d60c0 _os_unfair_lock_recursive_abort + 36
1 libsystem_platform.dylib 0x1f29d0a10 _os_unfair_lock_lock_slow + 304
2 Foundation 0x183d5e730 -[NSProcessInfo(NSProcessInfoHardwareState) isLowPowerModeEnabled] + 68
3 WebCore 0x192011004 <redacted> + 56
4 CoreFoundation 0x182535ee8 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 28
5 CoreFoundation 0x1825d1b9c ___CFXRegistrationPost_block_invoke + 52
6 CoreFoundation 0x1825a4f54 _CFXRegistrationPost + 456
7 CoreFoundation 0x18254bd54 _CFXNotificationPost + 716
8 Foundation 0x183d50028 -[NSNotificationCenter postNotificationName:object:userInfo:] + 96
9 Foundation 0x183dc89d4 NSProcessInfoNotifyPowerState + 188
10 Foundation 0x183d5e768 -[NSProcessInfo(NSProcessInfoHardwareState) isLowPowerModeEnabled] + 124
11 WebCore 0x191595118 <redacted> + 96
12 WebCore 0x19201116c WebCore::LowPowerModeNotifier::LowPowerModeNotifier(WTF::Function<void (bool)>&&) + 52
13 WebCore 0x192f36c4c WebCore::Page::Page(WebCore::PageConfiguration&&) + 1848
14 WebKitLegacy 0x1a605ae34 -[WebView(WebPrivate) _commonInitializationWithFrameName:groupName:] + 3060
15 WebKitLegacy 0x1a605a214 -[WebView(WebPrivate) _initWithFrame:frameName:groupName:] + 116
16 UIFoundation 0x18d306028 -[NSHTMLReader _loadUsingWebKit] + 832
17 UIFoundation 0x18d30715c -[NSHTMLReader attributedString] + 32
18 UIFoundation 0x18d2c04e8 _NSReadAttributedStringFromURLOrData + 8420
19 UIFoundation 0x18d2be378 -[NSAttributedString(NSAttributedStringUIFoundationAdditions) initWithData:options:documentAttributes:error:] + 156
20 MyAppExtensions 0x108aa0fc4 _hidden#912_ + 4374482884 (__hidden#360_:4374482884)
21 MyAppExtensions 0x108aa0894 NSAttributedString.init(htmlString:font:useDocumentFontSize:) + 36 (__hidden#925_:36)
22 MyAppName 0x1045eca18 {foo_class}.convertHTML(_:) + 670 (xxxx.swift:670)
23 MyAppName 0x1045e5c58 {foo_class}.some_function_3 + 620 (xxxx.swift:620)
24 MyAppName 0x1045e4760 {foo_class}.some_function_2 + 212 (xxxx.swift:212)
25 MyAppName 0x1045e4200 {foo_class}.some_function_1 + 138 (xxxx.swift:138)
.
.
.
42 UIKitCore 0x184b5e1d0 _UIGestureRecognizerSendTargetActions + 116
43 UIKitCore 0x184b2705c _UIGestureRecognizerSendActions + 284
44 UIKitCore 0x184b60580 -[UIGestureRecognizer _updateGestureForActiveEvents] + 636
45 UIKitCore 0x184b186fc _UIGestureEnvironmentUpdate + 1988
46 CoreFoundation 0x18254c570 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 36
47 CoreFoundation 0x18251b854 __CFRunLoopDoObservers + 572
48 CoreFoundation 0x1825168ec __CFRunLoopRun + 1052
49 CoreFoundation 0x18252a3c8 CFRunLoopRunSpecific + 600
50 GraphicsServices 0x19dd3b38c GSEventRunModal + 164
51 UIKitCore 0x184ed00bc -[UIApplication _run] + 1100
52 UIKitCore 0x184c4dbe8 UIApplicationMain + 2124
53 libswiftUIKit.dylib 0x199afe184 UIApplicationMain(_:_:_:_:) + 104
54 MyAppName 0x1043a5894 main + 7 (main.swift:7)
55 dyld 0x106b81a24 start
The following is code of converting HTML to NSAttributedString:
extension NSAttributedString {
convenience public init(htmlString html: String, font: UIFont? = nil, useDocumentFontSize: Bool = true) throws {
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
]
guard let data = html.data(using: .utf8, allowLossyConversion: true) else {
self.init(string: "")
return
}
guard let fontFamily = font?.familyName else {
try self.init(data: data, options: options, documentAttributes: nil)
return
}
let attr = try NSMutableAttributedString(data: data, options: options, documentAttributes: nil)
let fontSize: CGFloat? = useDocumentFontSize ? nil : font!.pointSize
let range = NSRange(location: 0, length: attr.length)
attr.enumerateAttribute(.font, in: range, options: .longestEffectiveRangeNotRequired) { attrib, range, _ in
if let htmlFont = attrib as? UIFont {
let traits = htmlFont.fontDescriptor.symbolicTraits
var descrip = htmlFont.fontDescriptor.withFamily(fontFamily)
if (traits.rawValue & UIFontDescriptor.SymbolicTraits.traitBold.rawValue) != 0 {
descrip = descrip.withSymbolicTraits(.traitBold)!
}
if (traits.rawValue & UIFontDescriptor.SymbolicTraits.traitItalic.rawValue) != 0 {
descrip = descrip.withSymbolicTraits(.traitItalic)!
}
attr.addAttribute(.font, value: UIFont(descriptor: descrip, size: fontSize ?? htmlFont.pointSize), range: range)
}
}
self.init(attributedString: attr)
}
Btw, I'm using Xcode 12.5 to archive the release build.
Discussion about this crash in the apple developer forum here:
You might need to avoid using NSAttributedString(data: , options: [.documentType: NSAttributedString.DocumentType.html] itself and parse the HTML string to generate attribute string by yourself. Perhaps,you can use some open-source projects on GitHub such as DTCoreText.

Go: Reading JSON file vs. Reading JSON string

I have a JSON file called example.json that looks like:
{
"name": "example",
"type": "record"
}
I also have a variable representing the above as a "string":
const example = `{
"name": "example",
"type": "record",
}`
I am trying to understand why reading the contents of the JSON file into bytes is different from reading the contents of the example variable. My code is as follows:
bytesJSON, err := ioutil.ReadFile("example.json")
if err != nil {
fmt.Println(err)
}
bytesVar, err := json.Marshal(example)
if err != nil {
fmt.Println(err)
}
Both are of the type []uint8, but look very different. Any ideas on why? And how I can make sure that they are always the same?
EDIT: Even using bytesVar := []byte(example) results in the same issue.
EDIT:
bytesJSON looks like:
[123 10 32 32 32 32 34 110 97 109 101 34 58 32 34 101 120 97 109 112 108 101 34 44 10 32 32 32 32 34 116 121 112 101 34 58 32 34 114 101 99 111 114 100 34 10 125]
bytesVar looks like:
[34 112 117 98 115 117 98 95 101 120 97 109 112 108 101 95 116 111 112 105 99 34]
when printed to stdout.
Note: The "edit" output in the question is using different example input than in the question.
If we print them as strings it becomes clear.
fmt.Println(string(bytesJSON))
{
"name": "example",
"type": "record",
}
ioutil.ReadFile is just what's in the file.
fmt.Println(string(bytesVar))
"{\n \"name\": \"example\",\n \"type\": \"record\",\n }"
json.Marshal has encoded the string example as JSON. That is a JSON string containing a string.
The equivalent to ioutil.ReadFile("example.json") is simply example.
If we unmarshal bytesVar we get back the original string in example.
var unmarshal string;
json.Unmarshal(bytesVar,&unmarshal)
fmt.Println(unmarshal)
{
"name": "example",
"type": "record",
}

Why do I convert map to json, map includes list values which is nothing after converting to json

func Test_JsonTtransfer(t *testing.T) {
uid := "306"
phoneList := list.New()
phoneList.PushBack("18513622928")
fmt.Println("phoneList=======", phoneList.Len())
jsonPhoneList, err := json.Marshal(phoneList)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println("jsonPhoneList=======", string(jsonPhoneList))
idCardList := list.New()
idCardList.PushBack("230405197608040640")
request := make(map[string]interface{})
request["uid"] = uid
request["phones"] = phoneList
request["id_cards"] = idCardList
json, err := json.Marshal(request)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println("json=======", json)
fmt.Println("json=======", string(json))
}
Output:
D:/Sys/server/Go\bin\go.exe test -v golang-test/com/http/test -run ^Test_JsonTtransfer$
phoneList======= 1
jsonPhoneList======= {}
json======= [123 34 105 100 95 99 97 114 100 115 34 58 123 125 44 34 112 104 111 110 101 115 34 58 123 125 44 34 117 105 100 34 58 34 51 48 54 34 125]
json======= {"id_cards":{},"phones":{},"uid":"306"}
ok golang-test/com/http/test 0.482s
Phones should be list values, but nothing. Help me.
Because the List type has no exported fields and the type does not implement the Marshalerinterface, List values always marshal to the text {}.
A simple fix is to use a slice instead of a list:
var phoneList []string
phoneList = append(phoneList, "18513622928")
fmt.Println("phoneList=======", len(phoneList)
playground example

Trying to write to a Hex file(.bin) using PB12.5 using BlobEdit (Blob)

I'm trying to write to a hex file using PB12.5, I'm able to write to it without any issues but through testing noticed I will need to send a null value (00) to the file at certain points.
I know if I assign null to a string, it will null out the entire string so I tried using a Blob where I can insert a null value when needed (BlobEdit(blb_data, ll_pos, CharA(0)) )
But BlobEdit() automatically inserts a null value in between each position, I don't want this as it's causing issues as I'm trying to update the hex file. I just need to add my CharA(lb_byte) to each consecutive position in the Blob.
Is there any way around this or is PB just unable to do this? Below is the code:
ll_test = 1
ll_pos = 1
ll_length = Len(ls_output)
Do While ll_pos <= (ll_length)
ls_data = Mid(ls_output, ll_pos, 2)
lb_byte = Event ue_get_decimal_value_of_hex(ls_data)
ll_test = BlobEdit(blb_data, ll_test, CharA(lb_byte), EncodingANSI!)
ll_pos = ll_pos + 2
Loop
Hex file appears as follows:
16 35 2D D8 08 45 29 18 35 27 76 25 30 55 66 85 44 66 57 A4 67 99
After Blob update:
16 00 48 00 5D 00 C3 92 00 08 00 48 00 51 00 E2
I hope to help you:
//////////////////////////////////////////////////////////////////////////
// Function: f_longtohex
// Description: LONG to HEXADECIMAL
// Ambito: public
// Argumentos: as_number //Variable long to convert to hexadecimal
// as_digitos //Number of digits to return
// Return: String
// Example:
// f_longtohex(198 , 2) --> 'C6'
// f_longtohex(198 , 4) --> '00C6'
//////////////////////////////////////////////////////////////////////////
long ll_temp0, ll_temp1
char lc_ret
if isnull(as_digitos) then as_digitos = 2
IF as_digitos > 0 THEN
ll_temp0 = abs(as_number / (16 ^ (as_digitos - 1)))
ll_temp1 = ll_temp0 * (16 ^ (as_digitos - 1))
IF ll_temp0 > 9 THEN
lc_ret = char(ll_temp0 + 55)
ELSE
lc_ret = char(ll_temp0 + 48)
END IF
RETURN lc_ret + f_longtohex(as_number - ll_temp1 , as_digitos - 1)
END IF
RETURN ''

Why is json.Marshal seemingly producing an array of ints?

I'm trying to wrap my head around the Go language and I've hit my first stumbling block with this simple example:
package main
import (
"encoding/json"
"fmt"
)
type MyStructure struct {
Integer int `json:"integer"`
Label string `json:"label"`
}
func main() {
ms := &MyStructure{9001, "over9000"}
msjson, _ := json.Marshal(ms)
fmt.Println(msjson) // expect: {"integer": 9001, "label": "over9000"}
}
My output is as follows: [123 34 105 110 116 101 103 101 114 34 58 57 48 48 49 44 34 108 97 98 101 108 34 58 34 111 118 101 114 57 48 48 48 34 125]
I'm clearly missing something obvious; could someone please point me in the right direction?
It produces a byte slice (ref : http://golang.org/pkg/encoding/json/#Marshal), use string(msjson) to get the string.
Also never ignore errors, it bites you back whenever you least expect it.
fmt.Println(string(msjson))
// or
fmt.Printf("%s\n", msjson) //shamelessly taken from #dustin's comment