I've been trying to retrieve the Resource with the path "/" (the root) from AWS Api Gateway using the Nodejs AWS SDK. I know the naïve solution would be to do it this way:
var AWS = require('aws-sdk');
var __ = require('lodash');
var Promise = require('bluebird');
var resources = [];
var apiGateway = Promise.promisifyAll(new AWS.APIGateway({apiVersion: '2015-07-09', region: 'us-west-2'}));
var _finishRetrievingResources = function (resources) {
var orderedResources = __.sortBy(resources, function (res) {
return res.path.split('/').length;
});
var firstResource = orderedResources[0];
};
var _retrieveNextPage = function (resp) {
resources = resources.concat(resp.data.items);
if (resp.hasNextPage()) {
resp.nextPage().on('success', _retrieveNextPage).send();
} else {
_finishRetrievingResources(resources);
}
};
var foo = apiGateway.getResources({restApiId: 'mah_rest_api_id'}).on('success', _retrieveNextPage).send();
However, does anybody know of an alternate method? I'd prefer to know that I'll alway have to do at most one call than having to do multiple.
PS: I know there are several optimizations that could be made (e.g. check for root path on every response), I really want to know if there's a single SDK Call that could fix this.
There is not a single call, though it can be if you have less than 500 resources. As a consolation prize, this is the best-practice, using position to prevent accidental misses if there are over 500 resources. If there are less than 500 resources, this will work with one call:
https://github.com/andrew-templeton/cfn-api-gateway-restapi/blob/bd964408bcb4bc6fc8ec91b5e1f0387c8f11691a/index.js#L77-L102
Related
I have a cloud function like this which has been set to run in multiple regions.
export const cloudFunction = functions
.region(["asia-south1", "us-central1", "europe-west1", "southamerica-east1"])
.https.onCall(async (data, context) => {});
How can I call the cloud function region nearest to the user? From any client side framework?
The best solution is to use an HTTPS Load Balancer and to create a serverless NEG with your Cloud Functions. The HTTPS Load Balancer will deploy a unicast IP, I mean an IP known in different PoP (Point of Presence) of Google, and will route the request to the closest location (from the PoP). It's native and out of the box, nothing to code.
You'll have to find the closet region based on user's timezone/location yourself and specify the region on client side for routing based on region w/o a balancer as each Cloud Function has it's own URL containing the region. For example, one way would be like:
const getClosestGcpRegion = () => {
const regions = ['asia-south1', 'us-central1', 'europe-west1']
const regionOffsets = {
'asia-south1': '+05:30',
'us-central1': '-06:00',
'europe-west1': '+01:00',
}
let closestRegion = ''
let closestOffset = Number.MAX_SAFE_INTEGER
for (const region of regions) {
const offset = regionOffsets[region].split(':')
const offsetMinutes = Number(offset[0]) * 60 + Number(offset[1])
const offsetDiff = Math.abs(DateTime.local().offset - offsetMinutes)
if (offsetDiff < closestOffset) {
closestOffset = offsetDiff
closestRegion = region
}
}
console.log({ closestRegion })
return closestRegion;
}
export const functions = getFunctions(app, getClosestGcpRegion())
Alternatively, also checkout Global external HTTP(S) load balancer with Cloud Functions that can help you achieve the same goal.
I have a public SPA website (written in Svelte), with no user authentication.
I'd like to use the Azure Text-to-speech service (as either REST Api or with the azure-cognitive-services npm package).
I now need to provide the api key to use the service... But I understand it is a bad practice to store that key in the client code.
What would be the way to use that service then? Do I really need a backend service? Do I need to wrap my text-to-speech code in something like an Azure Function?
Thanks
If you look at the Source Code of the microsoft-cognitiveservices-speech-sdk NPM package it is clear the only way to auth to the service is by using some form of token/id which you will have to store at client side.
Instead of that you can use an azure function which will take text and convert to text .
My index,js file in azure function .
var t = require('./test');
var fs = require('fs');
module.exports = async function (context, req) {
const name = (req.query.name || (req.body && req.body.name));
t(name);
context.res = {
body:""
}
}
The main processing will be in the file test.js
function test (text) {
var sdk = require("microsoft-cognitiveservices-speech-sdk");
var readline = require("readline");
// This will be the name of the file created later
var audioFile = "YourAudioFile.wav";
const speechConfig = sdk.SpeechConfig.fromSubscription(process.env.SPEECH_KEY, process.env.SPEECH_REGION);
const audioConfig = sdk.AudioConfig.fromAudioFileOutput(audioFile);
speechConfig.speechSynthesisVoiceName = "en-US-JennyNeural";
var synthesizer = new sdk.SpeechSynthesizer(speechConfig, audioConfig);
synthesizer.speakTextAsync(text,
function (result) {
if (result.reason === sdk.ResultReason.SynthesizingAudioCompleted) {
console.log("synthesis finished.");
} else {
console.error("Speech synthesis canceled, " + result.errorDetails +
"\nDid you set the speech resource key and region values?");
}
synthesizer.close();
synthesizer = null;
},
function (err) {
console.trace("err - " + err);
synthesizer.close();
synthesizer = null;
});
}
module.exports = test ;
Majority of code from above is from the MSDOC
At the end all you have to do is return the file created by the synthesizer.
I'm trying to migrate from the deprecated Microsoft.WindowsAzure.Storage to Azure.Storage. In my API app, I have a method that I call occasionally to programmatically set the CORS rules in my Azure Storage account.
How do I add CORS rules to the properties using the new Azure.Storage.Blobs?
My original code that worked under Microsoft.WindowsAzure.Storage is as follows. In the following code, the _client is an instance of CloudBlobClient. I understand that in Azure.Storage.Blobs, I need to use BlobServiceClient which I now do but as I said, some parts of the following code are not working because some methods/properties are no longer there. I'm sure they're moved somewhere else but I haven't been able to figure out where.
public async Task ConfigureCors()
{
var ALLOWED_CORS_ORIGINS = new List<String> { "http://localhost:49065", "https://myappdomain.com", "https://www.myappdomain", "https://login.microsoftonline.com" };
var ALLOWED_CORS_HEADERS = new List<String> { "x-ms-meta-qqfilename", "Content-Type", "x-ms-blob-type", "x-ms-blob-content-type" };
const CorsHttpMethods ALLOWED_CORS_METHODS = CorsHttpMethods.Get | CorsHttpMethods.Delete | CorsHttpMethods.Put | CorsHttpMethods.Options;
const int ALLOWED_CORS_AGE_DAYS = 5;
var properties = await _client.GetServicePropertiesAsync();
properties.DefaultServiceVersion = "2013-08-15";
await _client.SetServicePropertiesAsync(properties);
var addRule = true;
if (addRule)
{
var ruleWideOpenWriter = new CorsRule()
{
AllowedHeaders = ALLOWED_CORS_HEADERS,
AllowedOrigins = ALLOWED_CORS_ORIGINS,
AllowedMethods = ALLOWED_CORS_METHODS,
MaxAgeInSeconds = (int)TimeSpan.FromDays(ALLOWED_CORS_AGE_DAYS).TotalSeconds
};
properties.Cors.CorsRules.Clear();
properties.Cors.CorsRules.Add(ruleWideOpenWriter);
await _client.SetServicePropertiesAsync(properties);
}
}
Looks like I can get and set properties by changing _client.GetServicePropertiesAsync() to _client.GetPropertiesAsync() but DefaultServiceVersion is no longer there. Also I can't seem to find the right way to set CORS rules.
I'd appreciate your suggestions. Thanks!
You can use the code below when using Azure.Storage.Blobs(I'm using sync method, please change it to async method if you need that):
var properties = blobServiceClient.GetProperties().Value;
properties.DefaultServiceVersion = "xxx";
BlobCorsRule rule = new BlobCorsRule();
rule.AllowedHeaders= "x-ms-meta-qqfilename,Content-Type,x-ms-blob-type,x-ms-blob-content-type";
rule.AllowedMethods = "GET,DELETE,PUT,OPTIONS";
rule.AllowedOrigins = "http://localhost:49065,https://myappdomain.com,https://www.myappdomain,https://login.microsoftonline.com";
rule.MaxAgeInSeconds = 3600; // in seconds
properties.Cors.Add(rule);
blobServiceClient.SetProperties(properties);
It seems I can't find a proper way to use the read/write functions for admin in the Cloud Functions. I am working on a messaging function that reads new messages created in the Realtime Database with Cloud Functions Node.js and uses the snapshot to reference a path. Here is my initial exports function:
var messageRef = functions.database.ref('Messages/{chatPushKey}/Messages/{pushKey}');
var messageText;
exports.newMessageCreated = messageRef.onCreate((dataSnapshot, context) => {
console.log("Exports function executed");
messageText = dataSnapshot.val().messageContent;
var chatRef = dataSnapshot.key;
var messengerUID = dataSnapshot.val().messengerUID;
return readChatRef(messengerUID, chatRef);
});
And here is the function that reads from the value returned:
function readChatRef(someUID, chatKey){
console.log("Step 2");
admin.database.enableLogging(true);
var db;
db = admin.database();
var userInfoRef = db.ref('Users/' + someUID + '/User Info');
return userInfoRef.on('value', function(snap){
return console.log(snap.val().firstName);
});
}
In the firebase cloud functions log I can read all console.logs except for the one inside return userInfoRef.on.... Is my syntax incorrect? I have attempted several other variations for reading the snap. Perhaps I am not using callbacks efficiently? I know for a fact that my service account key and admin features are up to date.
If there is another direction I need to be focusing on please let me know.
I am seeing a weird error on the with Azure Node SDK where I get a 500 error back anytime i include anything in the filter attribute of the options parameter. I am using the Usage Details call within the ConsumptionManagementClient class. Code is below:
const credentials = await MsRest.loginWithServicePrincipalSecret(config.appId, config.apiKey, config.tenantId);
let client = new ConsumptionManagementClient(credentials, subscriptionId);
const scope = `/subscriptions/${subscriptionId}`;
const options = { filter: "usageStart ge datetime'2017-10-13T00:00:00.000Z'"};
let usage = await client.usageDetails.list(scope, options);
The above code produces a 500 error(even tried searching for other things another example being "billableQuantity ge 0.001") but it seems to error out no matter what i give it.
The code works fine when i try using another one of the options paramters:
const credentials = await MsRest.loginWithServicePrincipalSecret(config.appId, config.apiKey, config.tenantId);
let client = new ConsumptionManagementClient(credentials, subscriptionId);
const scope = `/subscriptions/${subscriptionId}`;
const options = { top: 50 };
let usage = await client.usageDetails.list(scope, options);
Any ideas? Thanks in advance for the help!