Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33,849 changes: 15,072 additions & 18,777 deletions package-lock.json

Large diffs are not rendered by default.

84 changes: 71 additions & 13 deletions packages/custom-functions-metadata/src/parseTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ const EXCLUDEFROMAUTOCOMPLETE = "excludefromautocomplete";
const HELPURL_PARAM = "helpurl";
const LINKEDENTITYLOADSERVICE = "linkedentityloadservice";
const REQUIRESADDRESS = "requiresaddress";
const REQUIRESSTREAMADDRESS = "requiresstreamaddress";
const REQUIRESPARAMETERADDRESSES = "requiresparameteraddresses";
const REQUIRESSTREAMPARAMETERADDRESSES = "requiresstreamparameteraddresses";
const STREAMING = "streaming";
const VOLATILE = "volatile";
const SUPPORT_SYNC = "supportsync";
Expand Down Expand Up @@ -434,7 +436,10 @@ export function parseTree(sourceCode: string, sourceFileName: string): IParseTre
if (functionDeclaration.body) {
const bodyText = functionDeclaration.body.getFullText();
// Fast case-sensitive check first
if (bodyText.indexOf("Excel.RequestContext") !== -1 || bodyText.indexOf("Excel.run") !== -1) {
if (
bodyText.indexOf("Excel.RequestContext") !== -1 ||
bodyText.indexOf("Excel.run") !== -1
) {
hasRichAPICall = true;
}
}
Expand Down Expand Up @@ -688,42 +693,69 @@ function getOptions(
isInvocationFunction: boolean,
extra: IFunctionExtras
): IFunctionOptions {
const addressRequired = isAddressRequired(func);
const streamAddressRequired = isStreamAddressRequired(func);
const parameterAddressesRequired = isRequiresParameterAddresses(func);
const streamParameterAddressesRequired = isRequiresStreamParameterAddresses(func);
const streamEnabled = isStreaming(func, isStreamingFunction);

const optionsItem: IFunctionOptions = {
cancelable: isCancelableTag(func, isCancelableFunction),
requiresAddress: isAddressRequired(func) && !isStreaming(func, isStreamingFunction),
requiresStreamAddress: isAddressRequired(func) && isStreaming(func, isStreamingFunction),
stream: isStreaming(func, isStreamingFunction),
requiresAddress: addressRequired && !streamEnabled,
requiresStreamAddress:
(streamAddressRequired && streamEnabled) || (addressRequired && streamEnabled),
stream: streamEnabled,
volatile: isVolatile(func),
requiresParameterAddresses:
isRequiresParameterAddresses(func) && !isStreaming(func, isStreamingFunction),
requiresParameterAddresses: parameterAddressesRequired && !streamEnabled,
requiresStreamParameterAddresses:
isRequiresParameterAddresses(func) && isStreaming(func, isStreamingFunction),
(streamParameterAddressesRequired && streamEnabled) ||
(parameterAddressesRequired && streamEnabled),
excludeFromAutoComplete: isExcludedFromAutoComplete(func),
linkedEntityLoadService: isLinkedEntityLoadService(func),
capturesCallingObject: capturesCallingObject(func),
supportSync: supportSync(func),
action: isAction(func),
};

if (isAddressRequired(func) || isRequiresParameterAddresses(func)) {
let errorParam: string = isAddressRequired(func)
? "@requiresAddress"
: "@requiresParameterAddresses";
if (addressRequired || parameterAddressesRequired) {
let errorParam: string = addressRequired ? "@requiresAddress" : "@requiresParameterAddresses";

if (!isStreamingFunction && !isCancelableFunction && !isInvocationFunction) {
// Validate that address annotations in streaming functions require StreamingInvocation parameter
if (streamEnabled && !isStreamingFunction) {
const functionPosition = getPosition(func, func.parameters.end);
const errorString = `Since ${errorParam} is present with streaming enabled, the last function parameter should be of type CustomFunctions.StreamingInvocation :`;
extra.errors.push(logError(errorString, functionPosition));
}
// Validate that address annotations in non-streaming and non-cancelable functions require Invocation parameter
if (!streamEnabled && !isCancelableFunction && !isInvocationFunction) {
const functionPosition = getPosition(func, func.parameters.end);
const errorString = `Since ${errorParam} is present, the last function parameter should be of type CustomFunctions.Invocation :`;
extra.errors.push(logError(errorString, functionPosition));
}
}

// Validate that stream address annotations require StreamingInvocation parameter
if (streamAddressRequired || streamParameterAddressesRequired) {
let errorParam: string = streamAddressRequired
? "@requiresStreamAddress"
: "@requiresStreamParameterAddresses";

if (!isStreamingFunction) {
const functionPosition = getPosition(func, func.parameters.end);
const errorString = `Since ${errorParam} is present, the last function parameter should be of type CustomFunctions.StreamingInvocation :`;
extra.errors.push(logError(errorString, functionPosition));
}
}

if (
optionsItem.linkedEntityLoadService &&
(optionsItem.excludeFromAutoComplete ||
optionsItem.volatile ||
optionsItem.stream ||
optionsItem.requiresAddress ||
optionsItem.requiresStreamAddress ||
optionsItem.requiresParameterAddresses ||
optionsItem.requiresStreamParameterAddresses ||
optionsItem.capturesCallingObject)
) {
let errorParam: string = "";
Expand All @@ -737,8 +769,12 @@ function getOptions(
errorParam = "@streaming";
} else if (optionsItem.requiresAddress) {
errorParam = "@requiresAddress";
} else if (optionsItem.requiresStreamAddress) {
errorParam = "@requiresStreamAddress";
} else if (optionsItem.requiresParameterAddresses) {
errorParam = "@requiresParameterAddresses";
} else if (optionsItem.requiresStreamParameterAddresses) {
errorParam = "@requiresStreamParameterAddresses";
} else if (optionsItem.capturesCallingObject) {
errorParam = "@capturesCallingObject";
}
Expand All @@ -750,7 +786,7 @@ function getOptions(
// supportSync can't coexist with volatile and streaming
if (optionsItem.supportSync && (optionsItem.volatile || optionsItem.stream)) {
const functionPosition = getPosition(func);
const errorString = `@supportSync cannot be used with ${optionsItem.volatile ? '@volatile' : '@streaming'}.`;
const errorString = `@supportSync cannot be used with ${optionsItem.volatile ? "@volatile" : "@streaming"}.`;
extra.errors.push(logError(errorString, functionPosition));
}

Expand All @@ -760,7 +796,9 @@ function getOptions(
optionsItem.volatile ||
optionsItem.stream ||
optionsItem.requiresAddress ||
optionsItem.requiresStreamAddress ||
optionsItem.requiresParameterAddresses ||
optionsItem.requiresStreamParameterAddresses ||
optionsItem.capturesCallingObject ||
optionsItem.linkedEntityLoadService ||
optionsItem.supportSync)
Expand All @@ -776,8 +814,12 @@ function getOptions(
errorParam = "@streaming";
} else if (optionsItem.requiresAddress) {
errorParam = "@requiresAddress";
} else if (optionsItem.requiresStreamAddress) {
errorParam = "@requiresStreamAddress";
} else if (optionsItem.requiresParameterAddresses) {
errorParam = "@requiresParameterAddresses";
} else if (optionsItem.requiresStreamParameterAddresses) {
errorParam = "@requiresStreamParameterAddresses";
} else if (optionsItem.capturesCallingObject) {
errorParam = "@capturesCallingObject";
} else if (optionsItem.linkedEntityLoadService) {
Expand Down Expand Up @@ -1109,6 +1151,14 @@ function isAddressRequired(node: ts.Node): boolean {
return hasTag(node, REQUIRESADDRESS);
}

/**
* Returns true if requiresStreamAddress tag found in comments
* @param node jsDocs node
*/
function isStreamAddressRequired(node: ts.Node): boolean {
return hasTag(node, REQUIRESSTREAMADDRESS);
}

/**
* Returns true if RequiresParameterAddresses tag found in comments
* @param node jsDocs node
Expand All @@ -1117,6 +1167,14 @@ function isRequiresParameterAddresses(node: ts.Node): boolean {
return hasTag(node, REQUIRESPARAMETERADDRESSES);
}

/**
* Returns true if requiresStreamParameterAddresses tag found in comments
* @param node jsDocs node
*/
function isRequiresStreamParameterAddresses(node: ts.Node): boolean {
return hasTag(node, REQUIRESSTREAMPARAMETERADDRESSES);
}

/**
* Returns true if excludedFromAutoComplete tag found in comments
* @param node jsDocs node
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Since @requiresAddress is present, the last function parameter should be of type CustomFunctions.Invocation : (10,56)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Since @requiresAddress is present, the last function parameter should be of type CustomFunctions.Invocation : (10,71)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

/**
* @customfunction
* @cancelable
* @requiresAddress
* @param {number} value Input value
*/
async function cancelableAddressWithoutInvocation(value) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

/**
* @customfunction
* @cancelable
* @requiresAddress
* @param value Input value
*/
export async function cancelableAddressWithoutInvocation(value: number): Promise<string> {

}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Since @requiresAddress is present, the last function parameter should be of type CustomFunctions.Invocation : (11,40)
Since @requiresAddress is present with streaming enabled, the last function parameter should be of type CustomFunctions.StreamingInvocation : (11,40)
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Since @requiresAddress is present, the last function parameter should be of type CustomFunctions.Invocation : (11,48)
Since @requiresAddress is present with streaming enabled, the last function parameter should be of type CustomFunctions.StreamingInvocation : (11,48)
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Since @requiresParameterAddresses is present, the last function parameter should be of type CustomFunctions.Invocation : (11,40)
Since @requiresParameterAddresses is present with streaming enabled, the last function parameter should be of type CustomFunctions.StreamingInvocation : (11,40)
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Since @requiresParameterAddresses is present, the last function parameter should be of type CustomFunctions.Invocation : (11,48)
Since @requiresParameterAddresses is present with streaming enabled, the last function parameter should be of type CustomFunctions.StreamingInvocation : (11,48)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Since @requiresStreamAddress is present, the last function parameter should be of type CustomFunctions.StreamingInvocation : (9,43)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Since @requiresStreamAddress is present, the last function parameter should be of type CustomFunctions.StreamingInvocation : (9,43)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

/**
* Test requires stream address without the @streaming tag.
* @customfunction
* @requiresStreamAddress
*/
function streamAddressMissingStreamingTag() {
// Empty
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

/**
* Test requires stream address without the @streaming tag.
* @customfunction
* @requiresStreamAddress
*/
function streamAddressMissingStreamingTag() {
// Empty
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Since @requiresStreamParameterAddresses is present, the last function parameter should be of type CustomFunctions.StreamingInvocation : (10,46)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Since @requiresStreamParameterAddresses is present, the last function parameter should be of type CustomFunctions.StreamingInvocation : (11,54)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

/**
* Test requires stream addresses without streaming.
* @param {string} x string
* @customfunction
* @requiresStreamParameterAddresses
*/
function streamAddressRequiresStreamingTest(x) {
// Empty
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

/**
* Test requires stream addresses without streaming.
* @param x string
* @customfunction

* @requiresStreamParameterAddresses
*/
function streamAddressRequiresStreamingTest(x: string) {
// Empty
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"allowCustomDataForDataTypeAny": true,
"functions": [
{
"description": "Streams the address of the current cell.",
"id": "ADDRESSSTREAM",
"name": "ADDRESSSTREAM",
"options": {
"requiresStreamAddress": true,
"stream": true,
"requiresStreamParameterAddresses": true
},
"parameters": [],
"result": {
"type": "string"
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

/**
* Streams the address of the current cell.
* @customfunction ADDRESSSTREAM
* @param {CustomFunctions.StreamingInvocation<string>} invocation stream invocation
* @streaming
* @requiresStreamAddress
* @requiresStreamParameterAddresses
*/
function addressStream(invocation) {
// Empty
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

/**
* Streams the address of the current cell.
* @customfunction ADDRESSSTREAM
* @param invocation stream invocation
* @streaming
* @requiresStreamAddress
* @requiresStreamParameterAddresses
*/
function addressStream(invocation: CustomFunctions.StreamingInvocation<string>) {
// Empty
}