Node.js - Microsoft Azure support

Azure functions, triggers and bindings

Azure functions allow the execution of source code in the cloud. The execution can be set to be triggered by some Azure events. A source code implementing a TypeScript azure function contains a function.json file and an index.ts file. These files are placed in the same directory and the name of the directory gives the name of the azure function. The index.ts file contains a default export function with the source code that will be executed when the azure function is triggered.

The com.castsoftware.nodejs extension is responsible for creating Azure function objects and supporting the triggers and bindings. It also links these objects to the TypeScript handler function. The level of support may depend upon the version of the com.castsoftware.nodejs version you are using. Please refer to its documentation page to understand the level of support you have.

Durable functions

Durable functions allow calling an azure function from another azure function. There are three kinds of durable functions: the orchestrator function, the client function and the activity function.

The startNew method from the “durable-function” client allow the client function to call an orchestrator function.  The callActivity method from the “durable-function” package allows calling an activity function.Whenever one of these method calls is found in a JavaScript source code, a call to Azure function object is created. The name of the object is evaluated from the first argument of the call. If an azure function object with the same name exists a call link from the call to Azure function to that matching azure function is created.

For example when analyzing the following source code:

index.ts

import * as df from "durable-functions"

const orchestrator = df.orchestrator(function* (context) {
    const outputs = [];
    outputs.push(yield context.df.callActivity("WorkActivity1", "Tokyo"));
    return outputs;
});

you will get the following result:

Durable function client with HTTP trigger

It is common that a durable function client is triggered by an HTTP call and that with a proper URL any orchestrator function can be triggered. In this case, for each existing orchestrator azure function, an Operation with the corresponding url is created with a callLink to a Call to Azure Function object to the orchestrator function. For more detail see the com.castsoftware.nodejs  extension documentation.

Azure Service Bus

NodeJS Azure Service Bus Publisher and NodeJS Azure Service Bus Receiver objects are created when the following methods are used:

Object created

Methods from ServiceBusClient from @azure/service-bus package


Methods from ServiceBusService from azure-sb package
NodeJS Azure Service Bus Publisher

sendMessages,
scheduleMessages

createSender, createReceiver
NodeJS Azure Service Bus Receiver receiveMessages, peekMessages, subscribe, getMessageIterator, receiveDeferredMessages receiveQueueMessage, receiveSubscriptionMessage

The name of the object created is that of the Queue (or the Topic). Whenever the evaluation of the Queue (or Topic) name fails, an Unknown object is created.

  • For publishers, a callLink  from the callable object (usually Function or Method) containing the call (to the supported method) to the publisher is added (see example below).
  • For receivers, some APIs (such as the subscribe API) require providing a handler function, in such case a callLink is created from the receiver to that handler. In other cases, a callLink is added from the receiver to the callable object (usually a Function or a Method) containing the call to the supported method (see example below).

Whenever an Azure Service Bus Publisher has the same name as an Azure Service Bus Receiver, the web service linker extension will create a call link from the Publisher to the Receiver.

Example

When analyzing the following source code:

import { ServiceBusClient, ServiceBusMessage, ServiceBusMessageBatch } from "@azure/service-bus";

function my_publish(Messages){
    const sbClient = new ServiceBusClient(connectionString);
    const sender = sbClient.createSender("MyQueue");
    await sender.sendMessages(Messages);
}

function my_receive(){
    const sbClient = new ServiceBusClient(connectionString);
    let receiver = sbClient.createReceiver("MyQueue", {
        receiveMode: "receiveAndDelete"
    });
    let messages = await receiver.receiveMessages(1);
}

you will get the following result:

Click to enlarge

Azure Blobs

When a source code contains calls to the APIs listed in the following table, this extension will create a link to Azure Blob objects:

LinkType

Methods from containerClient

import {BlobServiceClient} from "@azure/storage-blob";
const blobServiceClient = new BlobServiceClient(...);
const containerClient = blobServiceClient.getContainerClient(...)

Methods from BlobClients (blockBlobClient,
PageBlobClient, AppendBlobClient or BlobBatchCLient)

import {BlobServiceClient} from "@azure/storage-blob"
const blobServiceClient = new BlobServiceClient(...)
const containerClient = blobServiceClient.getContainerClient(...)
const blockBlobClient = containerClient.getBlockBlobClient(...)

