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

fix: support source and sink func declarations #1070

Merged
merged 7 commits into from
Mar 14, 2024

Conversation

wesbillman
Copy link
Member

@wesbillman wesbillman commented Mar 12, 2024

Fixes #1065

SOLVED: Some things to figure out still:

These were solved by assuming anything without a req is a source, anything without a resp, is a sink, and anything without either is empty.

  1. How to use Handle with these new definitions. Since the signature no longer matches the Handle func, what should we do here? The generated code here will fail:

    func main() {
      verbConstructor := server.NewUserVerbServer("time",
        server.Handle(time.Time),
        server.Handle(time.Sink), // these both have build errors since they don't match the expected signature
        server.Handle(time.Source),
      )
      plugin.Start(context.Background(), "time", verbConstructor, ftlv1connect.VerbServiceName, ftlv1connect.NewVerbServiceHandler)
    }
  2. How should we deal with the fact that the generated code doesn't match the original signature? This also leads to issues using things like ftl.callSink and ftl.callSource since the generated code doesn't match the signature.

    // original
    
    //ftl:verb
    func Source(context.Context) (SourceResp, error) {
    	return SourceResp{}, nil
    }
    
    // generated
    
    //ftl:verb
    func Source(context.Context, ftl.Unit) (SourceResp, error) {
      panic("Verb stubs should not be called directly, instead use github.com/TBD54566975/ftl/runtime-go/ftl.Call()")
    }

@alecthomas alecthomas mentioned this pull request Mar 12, 2024
Copy link
Collaborator

@alecthomas alecthomas left a comment

Choose a reason for hiding this comment

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

Nice! Good start, but you're right, the other two points need to be addressed. LMK what you come up with.

@wesbillman wesbillman force-pushed the support-source-and-sink-func-declarations branch 2 times, most recently from 6ebb5d6 to 7945e84 Compare March 13, 2024 20:41
@wesbillman wesbillman marked this pull request as ready for review March 13, 2024 20:42
Comment on lines 89 to 78
// SourceToRef returns the FTL reference for a Source.
func SourceToRef[Resp any](source Source[Resp]) SourceRef {
ref := runtime.FuncForPC(reflect.ValueOf(source).Pointer()).Name()
return SourceRef(goRefToFTLRef(ref))
}

// EmptyToRef returns the FTL reference for an Empty.
func EmptyToRef(empty Empty) VerbRef {
ref := runtime.FuncForPC(reflect.ValueOf(empty).Pointer()).Name()
return goRefToFTLRef(ref)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This stuff might change once we use a generic Ref everywhere. But this is working for now

Copy link
Collaborator

Choose a reason for hiding this comment

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

@worstell says she has a change for this in flight

Copy link
Member Author

Choose a reason for hiding this comment

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

Awesome I can work with her to get her change and mine merged and updated here.

return ftl.Unit{}, err
}

return handler(ftl.VerbRef{Module: ref.Module, Name: ref.Name}, verb)
Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure if this is the best approach here, but it works as intended.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Perfectly acceptable I think.

@wesbillman wesbillman force-pushed the support-source-and-sink-func-declarations branch from c36342b to 24887ee Compare March 13, 2024 20:46
Comment on lines +217 to +213
require(verb.valueParameters.size >= 1) { "$verbSourcePos Verbs must have at least one argument, ${verb.name} did not" }
require(verb.valueParameters.size <= 2) { "$verbSourcePos Verbs must have at most two arguments, ${verb.name} did not" }
Copy link
Member Author

Choose a reason for hiding this comment

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

@worstell would be good to get your 👀 on some of this kotlin stuff if you have a moment.

Copy link
Contributor

Choose a reason for hiding this comment

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

lgtm!

Copy link
Collaborator

@alecthomas alecthomas left a comment

Choose a reason for hiding this comment

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

LGTM but it would be good to add an integration test for these two cases.

@@ -201,6 +211,9 @@ var scaffoldFuncs = scaffolder.FuncMap{
}
panic(fmt.Sprintf("unsupported value %T", v))
},
"isSink": func(v schema.Verb) string {
return "wes"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wut

Copy link
Member Author

Choose a reason for hiding this comment

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

LOLOL

return ftl.Unit{}, err
}

return handler(ftl.VerbRef{Module: ref.Module, Name: ref.Name}, verb)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perfectly acceptable I think.

@@ -34,8 +34,19 @@ data class {{.Name|title}}
{{- else if is "Verb" . }}
{{.Comments|comment -}}@Verb
@Ignore
{{- if and (eq (type $ .Request) "Unit") (eq (type $ .Response) "Unit")}}
fun {{.Name|lowerCamel}}(context: Context): Unit = throw
Copy link
Collaborator

Choose a reason for hiding this comment

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

