Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[codegen] Output-versioned function invokes #612

Merged
merged 24 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f452bb2
Output-versioned function invokes
Zaid-Ajaj May 23, 2022
c3fd60c
simplify propertiesAsOutputs and generate all SDKs
Zaid-Ajaj May 24, 2022
0eb812b
format gen.go
Zaid-Ajaj May 24, 2022
46945eb
changelog entry
Zaid-Ajaj May 24, 2022
63a9f19
Merge branch 'main' into output-versioned-function-invokes
Zaid-Ajaj May 24, 2022
3f07287
Merge branch 'main' into output-versioned-function-invokes
Zaid-Ajaj May 29, 2022
961afd9
test codegen changes
Zaid-Ajaj May 29, 2022
52ec6e1
attempt to fix examples: use output-versioned invokes
Zaid-Ajaj May 29, 2022
0c969f7
Second attempt to fix examples
Zaid-Ajaj May 29, 2022
f2cc1a4
programgen: function invokes no londer need Output.of(...)
Zaid-Ajaj May 29, 2022
c60c7a3
programgen: remove outputOf since no longer used
Zaid-Ajaj May 29, 2022
6bf7f77
Bring back function invokes with completable future, disable template…
Zaid-Ajaj May 30, 2022
9529b07
Update generated providers from test data
Zaid-Ajaj May 30, 2022
1fd899c
remove propertiesAsOutputs flag and enable output-funcs test for codegen
Zaid-Ajaj Jun 3, 2022
b8dc094
enable output-funcs-edgeorder test
Zaid-Ajaj Jun 3, 2022
16ebebb
Add programgen example output-funcs-aws.pp
Zaid-Ajaj Jun 3, 2022
05987c7
Merge branch 'main' into output-versioned-function-invokes
Zaid-Ajaj Jun 3, 2022
c4297be
Re-generate providers using latest codegen changes (fixes dirty provi…
Zaid-Ajaj Jun 3, 2022
3a6d868
Merge branch 'main' into output-versioned-function-invokes
Zaid-Ajaj Jun 6, 2022
2489642
fix aws-java-webserver example
Zaid-Ajaj Jun 7, 2022
281f69a
fix import in aws-java-webserver
Zaid-Ajaj Jun 7, 2022
cbf236a
Revert disabling template tests
t0yv0 Jun 7, 2022
1984b65
Use tracking branch pulumi-java/main for templates refs
t0yv0 Jun 7, 2022
9698590
Clean up CHANGELOG_PENDING
t0yv0 Jun 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
19 changes: 9 additions & 10 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
### Improvements

- Fix #547: Implement fully qualified imports for generated programs from PCL
[#596](https://github.com/pulumi/pulumi-java/pull/596)
- [codegen] **Breaking**
[#163](https://github.com/pulumi/pulumi-java/issues/163): function
invokes now accept `Output<T>` in their arguments and return
`Output<T>` instead of `CompletableFuture<T>`
[#612](https://github.com/pulumi/pulumi-java/pull/612).

- Fix #419: Remove SDK dependency on Mockito

- Support for using [jbang](https://jbang.dev)

- Stack resource is now considered internal and cannot be directly instantiated by the user.
The TestResult returned in tests is now an interface and no longer exposes fields.
- [sdk] `Stack` resource is now considered internal and cannot be
directly instantiated by the user. Instead of inheriting from
`Stack`, please use the `Pulumi.run` form to define resources in
your stack as shown in the README.

### Bug Fixes

Fix #627 - sanitize codegen sdk version input in pulumi-java-gen
72 changes: 66 additions & 6 deletions pkg/codegen/java/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -1514,14 +1514,25 @@ func (mod *modContext) genFunctions(ctx *classFileContext, addClass addClassMeth
resultClass := tokenToFunctionResultClassName(mod, fun.Token)
resultFQN := outputsPkg.Dot(resultClass)
inputsPkg := javaPkg.Dot(names.Ident("inputs"))

// Generating "{Function}Args" class for invokes that return Output<T>
// builders for this class allow outputs as inputs for their properties
argsClass := names.Ident(tokenToName(fun.Token) + "Args")
argsFQN := inputsPkg.Dot(argsClass)

// Generating "{Function}PlainArgs" class for invokes that return CompletableFuture<T>
// builders for this class only allow non-outputs as inputs for their properties
plainArgsClass := names.Ident(tokenToName(fun.Token) + "PlainArgs")
plainArgsFQN := inputsPkg.Dot(plainArgsClass)

var argsType string
var plainArgsType string
if fun.Inputs == nil {
argsType = ctx.ref(names.InvokeArgs)
plainArgsType = ctx.ref(names.InvokeArgs)
} else {
argsType = ctx.ref(argsFQN)
plainArgsType = ctx.ref(plainArgsFQN)
}

var returnType string
Expand All @@ -1531,44 +1542,93 @@ func (mod *modContext) genFunctions(ctx *classFileContext, addClass addClassMeth
returnType = ctx.imports.Ref(names.Void)
}

// default method name returns Output<ReturnType>
methodName := tokenToFunctionName(fun.Token)

// another "plain" overload will return CompletableFuture<ReturnType>
plainMethodName := methodName + "Plain"

// Emit datasource inputs method
invokeOptions := ctx.ref(names.InvokeOptions)

if hasAllOptionalInputs(fun) {
// Generate invoke that return Output<T>
printCommentFunction(ctx, fun, indent)
// Add no args invoke
// Add no args invoke (returns Output<T>)
fprintf(w, " public static %s<%s> %s() {\n",
ctx.ref(names.CompletableFuture), returnType, methodName)
ctx.ref(names.Output), returnType, methodName)
fprintf(w,
" return %s(%s.Empty, %s.Empty);\n",
methodName, argsType, invokeOptions)
fprintf(w, " }\n")

// Generate invoke that return CompletableFuture<T>
printCommentFunction(ctx, fun, indent)
fprintf(w, " public static %s<%s> %s() {\n",
ctx.ref(names.CompletableFuture), returnType, plainMethodName)
fprintf(w,
" return %s(%s.Empty, %s.Empty);\n",
plainMethodName, plainArgsType, invokeOptions)
fprintf(w, " }\n")
}

// Add args only invoke
// Output version: add args only invoke
printCommentFunction(ctx, fun, indent)
fprintf(w, " public static %s<%s> %s(%s args) {\n",
ctx.ref(names.CompletableFuture), returnType, methodName, argsType)
ctx.ref(names.Output), returnType, methodName, argsType)
fprintf(w,
" return %s(args, %s.Empty);\n",
methodName, invokeOptions)
fprintf(w, " }\n")

// Add full invoke
// CompletableFuture version: add args only invoke
printCommentFunction(ctx, fun, indent)
fprintf(w, " public static %s<%s> %s(%s args) {\n",
ctx.ref(names.CompletableFuture), returnType, plainMethodName, plainArgsType)
fprintf(w,
" return %s(args, %s.Empty);\n",
plainMethodName, invokeOptions)
fprintf(w, " }\n")

// Output version: add full invoke
printCommentFunction(ctx, fun, indent)
fprintf(w, " public static %s<%s> %s(%s args, %s options) {\n",
ctx.ref(names.CompletableFuture), returnType, methodName, argsType, invokeOptions)
ctx.ref(names.Output), returnType, methodName, argsType, invokeOptions)
fprintf(w,
" return %s.getInstance().invoke(\"%s\", %s.of(%s.class), args, %s.withVersion(options));\n",
ctx.ref(names.Deployment), fun.Token, ctx.ref(names.TypeShape), returnType, mod.utilitiesRef(ctx))
fprintf(w, " }\n")

// CompletableFuture version: add full invoke
// notice how the implementation now uses `invokeAsync` instead of `invoke`
printCommentFunction(ctx, fun, indent)
fprintf(w, " public static %s<%s> %s(%s args, %s options) {\n",
ctx.ref(names.CompletableFuture), returnType, plainMethodName, plainArgsType, invokeOptions)
fprintf(w,
" return %s.getInstance().invokeAsync(\"%s\", %s.of(%s.class), args, %s.withVersion(options));\n",
ctx.ref(names.Deployment), fun.Token, ctx.ref(names.TypeShape), returnType, mod.utilitiesRef(ctx))
fprintf(w, " }\n")

// Emit the args and result types, if any.
if fun.Inputs != nil {
// Generate "{Function}Args" class for invokes that return Output<T>
// Notice here using fun.Inputs.InputShape.Properties which use the shape which accepting Outputs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

There's a bit of misnomer here. In other languages there is an Input<T> type, canonically (in TypeScript) it's an untagged union T | Output<T>. In Java we made the controversial decision to not model that type at all, so we only have Output<T> - but the cross-language Go framework is still calling that InputShape - which is fine. at the meta-level InputShape will inject InputType wrappers, that will be translated to Output<T> during the Java specific codegen pass.

if err := addClass(inputsPkg, argsClass, func(ctx *classFileContext) error {
args := &plainType{
mod: mod,
name: ctx.className.String(),
baseClass: "com.pulumi.resources.InvokeArgs",
propertyTypeQualifier: inputsQualifier,
properties: fun.Inputs.InputShape.Properties,
}
return args.genInputType(ctx)
}); err != nil {
return err
}

// Generate "{Function}PlainArgs" class for invokes that return CompletableFuture<T>
// Notice here using fun.Inputs.Properties which use the plain shape (not accepting Outputs)
if err := addClass(inputsPkg, plainArgsClass, func(ctx *classFileContext) error {
args := &plainType{
mod: mod,
name: ctx.className.String(),
Expand Down
5 changes: 1 addition & 4 deletions pkg/codegen/java/gen_program.go
Original file line number Diff line number Diff line change
Expand Up @@ -869,12 +869,9 @@ func (g *generator) genLocalVariable(w io.Writer, localVariable *pcl.LocalVariab
g.genIndent(w)
if isInvokeCall {
g.functionInvokes[variableName] = functionSchema
// convert CompletableFuture<T> to Output<T> using Output.of
// TODO: call the Output<T>-version of invokes when the SDK allows it.
functionDefinition := outputOf(localVariable.Definition.Value)
// TODO: lowerExpression isn't what we expect: function call should extract outputs into .apply(...) calls
//functionDefinitionWithApplies := g.lowerExpression(functionDefinition, localVariable.Definition.Value.Type())
g.Fgenf(w, "final var %s = %v;\n", variableName, functionDefinition)
g.Fgenf(w, "final var %s = %v;\n", variableName, localVariable.Definition.Value)
} else {
variable := localVariable.Definition.Value
g.Fgenf(w, "final var %s = %v;\n", variableName, g.lowerExpression(variable, variable.Type()))
Expand Down
20 changes: 0 additions & 20 deletions pkg/codegen/java/gen_program_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,26 +203,6 @@ func (g *generator) functionName(tokenArg model.Expression) (string, string) {
return names.Title(module) + "Functions" + "." + member, names.Title(member)
}

// outputOf creates a new call to the output intrinsic.
func outputOf(promise model.Expression) model.Expression {
promiseType, ok := promise.Type().(*model.PromiseType)
if !ok {
return promise
}

return &model.FunctionCallExpression{
Name: intrinsicOutput,
Signature: model.StaticFunctionSignature{
Parameters: []model.Parameter{{
Name: "promise",
Type: promiseType,
}},
ReturnType: model.NewOutputType(promiseType.ElementType),
},
Args: []model.Expression{promise},
}
}

func (g *generator) GenFunctionCallExpression(w io.Writer, expr *model.FunctionCallExpression) {
switch expr.Name {
case pcl.IntrinsicConvert:
Expand Down
6 changes: 4 additions & 2 deletions pkg/codegen/java/gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ var javaSpecificTests = []*test.SDKTest{
Directory: "mini-awsx",
Description: "Regression tests extracted from trying to codegen awsx",
},
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, this one is a bit surprising to me. My mental model was that we borrow tests from the common test suite and then add Java-specific regression tests here. Like mini-awsnative is Java specific test. But it would seem that output-funcs-edgeorder should be coming from pulumi/pulumi? It's not there?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is I was expecting it to be in test.PulumiPulumiSDKTests?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take care of sorting this out later, not blocking.

Directory: "output-funcs-edgeorder",
Description: "Testing EdgeOrder functions which return Output<T>",
},
}

func adaptTest(t *test.SDKTest) *test.SDKTest {
Expand All @@ -54,8 +58,6 @@ func adaptTest(t *test.SDKTest) *test.SDKTest {
t.Skip = codegen.NewStringSet("java/any") // TODO
case "plain-object-defaults":
t.Skip = codegen.NewStringSet("java/any") // TODO
case "output-funcs":
t.Skip = codegen.NewStringSet("java/any") // TODO
case "regress-8403":
t.Skip = codegen.NewStringSet("java/any") // TODO
case "plain-object-disable-defaults":
Expand Down
7 changes: 7 additions & 0 deletions pkg/codegen/java/typeshape.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ func (ts TypeShape) Optional() TypeShape {
}
}

func (ts TypeShape) Output() TypeShape {
return TypeShape{
Type: names.Output,
Parameters: []TypeShape{ts},
}
}

func (ts TypeShape) UnOptional() (bool, TypeShape) {
if ts.Type.Equal(names.Optional) {
return true, ts.Parameters[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public static void stack(Context ctx) {
.tags(Map.of("Name", "pulumi-vpc-rt"))
.build());

final var zones = Output.of(AwsFunctions.getAvailabilityZones());
final var zones = AwsFunctions.getAvailabilityZones();

final var vpcSubnet = zones.applyValue(getAvailabilityZonesResult -> {
final var resources = new ArrayList<Subnet>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ public static void main(String[] args) {
}

public static void stack(Context ctx) {
final var vpc = Output.of(Ec2Functions.getVpc(GetVpcArgs.builder()
final var vpc = Ec2Functions.getVpc(GetVpcArgs.builder()
.default_(true)
.build()));
.build());

final var subnets = Output.of(Ec2Functions.getSubnetIds(GetSubnetIdsArgs.builder()
final var subnets = Ec2Functions.getSubnetIds(GetSubnetIdsArgs.builder()
.vpcId(vpc.applyValue(getVpcResult -> getVpcResult.id()))
.build()));
.build());

var webSecurityGroup = new SecurityGroup("webSecurityGroup", SecurityGroupArgs.builder()
.vpcId(vpc.applyValue(getVpcResult -> getVpcResult.id()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ public static void stack(Context ctx) {
.build())
.build());

final var ami = Output.of(AwsFunctions.getAmi(GetAmiArgs.builder()
final var ami = AwsFunctions.getAmi(GetAmiArgs.builder()
.filters(GetAmiFilterArgs.builder()
.name("name")
.values("amzn-ami-hvm-*-x86_64-ebs")
.build())
.owners("137112412989")
.mostRecent(true)
.build()));
.build());

var server = new Instance("server", InstanceArgs.builder()
.tags(Map.of("Name", "web-server-www"))
Expand Down
4 changes: 2 additions & 2 deletions pkg/codegen/testing/test/testdata/azure-sa-pp/MyStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public static void stack(Context ctx) {
final var config = ctx.config();
final var storageAccountNameParam = config.get("storageAccountNameParam");
final var resourceGroupNameParam = config.get("resourceGroupNameParam");
final var resourceGroupVar = Output.of(CoreFunctions.getResourceGroup(GetResourceGroupArgs.builder()
final var resourceGroupVar = CoreFunctions.getResourceGroup(GetResourceGroupArgs.builder()
.name(resourceGroupNameParam)
.build()));
.build());

final var locationParam = config.get("locationParam").orElse(resourceGroupVar.applyValue(getResourceGroupResult -> getResourceGroupResult.location()));
final var storageAccountTierParam = config.get("storageAccountTierParam").orElse("Standard");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"src/main/java/com/pulumi/aws/config_endpoints/inputs/Endpoints.java",
"src/main/java/com/pulumi/aws/ec2_getAmi/Ec2_getAmiFunctions.java",
"src/main/java/com/pulumi/aws/ec2_getAmi/inputs/GetAmiArgs.java",
"src/main/java/com/pulumi/aws/ec2_getAmi/inputs/GetAmiPlainArgs.java",
"src/main/java/com/pulumi/aws/ec2_getAmi/outputs/GetAmiResult.java",
"src/main/java/com/pulumi/aws/iam_instanceProfile/InstanceProfile.java",
"src/main/java/com/pulumi/aws/iam_instanceProfile/InstanceProfileArgs.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,25 @@

import com.pulumi.aws.Utilities;
import com.pulumi.aws.ec2_getAmi.inputs.GetAmiArgs;
import com.pulumi.aws.ec2_getAmi.inputs.GetAmiPlainArgs;
import com.pulumi.aws.ec2_getAmi.outputs.GetAmiResult;
import com.pulumi.core.Output;
import com.pulumi.core.TypeShape;
import com.pulumi.deployment.Deployment;
import com.pulumi.deployment.InvokeOptions;
import java.util.concurrent.CompletableFuture;

public final class Ec2_getAmiFunctions {
public static CompletableFuture<GetAmiResult> getAmi(GetAmiArgs args) {
public static Output<GetAmiResult> getAmi(GetAmiArgs args) {
return getAmi(args, InvokeOptions.Empty);
}
public static CompletableFuture<GetAmiResult> getAmi(GetAmiArgs args, InvokeOptions options) {
public static CompletableFuture<GetAmiResult> getAmiPlain(GetAmiPlainArgs args) {
return getAmiPlain(args, InvokeOptions.Empty);
}
public static Output<GetAmiResult> getAmi(GetAmiArgs args, InvokeOptions options) {
return Deployment.getInstance().invoke("aws:ec2/getAmi:getAmi", TypeShape.of(GetAmiResult.class), args, Utilities.withVersion(options));
}
public static CompletableFuture<GetAmiResult> getAmiPlain(GetAmiPlainArgs args, InvokeOptions options) {
return Deployment.getInstance().invokeAsync("aws:ec2/getAmi:getAmi", TypeShape.of(GetAmiResult.class), args, Utilities.withVersion(options));
}
}
Loading