Methods from BlobServices

import * as azure from 'azure-storage'
const blobService = azure.createBlobService(...)
useInsert uploadBlockBlob syncUploadFromURL, upload', uploadPages, uploadPagesFromURL, beginCopyFromURL createAppendBlobFromBrowserFile, createAppendBlobFromLocalFile, createAppendBlobFromStream, createAppendBlobFromText, createBlobSnapshot, createBlobSnapshot, createBlockBlobFromLocalFile, createBlockBlobFromStream, createBlockBlobFromText, createBlockFromStream, createBlockFromText, createBlockFromURL, createOrReplaceAppendBlob, createPageBlob, createPageBlob, createPageBlobFromLocalFile, createPageBlobFromStream, createPagesFromStream, createWriteStreamToBlockBlob, createWriteStreamToBlockBlob, createWriteStreamToNewAppendBlob, createWriteStreamToNewPageBlob, startCopyBlob
useUpdate uploadBlockBlob commitBlockList, stageBlock, stageBlockFromURL, syncUploadFromURL, upload, uploadBrowserData
'ploadData, uploadFile, uploadStream, uploadPages, uploadPagesFromURL, appendBlock, appendBlockFromURL, beginCopyFromURL, startCopyIncremental
appendBlockFromStream, appendBlockFromText, appendFromBrowserFile, appendFromLocalFile, appendFromStream, appendFromText, commitBlocks, createAppendBlobFromBrowserFile, createAppendBlobFromLocalFile, createAppendBlobFromStream, createAppendBlobFromText, createBlobSnapshot, createBlockBlobFromLocalFile, createBlockBlobFromStream, createBlockBlobFromText, createBlockFromStream, createBlockFromText, createBlockFromURL, createOrReplaceAppendBlob, createPageBlob, createPageBlob, createPageBlobFromLocalFile, createPageBlobFromStream, createPagesFromStream, createWriteStreamToBlockBlob, createWriteStreamToBlockBlob, createWriteStreamToExistingAppendBlob, createWriteStreamToExistingAppendBlob, createWriteStreamToExistingPageBlob, startCopyBlob
useDelete deleteBlob, deleteIfExists clearPages, deleteBlobs, delete, deleteIfExists deleteBlob, deleteBlobIfExists, deleteContainer, deleteContainerIfExists
useSelect
getBlockList, query, createSnapshot, download, downloadToBuffer, downloadToFile, beginCopyFromURL, startCopyIncremental createBlobSnapshot, createReadStream, getBlobToLocalFile, getBlobToStream, getBlobToText, startCopyBlob, createBlockFromURL

Example

When analyzing the following code, a blob container named my_container is created as well as a useInsert and a useUpdate links from the main function to that container:

import { BlobServiceClient } from "@azure/storage-blob";

const blobServiceClient = new BlobServiceClient(account_url, defaultAzureCredential);

async function main() {
  const containerClient = blobServiceClient.getContainerClient("my_container");
  const content = "Hello world!";
  const blockBlobClient = containerClient.getBlockBlobClient("blobName");
  const uploadBlobResponse = await blockBlobClient.upload(content, content.length);
}

This will produce the following result:

Azure CosmosDB

The @azure/cosmos package is supported. Whenever a container client use one of the method listed in the following table, a link is created to the corresponding collection.

LinkType

Methods from Container

Methods from Item

Methods from Items

useDelete delete delete batch, bulk
useSelect
read, query batch, bulk, query, readAll
useUpdate
replace, patch batch, bulk, upsert
useInsert

batch, bulk, create

If the evaluation of the container name fails (either due to missing information in the source code or to limitations in the evaluation) a link is created to an Unknown collection.

Example

Analyzing the following code:

import { Container, FeedOptions, SqlQuerySpec, CosmosClient } from "@azure/cosmos";


function my_query(){
  const client = new CosmosClient({
    endpoint,
    key
  });

  const query1 = "Select * from c order by c._ts";
  const container = client.database('My database').container('My collection');
  const queryIterator = container.items.query(query1, options);
}

will produce the following result:

Azure Event Hubs

