I'm trying to learn just the bare basics about suitelets in my NetSuite environment.
I have copy/pasted this code from the Help Center and I am receiving this error when clicking the URl "An unexpected error has occurred. Please click here to notify support and provide your contact information."
/**
* #NApiVersion 2.x
* #NScriptType Suitelet
*/
define([], function() {
function onRequest(context) {
var html = '<html><body><h1>Hello World</h1></body></html>';
context.response.write(html);
context.response.setHeader({
name: 'Custom-Header-Demo',
value: 'Demo'
});
}
return {
onRequest: onRequest
};
});
I have tried putting log.debugs within the script but I'm not getting anything in the execution log. I have uploaded the script, deployed, and released the suitelet but I'm still getting this error.
Error Screenshot
Tips For Success With Suitlets
1. Think like you're coding Node Express / Nextjs APIs
Set up your code to handle each request method type
/**
* #NApiVersion 2.1
* #NScriptType Suitelet
*/
define(["N/cache"], /**
* #param {cache} cache
*/
(cache) => {
/**
* Defines the Suitelet script trigger point.
* #param {Object} scriptContext
* #param {ServerRequest} scriptContext.request - Incoming request
* #param {ServerResponse} scriptContext.response - Suitelet response
* #since 2015.2
*/
const onRequest = (scriptContext) => {
let sc = scriptContext;
let req = sc.request;
let res = sc.response;
let { method, parameters, body, clientIpAddress, headers } = req;
let { write, setHeader } = res;
switch (method) {
case "GET":
// Run logic for each specific request type (Save creating files for each operation)
// Set header first then return
setHeader({
name: "Content-Type",
value: "application/json",
});
// return your server response
write({
output: "Hello World",
});
break;
case "POST":
// Add logic here for POST
break;
default:
log.error({ title: `❌ method: ${method} not supported` });
}
};
return { onRequest };
});
2. Utilize WebStorm & The SuiteCloud IDE Plugin
This will help you template out files faster and import modules and their JSDocs automatically along with giving you better IntelliSense. Along with instantly uploading your file instead of having to do it through the UI of NetSuite every time.
3. Use 2.1 Where its supported
Using 2.x is similar to ES2012 which is not the best for using let, const, and all the new ES6 functions natively available to us. Along with using string templating or literals as in the example above.
4. Use N/cache If possible
If you're fetching data like a record or ID to retrieve. Using N/cache will make the suitelet's execution much faster.
Related to your error
Try what I suggested in the code snippet. You want to set your header first before you return your response. As you're trying to set a header where the response has already been sent. Also NetSuite don't understand half the time the type of data you're sending/returning. So setting the Content-Type header will allow your response to be displayed as expected.
// If you're trying to send JSON you stringify it first before you send the data
write({
output: JSON.stringify({
text: 'Hello World'
})
});
If you are sending HTML as a response you would do the following
// Set header first for content return define
setHeader({
name: "Content-Type",
value: "text/html",
});
write({
output: '<html><body><h1>Hello World</h1></body></html>'
});
Hope this helps! 🚀
I ran the script on my Netsuite account and I didn't get the error.
Which role user did you use it to create the script and view the results? Try with the Administrator role and for sure that error is not going to be throw.
Related
Problem
I was trying to use 'aws-amplify' GET API request with query parameters on the client side, but it turned out to be Request failed with status code 403, and the response showed:
"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
Note: React.js as front-end, Javascript as back-end.
My code
Front-end
function getData() {
const apiName = 'MyApiName';
const path = '/path';
const content = {
body:{
data:'myData',
},
};
return API.get(apiName, path, content);
}
Back-end
try {
const result = await dynamoDbLib.call("query", params);
} catch (e) {
return failure({ status: false });
}
What I did to debug
The GET lambda function works fine in Amazon Console (Tested)
If I change the backend lambda function so that the frontend request can be made without parameters, i.e. return API.get(apiName, path), then no error shows up.
My question
How can I make this GET request with query parameters works?
I changed GET to POST (return API.post()), everything works fine now.
If anyone can provide a more detailed explanation, it would be very helpful.
I would like to read an info set in http response header when I make a query to GraphQL server.
When I execute a query with urql client, I only get these infos :
/** Resulting data from an [operation]{#link Operation}. */
export interface OperationResult<Data = any> {
/** The [operation]{#link Operation} which has been executed. */
operation: Operation;
/** The data returned from the Graphql server. */
data?: Data;
/** Any errors resulting from the operation. */
error?: CombinedError;
/** Optional extensions return by the Graphql server. */
extensions?: Record<string, any>;
/** Optional stale flag added by exchanges that return stale results. */
stale?: boolean;
}
Is there a way to get response headers ?
You can use a fetch wrapper in the provided client (If it's global to your app) or on in a specific client in a component with the fetch option like this:
const client = createClient({
url: `https://example.org/graphql`,
fetch: (...args) =>
fetch(...args).then((response) => {
const myHeader = response.headers.get('my-header');
if (myHeader) {
/* do stuff */
}
return response;
}),
fetchOptions: { credentials: 'include' },
…
});
The short answer is, no, urql in its default behaviour does not expose the response headers of a request as can be seen here: https://github.com/FormidableLabs/urql/blob/master/packages/core/src/internal/fetchSource.ts#L23-L29
It's simply swallowed.
The long answer is, if your replace the fetchExchange with a custom implementation you can extract whatever information you want. Unfortunately that's obviously custom and some work, but on the other hand, urql allows for you to drill into its internals and swap this behaviour out.
We have a site example.com behind ssl that runs a page with ApplePay.
We've got a server side that returns a Merchant Session that looks like the following:
{"epochTimestamp":1581975586106,"expiresAt":1581979186106,"merchantSessionIdentifier":"SSH8E666B0...","nonce":"1239e567","merchantIdentifier":"...8557220BAF491419A...","domainName":"example.com","displayName":"ApplePay","signature":"...20101310f300d06096086480165030402010500308..."}
We receive this response in session.onvalidatemerchant as a string and convert it to a Json Object and pass to session.completeMerchantValidation.
As a result we get the following error:
Code: "InvalidAccessError"
Message: "The object does not support the operation or argument"
We run the following code on our page:
.....
session.onvalidatemerchant = (event) => {
const validationURL = event.validationURL;
getApplePaySession(validationURL).then(function (response) {
try {
let resp = JSON.parse(response);
session.completeMerchantValidation(resp);
} catch (e) {
console.error(JSON.stringify(e));
}
});
};
....
Additional questions:
Is the object described above a "correct" Merchant Session opaque that needs to be passed to completeMerchantValidation or it's missing some fields?
Is this object needs to be passed as is or it needs to be base64 encoded?
Does it need to be wrapped into another object?
Any help or lead is greatly appreciated.
I'm doing the tutorial for IBM Watson Speech-to-text. In the section "Using the WebSocket interface", subsection "Opening a connection and passing credentials", I copied the following code:
var token = watsonToken;
console.log(token); // token looks good
var wsURI = 'wss://stream.watsonplatform.net/speech-to-text/api/v1/recognize?watson-token=' +
token + '&model=es-ES_BroadbandModel';
var websocket = new WebSocket(wsURI);
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
I'm using Angular so I made a value for the token:
app.value('watsonToken', 'Ln%2FV...');
I get back an error message:
WebSocket connection to 'wss://stream.watsonplatform.net/speech-to-text/api/v1/recognize?watson-toke...&model=es-ES_BroadbandModel' failed: HTTP Authentication failed; no valid credentials available
I tried hardcoding the token:
var wsURI = 'wss://stream.watsonplatform.net/speech-to-text/api/v1/recognize?watson-token=Ln%2FV2...&model=es-ES_BroadbandModel';
Same error message.
IBM's documentation on tokens says that an expired or invalid token will return a 401 error, which I didn't get, so I presume that my token is neither expired nor invalid. Any suggestions?
I think you can see the Official Example from IBM Developers here.
The error is because the authentication does not work fine before you send the request to recognize, try to follow the same step inside this repository, like:
const QUERY_PARAMS_ALLOWED = ['model', 'X-Watson-Learning-Opt-Out', 'watson-token', 'customization_id'];
/**
* pipe()-able Node.js Readable/Writeable stream - accepts binary audio and emits text in it's `data` events.
* Also emits `results` events with interim results and other data.
* Uses WebSockets under the hood. For audio with no recognizable speech, no `data` events are emitted.
* #param {Object} options
* #constructor
*/
function RecognizeStream(options) {
Duplex.call(this, options);
this.options = options;
this.listening = false;
this.initialized = false;
}
util.inherits(RecognizeStream, Duplex);
RecognizeStream.prototype.initialize = function() {
const options = this.options;
if (options.token && !options['watson-token']) {
options['watson-token'] = options.token;
}
if (options.content_type && !options['content-type']) {
options['content-type'] = options.content_type;
}
if (options['X-WDC-PL-OPT-OUT'] && !options['X-Watson-Learning-Opt-Out']) {
options['X-Watson-Learning-Opt-Out'] = options['X-WDC-PL-OPT-OUT'];
}
const queryParams = extend({ model: 'en-US_BroadbandModel' }, pick(options, QUERY_PARAMS_ALLOWED));
const queryString = Object.keys(queryParams)
.map(function(key) {
return key + '=' + (key === 'watson-token' ? queryParams[key] : encodeURIComponent(queryParams[key])); // our server chokes if the token is correctly url-encoded
})
.join('&');
const url = (options.url || 'wss://stream.watsonplatform.net/speech-to-text/api').replace(/^http/, 'ws') + '/v1/recognize?' + queryString;
const openingMessage = extend(
{
action: 'start',
'content-type': 'audio/wav',
continuous: true,
interim_results: true,
word_confidence: true,
timestamps: true,
max_alternatives: 3,
inactivity_timeout: 600
},
pick(options, OPENING_MESSAGE_PARAMS_ALLOWED)
);
This code is from IBM Developers and for my project I'm using and works perfectly.
You can see in the code line #53, set the listening to true, otherwise it will eventually timeout and close automatically with inactivity_timeout applies when you're sending audio with no speech in it, not when you aren't sending any data at all.
Have another example, see this example from IBM Watson - Watson Developer Cloud using Javascript for Speech to Text.
Elementary, my dear Watson! There are three or four things to pay attention to with IBM Watson tokens.
First, you won't get a token if you use your IBMid and password. You have to use the username and password that were provided for a project. That username is a string of letters and numbers with hyphens.
Second, the documentation for tokens gives you code for getting a token:
curl -X GET --user {username}:{password}
--output token
"https://stream.watsonplatform.net/authorization/api/v1/token?url=https://stream.watsonplatform.net/text-to-speech/api"
Part of that code is hidden on the webpage, specifically the part that says /text-to-speech/. You need to change that to the Watson product or service you want to use, e.g., /speech-to-text/. Tokens are for specific projects and specific services.
Third, tokens expire in one hour.
Lastly, I had to put in backslashes to get the code to run in my terminal:
curl -X GET --user s0921i-s002d-dh9328d9-hd923:wy928ye98e \
--output token \
"https://stream.watsonplatform.net/authorization/api/v1/token?url=https://stream.watsonplatform.net/speech-to-text/api"
I included locally in javascript a list of commonly used terms, and then I would also like to get json response from the server through ajax response. How can it be done?
var projects = ["apple", "orange"];
$('#search').autocomplete({
source: projects
});
then append the result from ajax?
The way you would go about this would be to combine the results you get back from the server with the local results array. You can accomplish this by passing a function to the source option of autocomplete:
There are three steps you'll have to perform:
Make the AJAX request and get results from the server.
Filter the local array
Combine the results
This should be pretty simple. Something like this would work:
$("input").autocomplete({
source: function(request, response) {
/* local results: */
var localResults = $.ui.autocomplete.filter(localArray, request.term);
/* Remote results: */
$.ajax({
/* AJAX options omitted... */
success: function(data) {
/* Process remote data using $.map, if necessary, then concatenate local
* and remote results.
*/
response(data.concat(localResults));
}
});
}
});
I've worked up a full example here: http://jsfiddle.net/FZ4N4/