You could probably pull fun {{.Name|lowerCamel}}(context: Context out from the if/else bodies if you wanted to DRY this a bit more.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll noodle on this one a bit. It makes the template quite a bit tougher for me to read, but maybe I'm just weird :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

I was wondering that too. Just leave it as-is if it's too gross.

@wesbillman wesbillman force-pushed the support-source-and-sink-func-declarations branch from 6e12590 to 5101fb4 Compare March 13, 2024 23:05
Comment on lines +216 to +221
func TestPubSub(t *testing.T) {
runForRuntimes(t, func(modulesDir string, runtime string, rd runtimeData) []test {
return []test{
{name: fmt.Sprintf("PubSub%s", rd.testSuffix), assertions: assertions{
run("ftl", rd.initOpts...),
scaffoldTestData(runtime, "pubsub", rd.modulePath),
Copy link
Member Author

Choose a reason for hiding this comment

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

I added these under pubsub assuming they can be extended to support pubsub once it's ready for integration tests.

if (verb.valueParameters.size == 2) {
val reqParam = verb.valueParameters.last()
require(reqParam.typeReference?.resolveType()
?.let { it.toClassDescriptor().isData || it.isEmptyBuiltin() || it.isUnit() }
Copy link
Contributor

Choose a reason for hiding this comment

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

do we want to allow kotlin.Unit as a request type? i thought we were going to infer this from omission with the new change

Copy link
Member Author

Choose a reason for hiding this comment

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

Ahh good catch! I had this in before I added the size check above and forgot to remove! Thanks for pointing this one out!

@wesbillman wesbillman force-pushed the support-source-and-sink-func-declarations branch from 8220825 to f2778f6 Compare March 13, 2024 23:09
respClass.fqNameOrNull()?.asString()
}"
verb.createTypeBindingForReturnType(bindingContext)?.type?.let {
require(it.toClassDescriptor().isData || it.isEmptyBuiltin() || it.isUnit()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

same as above, do we want to look for a null return type instead of a kotlin.Unit return type?

Copy link
Member Author

Choose a reason for hiding this comment

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

On this one, I think kotlin infers the return type of Unit if it's omitted.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah got it

@wesbillman wesbillman force-pushed the support-source-and-sink-func-declarations branch from f2778f6 to 65356c4 Compare March 13, 2024 23:23
Comment on lines 250 to 258
var requestRef = Type(unit = xyz.block.ftl.v1.schema.Unit())
if (verb.valueParameters.size > 1) {
val ref = verb.valueParameters.last()?.let {
val position = it.getLineAndColumn().toPosition()
return@let it.typeReference?.resolveType()?.toSchemaType(position)
}
ref?.let { requestRef = it }

}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
var requestRef = Type(unit = xyz.block.ftl.v1.schema.Unit())
if (verb.valueParameters.size > 1) {
val ref = verb.valueParameters.last()?.let {
val position = it.getLineAndColumn().toPosition()
return@let it.typeReference?.resolveType()?.toSchemaType(position)
}
ref?.let { requestRef = it }
}
val requestRef = verb.valueParameters.takeIf { it.size > 1 }?.last()?.let {
val position = it.getLineAndColumn().toPosition()
return@let it.typeReference?.resolveType()?.toSchemaType(position)
} ?: Type(unit = xyz.block.ftl.v1.schema.Unit())

Comment on lines 260 to 264
var returnRef = Type(unit = xyz.block.ftl.v1.schema.Unit())
val ref = verb.createTypeBindingForReturnType(bindingContext)?.let {
val position = it.psiElement.getLineAndColumn().toPosition()
return@let it.type.toSchemaType(position)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
var returnRef = Type(unit = xyz.block.ftl.v1.schema.Unit())
val ref = verb.createTypeBindingForReturnType(bindingContext)?.let {
val position = it.psiElement.getLineAndColumn().toPosition()
return@let it.type.toSchemaType(position)
}
val returnRef = verb.createTypeBindingForReturnType(bindingContext)?.let {
val position = it.psiElement.getLineAndColumn().toPosition()
return@let it.type.toSchemaType(position)
} ?: Type(unit = xyz.block.ftl.v1.schema.Unit())

@wesbillman wesbillman force-pushed the support-source-and-sink-func-declarations branch from 65356c4 to 3824b82 Compare March 14, 2024 16:34
@wesbillman wesbillman force-pushed the support-source-and-sink-func-declarations branch from 3824b82 to 632022e Compare March 14, 2024 16:51
@wesbillman wesbillman merged commit bb02db1 into main Mar 14, 2024
11 checks passed
@wesbillman wesbillman deleted the support-source-and-sink-func-declarations branch March 14, 2024 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for source and sink function declarations
3 participants