This extension supports the version 5.x of @azure/event-hubs package as follows:

  • Whenever a call to one of the following API is found in the source code this extension evaluates the name of the Event Hub in which the operation is made and creates an Event Hub Publisher with a callLink from the caller. 
    • EventHubBufferedProducerClient.enqueueEvent
    • EventHubBufferedProducerClient.flush
    • EventHubProducerClient.sendBatch
  • Whenever a call to EventHubConsumerClient.subscribe is found in the source code, this extension evaluates the name of the Event Hub in which the operation is made and creates an Event Hub Receiver with a callLink to the handler method*.*

If the evaluation of the Event Hub name fails (either due to missing information in the source code or to limitations in the evaluation) an Unknown Event Hub Publisher/Receiver is created.

Example

Analyzing the following code:

publisher.ts

const { EventHubProducerClient } = require("@azure/event-hubs");

const eventHubName = "tests-hub";

function my_publish(){
  // Create a producer client to send messages to the event hub.
  const producer = new EventHubProducerClient(connectionString, eventHubName, {retryOptions: {maxRetries: 3, maxRetryDelayInMs: 3000}});

  const batch = await producer.createBatch({ partitionKey: i });
  batch.tryAdd({ body: "Message" });
  // Send the batch to the event hub.
  await producer.sendBatch(batch);

}

consumer.ts

const { EventHubConsumerClient } = require("@azure/event-hubs");

const eventHubName = "tests-hub";

async function main() {

  // Create a consumer client for the event hub by specifying the checkpoint store.
  const consumerClient = new EventHubConsumerClient(consumerGroup, connectionString, eventHubName, checkpointStore);

  // Subscribe to the events, and specify handlers for processing the events and errors.
  const subscription = consumerClient.subscribe({
      processEvents: async (events, context) => {
        console.log(events.length);
      }
    }
  );
}

will produce the following result:

Azure SignalR

ASP.NET Core

This extension supports the calls to server for the @microsoft/signalr and @aspnet/signalr packages.

Whenever a call to one of the following API is found in the source code this extension evaluates the name of the invoked method as well as the hub name and creates a NodeJS Azure SignalR Call to Hub Method object named with the name of the invoked method and with a property storing the hub name:

  • HubConnection.send
  • HubConnection.invoke
  • HubConnection.stream

If the evaluation of the method name fails (either due to missing information in the source code or to limitations in the evaluation) a NodeJS Azure SignalR Call to Unknown Hub Method object is created.

ASP.NET

This extension supports:

  • calls to server using signalr package.
  • both calls with or without the generated proxy.

Without generated proxy

Whenever an invoke call is found, if this call comes from a hub proxy created from a createHubProxy() call, this extension creates a NodeJS Azure SignalR Call to Hub Method. Its name is evaluated from the first argument of the invoke and it has a hub name property whose value is evaluated from the first argument of the createHubProxy Call.

If the evaluation of the method name fails (either due to missing information in the source code or to limitations in the evaluation) a NodeJS Azure SignalR Call to Unknown Hub Method object is created.

With generated proxy

When a method call $.connection.hubname.server.methodname() is found, this extension creates a NodeJS Azure SignalR Call to Hub Method. Its name is methodname and it has a hub name property whose value is hubname.

Example

Analyzing one of the following pieces of code:

module_for_aspnet_core.ts

import * as signalr from '@microsoft/signalr'

function my_invoke(){
    const connection = new signalR.HubConnectionBuilder()
    .withUrl("/myhub")
    .configureLogging(signalR.LogLevel.Information)
    .build();
    
    await connection.start()
    await connection.invoke("MyMethod", user, message);
    
}

module_for_aspnet_without_generated_proxy.ts

function my_invoke(){
  var connection = $.hubConnection();
  var hubProxy= connection.createHubProxy('myhub');
  hubProxy.invoke('MyMethod', { UserName: userName, Message: message})
}

module_for_aspnet_with_generated_proxy.ts

function my_invoke(){
   var hubProxy = $.connection.myhub;
   hubProxy.server.MyMethod({user:user, message:message})
}

will produce the following result:

If a DotNet Azure SignalR Method object exists with matching method name and hub name exits the com.castsoftware.wbslinker extension will create a link from the *NodeJS Azure SignalR Call to Hub Method * to that object.