Skip to content

Commit

Permalink
Merge #11564 #11581
Browse files Browse the repository at this point in the history
11564: [sdk/dotnet/nodejs] Add InvokeSingle variants to dotnet and nodejs SDKs r=Zaid-Ajaj a=Zaid-Ajaj

<!--- 
Thanks so much for your contribution! If this is your first time contributing, please ensure that you have read the [CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md) documentation.
-->

# Description

Needed for #11418 where we have invoke calls that return a simple type (such as `number`) but the runtime and engine _require_ the outputs to be a struct (as per the RPC definition) so here we are adding variants of `invoke` that unwraps the returned object and gets the first value from that object.

For example, a provider with a schema `outputs: { type: "number" }` will have an invoke implementation that returns `{ __result: 42.0 }`, in these case (non-object return types) we use `InvokeSingle` instead of `Invoke`

> It doesn't matter what the name of the key is, we always get the first value by first key.

## Checklist

<!--- Please provide details if the checkbox below is to be left unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my feature works
<!--- 
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the `changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the Pulumi Service,
then the service should honor older versions of the CLI where this change would not exist.
You must then bump the API version in /pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi Service API version
  <!-- `@Pulumi` employees: If yes, you must submit corresponding changes in the service repo. -->


11581: Fix PCL binding of options.range = number r=Frassle a=Frassle

<!--- 
Thanks so much for your contribution! If this is your first time contributing, please ensure that you have read the [CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md) documentation.
-->

# Description

<!--- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. -->

Fixes the binding issue with `options { range = number }`.

## Checklist

<!--- Please provide details if the checkbox below is to be left unchecked. -->
- [x] I have added tests that prove my fix is effective or that my feature works
<!--- 
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the `changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the Pulumi Service,
then the service should honor older versions of the CLI where this change would not exist.
You must then bump the API version in /pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi Service API version
  <!-- `@Pulumi` employees: If yes, you must submit corresponding changes in the service repo. -->


Co-authored-by: Zaid Ajaj <zaid.naom@gmail.com>
Co-authored-by: Fraser Waters <fraser@pulumi.com>
  • Loading branch information
