I might be losing my mind here, but I've written a Collection-wide pre-request script in Postman for the first time and in order to try and debug it I've put a bunch of console.log() statements in. What I've found is that the value displayed in the console for one of my variables is always the same, even when I'm setting it to something else in the script.
Here's my pre-request script:
// Set Timestamp variable
var moment = require("moment");
var epochTimestamp = moment().unix().toString();
pm.collectionVariables.set("unixTimestamp", epochTimestamp);
// Set ClientIdHash variable
var clientId = pm.environment.get("apiClientId");
var clientSecret = pm.environment.get("apiClientSecret");
var unhashed = `${clientId.toLowerCase()}.${epochTimestamp.toLowerCase()}.${clientSecret.toLowerCase()}`;
console.log("Unhashed: " + unhashed);
var bytes = strToUtf8Bytes(unhashed);
console.log("Bytes: " + bytes);
var bytesHash = CryptoJS.SHA512(bytes);
console.log("Bytes Hash: " + bytesHash);
var clientIdHash = bytesHash.toString(CryptoJS.enc.Hex);
console.log("Client ID Hash: " + clientIdHash);
pm.collectionVariables.set("clientIdHash", clientIdHash);
function strToUtf8Bytes(str) {
const utf8 = [];
for (let ii = 0; ii < str.length; ii++) {
let charCode = str.charCodeAt(ii);
if (charCode < 0x80) utf8.push(charCode);
else if (charCode < 0x800) {
utf8.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
} else if (charCode < 0xd800 || charCode >= 0xe000) {
utf8.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
} else {
ii++;
// Surrogate pair:
// UTF-16 encodes 0x10000-0x10FFFF by subtracting 0x10000 and
// splitting the 20 bits of 0x0-0xFFFFF into two halves
charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (str.charCodeAt(ii) & 0x3ff));
utf8.push(
0xf0 | (charCode >> 18),
0x80 | ((charCode >> 12) & 0x3f),
0x80 | ((charCode >> 6) & 0x3f),
0x80 | (charCode & 0x3f),
);
}
}
return utf8;
}
This executes as expected when I send a request. The problem is that my API requests are always rejected because the clientIdHash is wrong, which is why I put in the console.log() statements to try and debug it.
What I found is that whatever value I set for the unhashed variable, the value of the bytesHash variable is always the same.
For example, here is the console output for the above script with a "real" value for the unhashed variable (obviously not actually real as the clientId and clientSecret values are dummies):
And here's the console output if I change the unhashed value to "Literally anything":
As you can see, the console shows that the values of the unhashed and bytes values change, as you'd expect, but the values of the bytesHash and clientIdHash remain the same, even though they're derived from the bytes variable!
I've tried restarting Postman, updating it to the latest version (8.4), and deleting all of the files in c:\users\myusername\appdata\roaming\postman\cache, all to no avail. Across all of these attempts, whatever value I set the unhashed variable to, the bytesHash variable's value is always exactly as it appears in those screenshots.
I'm fully prepared for someone to point out to how I'm being monumentally stupid here, but at the moment I can't see why this is happening!
Turns out that I was being pretty stupid. The CryptoJS function SHA512() accepts "either strings or instances of CryptoJS.lib.WordArray," according to the docs, not a byte array, as I was trying to pass. I still wish it'd thrown an exception though, rather than presumably returning some sort of error response as a hash!
Related
as basic as this may sound I am having difficulty writing this. I have two columns with checkboxes in a sheet(main) and I want to be able to checkbox(true) column 'O' if column 'm' has a checkmark after I am done with the sheet(macro button).
Thanks for any input.
If M is true set O to true
function lfunko() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const [hA,...vs] = sh.getDataRange().getValues();
vs.forEach((r,i) => {
if(r[12] == "TRUE") {
sh.getRange(i + 2, 15).setValue("TRUE");
}
})
}
Good afternoon all,
I'm trying to call all of the results within an API that has:
6640 total records
100 records per page
67 pages of results (total records / records per page)
This is an ever growing list so I've used variables to create the above values.
I can obviously use the $Skip ODATA expression to get any one of the 67 pages by adding the expression to the end of the URL like so (which would skip the first 100, therefore returning the 2nd page:
https://psa.pulseway.com/api/servicedesk/tickets/?$Skip=100
What I'm trying to do though is to create a custom function that will loop through each of the 67 calls, changing the $Skip value by an increment of 100 each time.
I thought I'd accomplished the goal with the below code:
let
Token = "Token",
BaseURL = "https://psa.pulseway.com/api/",
Path = "servicedesk/tickets/",
RecordsPerPage = 100,
CountTickets = Json.Document(Web.Contents(BaseURL,[Headers = [Authorization="Bearer " & Token],RelativePath = Path & "count"])),
TotalRecords = CountTickets[TotalRecords],
GetJson = (Url) =>
let Options = [Headers=[ #"Authorization" = "Bearer " & Token ]],
RawData = Web.Contents(Url, Options),
Json = Json.Document(RawData)
in Json,
GetPage = (Index) =>
let Skip = "$Skip=" & Text.From(Index * RecordsPerPage),
URL = BaseURL & Path & "?" & Skip,
Json = GetJson(URL)
in Json,
TotalPages = Number.RoundUp(TotalRecords / RecordsPerPage),
PageIndicies = {0.. TotalPages - 1},
Pages = List.Transform(PageIndicies, each GetPage(_))
in
Pages
I got all happy when it successfully made the 67 API calls and combined the results into a list for me to load in to a Power Query table, however what I'm actually seeing is the first 100 records repeated 67 times.
That tells me that my GetPage custom function which handles the $Skip value isn't changing and is stuck on the first one. To make sure the Skip index was generating them properly I duplicated the query and changed the code to load in the $Skip values and see what they are, expecting them all to be $Skip=0, what I see though is the correct $Skip values as below:
Image showing correct Skip values
It seems everything is working as it should be, only I'm only getting the first page 67 times.
I've made a couple of posts on other community site around this issue before but I realise the problem I was (poorly) describing was far too broad to get any meaningful assistance. I think now I've gotten to the point where I understand what my own code is doing and have really zoomed in to the problem - I just don't know how to fix it when I'm at the final hurdle...
Any help/advice would be massively appreciated. Thank you.
Edit: Updated following #RicardoDiaz answer.
let
// Define base parameters
Filter = "",
Path = "servicedesk/tickets/",
URL = "https://psa.pulseway.com/api/",
Token = "Token",
Limit = "100",
// Build the table based on record start and any filters
GetEntityRaw = (Filter as any, RecordStart as text, Path as text) =>
let
Options = [Headers=[ #"Authorization" = "Bearer " & Token ]],
URLbase = URL & Path & "?bearer=" & Token & "&start=" & RecordStart & "&limit=" & Text.From(Limit),
URLentity = if Filter <> null then URLbase & Filter else URLbase,
Source = Json.Document(Web.Contents(URLentity, Options)),
Result = Source[Result],
toTable = Table.FromList(Result, Splitter.SplitByNothing(), null, null, ExtraValues.Error)
in
toTable,
// Recursively call the build table function
GetEntity = (optional RecordStart as text) as table =>
let
result = GetEntityRaw(Filter, RecordStart, Path),
nextStart = Text.From(Number.From(RecordStart) + Limit),
nextTable = Table.Combine({result, #GetEntity(nextStart)}),
check = try nextTable otherwise result
in
check,
resultTable = GetEntity("0")
in
resultTable
As I couldn't test your code, it's kind of hard to provide you a concrete answer.
Said that, please review the generic code I use to connect to an api and see if you can find where yours is not working
EDIT: Changed api_record_limit type to number (removed the quotation marks)
let
// Define base parameters
api_url_filter = "",
api_entity = "servicedesk/tickets/",
api_url = "https://psa.pulseway.com/api/",
api_token = "Token",
api_record_limit = 500,
// Build the table based on record start and any filters
fx_api_get_entity_raw = (api_url_filter as any, api_record_start as text, api_entity as text) =>
let
api_url_base = api_url & api_entity & "?api_token=" & api_token & "&start=" & api_record_start & "&limit=" & Text.From(api_record_limit),
api_url_entity = if api_url_filter <> null then api_url_base & api_url_filter else api_url_base,
Source = Json.Document(Web.Contents(api_url_entity)),
data = Source[data],
toTable = Table.FromList(data, Splitter.SplitByNothing(), null, null, ExtraValues.Error)
in
toTable,
// Recursively call the build table function
fxGetEntity = (optional api_record_start as text) as table =>
let
result = fx_api_get_entity_raw(api_url_filter, api_record_start, api_entity),
nextStart = Text.From(Number.From(api_record_start) + api_record_limit),
nextTable = Table.Combine({result, #fxGetEntity(nextStart)}),
check = try nextTable otherwise result
in
check,
resultTable = fxGetEntity("0"),
expandColumn = Table.ExpandRecordColumn(
resultTable,
"Column1",
Record.FieldNames(resultTable{0}[Column1]),
List.Transform(Record.FieldNames(resultTable{0}[Column1]), each _)
)
in
expandColumn
QUESTION TO OP:
Regarding this line:
Result = Source[Result],
Does the json return a field called result instead of data?
I'm investigate too much on _gid and _ga. And as i know, the definition of them:
_ga: used to identify unique users and it expires after 2 years.
_gid: used to identify unique users and it expires after 24 hours.
Example:
_ga: GA1.3.292651669.1502954402
_gid: GA1.3.974792242.1509957189
From what are the values in _ga cookie?, I know the meaning of each filed in _ga. And it's the same for _gid.
But I don't know how to generate third field, random generated user ID(for _ga:292651669 and for _gid: 974792242).
I tried to delete both _ga & _gid and I get the new couple _ga: GA1.3.2097663971.1509959880 and _gid: GA1.3.1180999143.1509959880. The third fields of both are changed. So how can they generate and how google identify user by them. Assumption I open browser today with a couple of _gid and _ga, and tomorrow, I cleared all cookies, ga will create of new couplw (_gid,_ga).; It means I'm in today that different tomorrow's me.
Please help me.
Thanks & Best Regards,
Jame
Here is the code from analytics.js release 2017-09-21 that generates the random id.
It's a random value based on current time, userAgent, cookie, referrer and history.length from the window object.
var O = window;
// var M = document;
// Stack Overflow doesn't allow cookie access in sandbox, so lets fake it:
var M = [];
M.cookie = "cookieName=someValue";
var hd = function() {
return Math.round(2147483647 * Math.random())
}
function La(a) {
var b = 1, c;
if (a)
for (b = 0,
c = a.length - 1; 0 <= c; c--) {
var d = a.charCodeAt(c);
b = (b << 6 & 268435455) + d + (d << 14);
d = b & 266338304;
b = 0 != d ? b ^ d >> 21 : b
}
return b
}
var ra = function() {
for (var a = O.navigator.userAgent + (M.cookie ? M.cookie : "") + (M.referrer ? M.referrer : ""), b = a.length, c = O.history.length; 0 < c; )
a += c-- ^ b++;
return [hd() ^ La(a) & 2147483647, Math.round((new Date).getTime() / 1E3)].join(".")
}
document.getElementById('output').innerText = ra();
<h3>Output:</h3>
<pre id="output"></pre>
So, basically, in Excel, I have 4 columns of data (all with strings) that I want to process, and want to have the results in another column, like this (nevermind the square brackets, they just represent cells):
Line Column1 Column2 Column3 Column4 Result
1: [a] [b] [k] [YES] [NO]
2: [a] [c] [l] [YES] [NO]
3: [b] [e] [] [YES] [NO]
4: [c] [e] [f] [NO] [NO]
5: [d] [h] [b] [NO] [NO]
6: [d] [] [w] [NO] [NO]
7: [e] [] [] [YES] [NO]
8: [j] [m] [] [YES] [YES]
9: [j] [] [] [YES] [YES]
10: [] [] [] [YES] [YES]
The process that I want the data to go through is this:
Assume that CheckingLine is the Line for which I currently want to calculate the value of Result, and that CurrentLine is any Line (except CheckingLine) that I am using to calculate the value of Result, at a given moment.
If Column4[CheckingLine] is "NO", Result is "NO" (simple enough, no help needed);
Example: CheckingLine = 1 -> Column4[1] = "NO" -> Result = "NO";
Else, I want to make sure that all Lines that share a common value with CheckingLine (in any Column between 1 and 3), also have Column4 as "YES" (Doing that would be simple enough even without VBA - in fact, I started by doing it in plain Excel and realised that it wasn't what I wanted) - if that happens, Result is "YES";
Example: CheckingLine = 8 -> Only shared value is "j" -> CurrentLine = 9 -> Column4[9] = "YES" -> Result = "YES";
Here's the tricky part: If one of those lines has any value (again, in any Column between 1 and 3) that IS NOT shared with CheckingLine, I want to do the whole process (restart at 1.), but checking the CurrentLine instead.
Example: CheckingLine = 2, "a" is shared with Line 1, c is shared with Line 4 -> CurrentLine = 1 -> Column4[1] = "YES", but "b" and "k" are not shared with CheckingLine -> CheckingLine' = 1 -> "b" is shared with Line 5 -> Column4[5] = "NO" -> Result = "NO";
I have written the corresponding C++ code (which works) (and it could have been in any other language, C++ was just the one I was using at the moment) (and the code HAS NOT been optimized in any way, because it's purpose was to be AS CLEAR about its functionality AS POSSIBLE) (the table above is the actual result of running it):
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> column1, column2, column3, column4, contentVector;
unsigned int location, columnsSize;
void InsertInVector(std::string Content)
{
if(Content == "")
{
return;
}
for(unsigned int i = 0; i < contentVector.size(); i++)
{
if(contentVector[i] == Content)
{
return;
}
}
contentVector.push_back(Content);
}
std::string VerifyCurrentVector(unsigned int Start)
{
std::string result = "";
if(contentVector.size() == 0)
{
result = "YES";
}
else
{
unsigned int nextStart = contentVector.size();
for(unsigned int i = 0; i < columnsSize; i++)
{
if(i != location)
{
for(unsigned int j = Start; j < nextStart; j++)
{
if(column1[i] == contentVector[j])
{
InsertInVector(column2[i]);
InsertInVector(column3[i]);
}
else if(column2[i] == contentVector[j])
{
InsertInVector(column1[i]);
InsertInVector(column3[i]);
}
else if(column3[i] == contentVector[j])
{
InsertInVector(column1[i]);
InsertInVector(column2[i]);
}
}
}
}
if(nextStart == contentVector.size())
{
for(unsigned int i = 0; i < columnsSize; i++)
{
if(i != location)
{
for(unsigned int j = 0; j < nextStart; j++)
{
if(column1[i] == contentVector[j] || column2[i] ==
contentVector[j] || column3[i] == contentVector[j])
{
if(column4[i] == "NO")
{
result = "NO";
return result;
}
}
}
}
}
result = "YES";
}
else
{
result = VerifyCurrentVector(nextStart);
}
}
return result;
}
std::string VerifyCell(unsigned int Location)
{
std::string result = "";
location = Location - 1;
if(column4.size() < Location)
{
result = "Error";
}
else if(column4[location] == "NO")
{
result = "NO";
}
else
{
contentVector.clear();
InsertInVector(column1[location]);
InsertInVector(column2[location]);
InsertInVector(column3[location]);
result = VerifyCurrentVector(0);
}
return result;
}
void SetUpColumns(std::vector<std::string> &Column1, std::vector<std::string> &Column2,
std::vector<std::string> &Column3, std::vector<std::string> &Column4)
{
if(Column4.size() > Column1.size())
{
for(unsigned int i = Column1.size(); i < Column4.size(); i++)
{
Column1.push_back("");
}
}
if(Column4.size() > Column2.size())
{
for(unsigned int i = Column2.size(); i < Column4.size(); i++)
{
Column2.push_back("");
}
}
if(Column4.size() > Column3.size())
{
for(unsigned int i = Column3.size(); i < Column4.size(); i++)
{
Column3.push_back("");
}
}
column1 = Column1;
column2 = Column2;
column3 = Column3;
column4 = Column4;
columnsSize = Column4.size();
}
int main()
{
std::vector<std::string> Column1, Column2, Column3, Column4;
Column1.push_back("a");
Column1.push_back("a");
Column1.push_back("b");
Column1.push_back("c");
Column1.push_back("d");
Column1.push_back("d");
Column1.push_back("e");
Column1.push_back("j");
Column1.push_back("j");
Column2.push_back("b");
Column2.push_back("c");
Column2.push_back("e");
Column2.push_back("e");
Column2.push_back("h");
Column2.push_back("");
Column2.push_back("");
Column2.push_back("m");
Column3.push_back("k");
Column3.push_back("l");
Column3.push_back("");
Column3.push_back("f");
Column3.push_back("b");
Column3.push_back("w");
Column4.push_back("YES");
Column4.push_back("YES");
Column4.push_back("YES");
Column4.push_back("NO");
Column4.push_back("NO");
Column4.push_back("NO");
Column4.push_back("YES");
Column4.push_back("YES");
Column4.push_back("YES");
Column4.push_back("YES");
SetUpColumns(Column1, Column2, Column3, Column4);
std::cout << "Line\t" << "Column1\t" << "Column2\t" << "Column3\t" << "Column4\t" <<
std::endl;
for(unsigned int i = 0; i < Column4.size(); i++)
{
std::cout << i + 1 << ":\t" << "[" << column1[i] << "]\t[" << column2[i] <<
"]\t[" << column3[i] << "]\t[" << column4[i] << "]\t[" << VerifyCell(i + 1)
<< "]" << std::endl;
}
return 0;
}
So, after this lengthy explanation, what I want to know is this:
Is there any way to do this in Excel's VBA (or even better, in plain Excel without VBA)?
If not, how can I have my code (which I can easily translate to another C-like language and/or optimise) get the data from, and deliver the results to, Excel?
Is there any way to do this in Excel's VBA?
Yes, you can surely do this with VBA, it is a complete and powerful programming language
(or even better, in plain Excel without VBA)?
Nope. The calculation seems too complicated to fit with Excel formulae without any VBA code.
If not, how can I have my code (which I can easily translate to another C-like language and/or optimise) get the data from, and deliver the results to, Excel?
You can access Excel from C++ in many ways. Using ATL is one of them. another, easier way would be to import/export your Excel file in CSV format, which is easy to parse and write from C++.
Also consider C#, it has complete COM inter-operability to access office components.
Ok, if you like to "whipped the code in a rush" then you'll love VBA, next time please try to ask a more specific question. Based on code and comments #MikeAscended you're a relatively good programmer, with a grasp of functions/recursion, variable/parameters, conditions, loops, data structures, etc. Re: " I have only touched VBA once in my life and ran away from it" My intent is to get you started and give you syntax here not necessarily a working solution. I'm happy to answer any further specific questions you may continue to have.
Strategy-wise,
I recommend plain VBA which is easy to use in Excel. Obviously your problem can be solved in many ways including formulas, however VBA is a powerful tool that any programmer will benefit from using.
Code-wise,
To start access the editor from Excel press [Alt-F11], or from Design Mode insert and double-click an ActiveX button. To run a macro press [Alt-F8], or in VBA click the green play button.
One last note, if you want those line numbers in column 1 in excel then yours will become Column 2-5 or B-F. I'm assuming you'll use the row numbers in excel so that Column 1 is A, but row 1 will still have titles, so you are staring your data on row 2.
sub processResults_Col5()
' Run This Script as Main()
dim rowCount as long, i as long 'rowCount = columnsSize
with sheets(1)
.Range("A1:D1") = Array("a", "b", "k", "YES")
' finish init here
' SetUpColumns not necessary in excel
if .cells(2,1).value <> "" then 'do not use .end(xldown) if data is missing
rowCount = .cells(1,1).end(xldown).row
for i = 1 to rowCount
.cells(i,5) = verifyCell(i + 1, rowCount)
next i
endif 'space will be added :p
end with
end sub
function verifyCell(rowLocation as long, size as long, optional wSh as excel.worksheet) as string
' the rest should be easy for you to figure out based on C-code
with wSh
if wsh is nothing then set wsh = activesheet 'let VBA capitalize stuff so you know you typed it correctly
if size < rowlocation then
verifyCell = "Error" 'the function name is the return value
'msgbox "Error" ' you can uncomment this line to see error
elseif cells(rowLocation, 4).value = "NO" then
cells(rowLocation, 5) = "NO" 'set result
else
call InsertInVector(rowLocation) 'CheckingLine
' edit the current rowLocation with for loops
verifyCell = VerifyCurrentVector(0) 'whatever you're doing here
endif
end with
end function
sub InsertInVector()
end sub
sub VerifyCurrentVector() 'function returns a value
end sub
Some tips:
Generally, Comment Your Code!
Generally, The first word/acronym of Variable and Object names should start in lowercase, then continue in camel-case. This helps distinguish them from library types.
In VBA always put [option explicit] in the beginning of every sheet/module, this requires you to [dim varName as Type] which will help debugging and make your code more explicit so it's easy to understand.
In VBA for numbers use type Long, learn early vs late-binding. If you're instantiating any object that requires a reference/library, always state it explicitly. This includes Excel.Worksheet, Excel.Workbook, etc. (eg. you may want your code in MS Access)
In Office One of the first settings you're going to want to disable is the popup error window, also use debug.print and the immediate box a few times.
Generally, as you know from C++ take your time, try to write correct code on your the first try as this will save you debugging time. Try not to rush and keep coffee & healthy snacks on hand. Good luck and have fun :)
I am getting input from user as string, The string is converted into hex and that values are printed.. the code is as follows..
STX = input("enter STX Value")
Deviceid = input("enter device id")
subid = input("enter address of the Device and load details")
Comnd = "41"
Data = "01"
load = input("enter the load should be on or OFF,if on type 01 other wise type 00")
Eor = hex(int(STX, 16) ^ int(Deviceid, 16) ^ int(subid, 16) ^ int(Comnd, 16) ^ int(Data, 16) ^ int(load, 16))
Addsum = int(STX, 16)+int(Deviceid, 16)+int(subid, 16)+int(Comnd, 16)+int(Data, 16)+int(load, 16)+int(Eor, 16)
The output of the program is as follows
enter STX Value"F7"
enter device id"03"
enter address of the Device and load details"83"
enter the load should be on or OFF,if on type 01 other wise type 00"01"
0x36
the add sum is
0x1f6
In the last line 0x1f6 i want omit '1'. how can i do that.. thanks for your help...
You can use the modulo operator % to get the last two digits and then convert that number back to hexadecimal format, using either the hex function or a format string.
>>> m = 0x106 % 16**2
>>> print("0x%02x" % m)
0x06