3 people committed Dec 7, 2022
3 parents 1ae56cc + 5022d9d + f93977c commit 1290936
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 36 deletions.
@@ -0,0 +1,4 @@
changes:
- type: feat
scope: sdk/dotnet,nodejs
description: Add InvokeSingle variants to dotnet and nodejs SDKs
2 changes: 2 additions & 0 deletions pkg/codegen/pcl/binder_resource.go
Expand Up @@ -305,6 +305,8 @@ func (b *binder) bindResourceBody(node *Resource) hcl.Diagnostics {
diags := rangeExpr.Typecheck(false)
contract.Assert(len(diags) == 0)

rangeValue = model.NumberType

node.VariableType = rangeExpr.Type()
default:
rk, rv, diags := model.GetCollectionTypes(typ, rng.Range())
Expand Down
1 change: 0 additions & 1 deletion pkg/codegen/testing/test/program_driver.go
Expand Up @@ -110,7 +110,6 @@ var PulumiPulumiProgramTests = []ProgramTest{
{
Directory: "simple-range",
Description: "Simple range as int expression translation",
BindOptions: []pcl.BindOption{pcl.AllowMissingVariables},
},
{
Directory: "azure-native",
Expand Down
@@ -1,20 +1,24 @@
using System.Collections.Generic;
using Pulumi;
using Aws = Pulumi.Aws;
using Random = Pulumi.Random;

return await Deployment.RunAsync(() =>
{
var bucket = new List<Aws.S3.Bucket>();
for (var rangeIndex = 0; rangeIndex < 10; rangeIndex++)
var numbers = new List<Random.RandomInteger>();
for (var rangeIndex = 0; rangeIndex < 2; rangeIndex++)
{
var range = new { Value = rangeIndex };
bucket.Add(new Aws.S3.Bucket($"bucket-{range.Value}", new()
numbers.Add(new Random.RandomInteger($"numbers-{range.Value}", new()
{
Website = new Aws.S3.Inputs.BucketWebsiteArgs
{
IndexDocument = $"index-{range.Value}.html",
},
Min = 1,
Max = range.Value,
Seed = $"seed{range.Value}",
}));
}
return new Dictionary<string, object?>
{
["first"] = numbers[0].Id,
["second"] = numbers[1].Id,
};
});

Expand Up @@ -3,26 +3,28 @@ package main
import (
"fmt"

"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/s3"
"github.com/pulumi/pulumi-random/sdk/v4/go/random"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
var bucket []*s3.Bucket
for index := 0; index < 10; index++ {
var numbers []*random.RandomInteger
for index := 0; index < 2; index++ {
key0 := index
val0 := index
__res, err := s3.NewBucket(ctx, fmt.Sprintf("bucket-%v", key0), &s3.BucketArgs{
Website: &s3.BucketWebsiteArgs{
IndexDocument: pulumi.String(fmt.Sprintf("index-%v.html", val0)),
},
__res, err := random.NewRandomInteger(ctx, fmt.Sprintf("numbers-%v", key0), &random.RandomIntegerArgs{
Min: pulumi.Int(1),
Max: pulumi.Float64(val0),
Seed: pulumi.String(fmt.Sprintf("seed%v", val0)),
})
if err != nil {
return err
}
bucket = append(bucket, __res)
numbers = append(numbers, __res)
}
ctx.Export("first", numbers[0].ID())
ctx.Export("second", numbers[1].ID())
return nil
})
}
@@ -1,9 +1,13 @@
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as random from "@pulumi/random";

const bucket: aws.s3.Bucket[] = [];
for (const range = {value: 0}; range.value < 10; range.value++) {
bucket.push(new aws.s3.Bucket(`bucket-${range.value}`, {website: {
indexDocument: `index-${range.value}.html`,
}}));
const numbers: random.RandomInteger[] = [];
for (const range = {value: 0}; range.value < 2; range.value++) {
numbers.push(new random.RandomInteger(`numbers-${range.value}`, {
min: 1,
max: range.value,
seed: `seed${range.value}`,
}));
}
export const first = numbers[0].id;
export const second = numbers[1].id;
@@ -1,8 +1,11 @@
import pulumi
import pulumi_aws as aws
import pulumi_random as random

bucket = []
for range in [{"value": i} for i in range(0, 10)]:
bucket.append(aws.s3.Bucket(f"bucket-{range['value']}", website=aws.s3.BucketWebsiteArgs(
index_document=f"index-{range['value']}.html",
)))
numbers = []
for range in [{"value": i} for i in range(0, 2)]:
numbers.append(random.RandomInteger(f"numbers-{range['value']}",
min=1,
max=range["value"],
seed=f"seed{range['value']}"))
pulumi.export("first", numbers[0].id)
pulumi.export("second", numbers[1].id)
16 changes: 10 additions & 6 deletions pkg/codegen/testing/test/testdata/simple-range-pp/simple-range.pp
@@ -1,8 +1,12 @@
resource bucket "aws:s3:Bucket" {
resource numbers "random:index/randomInteger:RandomInteger" {
options {
range = 10
range = 2
}
website = {
indexDocument = "index-${range.value}.html"
}
}

min = 1
max = range.value
seed = "seed${range.value}"
}

output first { value = numbers[0].id }
output second { value = numbers[1].id }
29 changes: 29 additions & 0 deletions sdk/dotnet/Pulumi/Deployment/DeploymentInstance.cs
Expand Up @@ -50,6 +50,22 @@ internal DeploymentInstance(IDeployment deployment)
public Output<T> Invoke<T>(string token, InvokeArgs args, InvokeOptions? options = null)
=> _deployment.Invoke<T>(token, args, options);

/// <summary>
/// Dynamically invokes the function '<paramref name="token"/>', which is offered by a
/// provider plugin.
/// <para/>
/// The result of <see cref="InvokeSingle"/> will be a <see cref="Output"/> resolved to the
/// result value of the provider plugin.
/// <para/>
/// Similar to the earlier <see cref="InvokeSingleAsync"/>, but supports passing input values
/// and returns an Output value.
/// <para/>
/// The <paramref name="args"/> inputs can be a bag of computed values(including, `T`s,
/// <see cref="Task{TResult}"/>s, <see cref="Output{T}"/>s etc.).
/// </summary>
public Output<T> InvokeSingle<T>(string token, InvokeArgs args, InvokeOptions? options = null)
=> _deployment.InvokeSingle<T>(token, args, options);

/// <summary>
/// Dynamically invokes the function '<paramref name="token"/>', which is offered by a
/// provider plugin.
Expand All @@ -63,6 +79,19 @@ public Output<T> Invoke<T>(string token, InvokeArgs args, InvokeOptions? options
public Task<T> InvokeAsync<T>(string token, InvokeArgs args, InvokeOptions? options = null)
=> _deployment.InvokeAsync<T>(token, args, options);

/// <summary>
/// Dynamically invokes the function '<paramref name="token"/>', which is offered by a
/// provider plugin.
/// <para/>
/// The result of <see cref="InvokeSingleAsync"/> will be a <see cref="Task"/> resolved to the
/// result value of the provider plugin which is expected to be a dictionary with single value.
/// <para/>
/// The <paramref name="args"/> inputs can be a bag of computed values(including, `T`s,
/// <see cref="Task{TResult}"/>s, <see cref="Output{T}"/>s etc.).
/// </summary>
public Task<T> InvokeSingleAsync<T>(string token, InvokeArgs args, InvokeOptions? options = null)
=> _deployment.InvokeSingleAsync<T>(token, args, options);

/// <summary>
/// Same as <see cref="InvokeAsync{T}(string, InvokeArgs, InvokeOptions)"/>, however the
/// return value is ignored.
Expand Down
13 changes: 13 additions & 0 deletions sdk/dotnet/Pulumi/Deployment/Deployment_Invoke.cs
@@ -1,6 +1,7 @@
// Copyright 2016-2021, Pulumi Corporation

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -18,9 +19,21 @@ Task IDeployment.InvokeAsync(string token, InvokeArgs args, InvokeOptions? optio
Task<T> IDeployment.InvokeAsync<T>(string token, InvokeArgs args, InvokeOptions? options)
=> InvokeAsync<T>(token, args, options, convertResult: true);

async Task<T> IDeployment.InvokeSingleAsync<T>(string token, InvokeArgs args, InvokeOptions? options)
{
var outputs = await InvokeAsync<Dictionary<string, T>>(token, args, options, convertResult: true);
return outputs.Values.First();
}

Output<T> IDeployment.Invoke<T>(string token, InvokeArgs args, InvokeOptions? options)
=> new Output<T>(RawInvoke<T>(token, args, options));

Output<T> IDeployment.InvokeSingle<T>(string token, InvokeArgs args, InvokeOptions? options)
{
var outputResult = new Output<Dictionary<string, T>>(RawInvoke<Dictionary<string, T>>(token, args, options));
return outputResult.Apply(outputs => outputs.Values.First());
}

private async Task<OutputData<T>> RawInvoke<T>(string token, InvokeArgs args, InvokeOptions? options)
{
// This method backs all `Fn.Invoke()` calls that generate
Expand Down
24 changes: 24 additions & 0 deletions sdk/dotnet/Pulumi/Deployment/IDeployment.cs
Expand Up @@ -38,6 +38,18 @@ internal interface IDeployment
/// </summary>
Task<T> InvokeAsync<T>(string token, InvokeArgs args, InvokeOptions? options = null);

/// <summary>
/// Dynamically invokes the function '<paramref name="token"/>', which is offered by a
/// provider plugin.
/// <para/>
/// The result of <see cref="InvokeSingleAsync"/> will be a <see cref="Task"/> resolved to the
/// result value of the provider plugin that returns a bag of properties with a single value that is returned.
/// <para/>
/// The <paramref name="args"/> inputs can be a bag of computed values(including, `T`s,
/// <see cref="Task{TResult}"/>s, <see cref="Output{T}"/>s etc.).
/// </summary>
Task<T> InvokeSingleAsync<T>(string token, InvokeArgs args, InvokeOptions? options = null);

/// <summary>
/// Dynamically invokes the function '<paramref name="token"/>', which is offered by a
/// provider plugin.
Expand All @@ -50,6 +62,18 @@ internal interface IDeployment
/// </summary>
Output<T> Invoke<T>(string token, InvokeArgs args, InvokeOptions? options = null);

/// <summary>
/// Dynamically invokes the function '<paramref name="token"/>', which is offered by a
/// provider plugin.
/// <para/>
/// The result of <see cref="InvokeSingle"/> will be a <see cref="Output"/> resolved to the
/// result value of the provider plugin that returns a bag of properties with a single value that is returned.
/// <para/>
/// The <paramref name="args"/> inputs can be a bag of computed values(including, `T`s,
/// <see cref="Task{TResult}"/>s, <see cref="Output{T}"/>s etc.).
/// </summary>
Output<T> InvokeSingle<T>(string token, InvokeArgs args, InvokeOptions? options = null);

/// <summary>
/// Same as <see cref="InvokeAsync{T}(string, InvokeArgs, InvokeOptions)"/>, however the
/// return value is ignored.
Expand Down
4 changes: 3 additions & 1 deletion sdk/dotnet/Pulumi/PublicAPI.Shipped.txt
Expand Up @@ -91,6 +91,7 @@ Pulumi.DeploymentInstance.Call(string token, Pulumi.CallArgs args, Pulumi.Resour
Pulumi.DeploymentInstance.Call<T>(string token, Pulumi.CallArgs args, Pulumi.Resource self = null, Pulumi.CallOptions options = null) -> Pulumi.Output<T>
Pulumi.DeploymentInstance.InvokeAsync(string token, Pulumi.InvokeArgs args, Pulumi.InvokeOptions options = null) -> System.Threading.Tasks.Task
Pulumi.DeploymentInstance.InvokeAsync<T>(string token, Pulumi.InvokeArgs args, Pulumi.InvokeOptions options = null) -> System.Threading.Tasks.Task<T>
Pulumi.DeploymentInstance.InvokeSingleAsync<T>(string token, Pulumi.InvokeArgs args, Pulumi.InvokeOptions options = null) -> System.Threading.Tasks.Task<T>
Pulumi.DeploymentInstance.IsDryRun.get -> bool
Pulumi.DeploymentInstance.ProjectName.get -> string
Pulumi.DeploymentInstance.StackName.get -> string
Expand Down Expand Up @@ -395,4 +396,5 @@ static Pulumi.Urn.Create(Pulumi.Input<string> name, Pulumi.Input<string> type, P
static readonly Pulumi.CallArgs.Empty -> Pulumi.CallArgs
static readonly Pulumi.InvokeArgs.Empty -> Pulumi.InvokeArgs
static readonly Pulumi.ResourceArgs.Empty -> Pulumi.ResourceArgs
Pulumi.DeploymentInstance.Invoke<T>(string token, Pulumi.InvokeArgs args, Pulumi.InvokeOptions options = null) -> Pulumi.Output<T>
Pulumi.DeploymentInstance.Invoke<T>(string token, Pulumi.InvokeArgs args, Pulumi.InvokeOptions options = null) -> Pulumi.Output<T>
Pulumi.DeploymentInstance.InvokeSingle<T>(string token, Pulumi.InvokeArgs args, Pulumi.InvokeOptions options = null) -> Pulumi.Output<T>
13 changes: 13 additions & 0 deletions sdk/nodejs/runtime/invoke.ts
Expand Up @@ -74,6 +74,19 @@ export function invoke(tok: string, props: Inputs, opts: InvokeOptions = {}): Pr
return invokeAsync(tok, props, opts);
}

/*
* `invokeSingle` dynamically invokes the function, `tok`, which is offered by a provider plugin.
* Similar to `invoke`, but returns a single value instead of an object with a single key.
*/
export function invokeSingle(tok: string, props: Inputs, opts: InvokeOptions = {}): Promise<any> {
return invokeAsync(tok, props, opts).then(outputs => {
// assume outputs have a single key
const keys = Object.keys(outputs);
// return the first key's value from the outputs
return outputs[keys[0]];
});
}

export async function streamInvoke(
tok: string,
props: Inputs,
Expand Down

0 comments on commit 1290936

Please sign in to comment.