#FunctionArg
1 messages ยท Page 1 of 1 (latest)
it's definitely related
You're saying that you need to make it idable as well?
TypeDef is IDable right? I think that's the problem, when you get a list of IDable objects and then try to use them, you get a panic
IIRC
basically the TypeDef field isn't marked as one that can be pre-fetched (wherever that happens) - so when you call it, it's trying to run a query, but that object doesn't even have the GraphQL client on it
I did funcArg.ID() and got no error from Vim, only on :e ๐
yeah TypeDef is fine, but the problem is this is a list of []FunctionArg, so to be able to even get to the TypeDef we'll need FunctionArg to be ID-able
it'll be blowing up on the bottom line at the moment
i think the general fix could be that for all ID-able objects, we pre-populate a r.q that is basically { functionArg(id: "...") { ... } }
something like that anyway
Proposing a new convention for how ID-able objects are constructed, especially types like FunctionArg that have required fields:
scalar FunctionArgID
type FunctionArg {
id: FunctionArgID!
name: String!
typeDef: TypeDef!
}
# goal: ability to construct a FunctionArg from ID OR optional/required args,
# such that a FunctionArg is never "partially instantiated"
# solution: two constructors, one by required ID, another with required args (if any)
type Query {
getFunctionArg(id: FunctionArgID!): FunctionArg!
functionArg(name: String!, typeDef: TypeDefID!): FunctionArg!
}
### rationale below
type Query {
functionArg(id: FunctionArgID!): FunctionArg!
functionArgWith(name: String!, typeDef: TypeDefID!): FunctionArg!
}
# problem: what if it has no required args? functionArgWith() is weird
# solution: newFunctionArg
type Query {
functionArg(id: FunctionArgID!): FunctionArg!
newFunctionArg(name: String!, typeDef: TypeDefID!): FunctionArg!
}
# problem: "new" feels weird. we want it to feel like a query, not like state.
# plus if you call the same query multiple times you get objects with the same ID,
# which is not how "new" works anywhere else.
# solution: functionArg constructs from values, functionArgFromID constructs from ID
type Query {
functionArgFromID(id: FunctionArgID!): FunctionArg!
functionArg(name: String!, typeDef: TypeDefID!): FunctionArg!
}
# problem: fights with your tab completion
# solution: functionArgFromID -> getFunctionArg
type Query {
getFunctionArg(id: FunctionArgID!): FunctionArg!
functionArg(name: String!, typeDef: TypeDefID!): FunctionArg!
}
# this feels OK? optimizes for the regular constructor, since getFoo tends to be used internally.
#
# getFoo has precedence from https://dgraph.io/docs/graphql/schema/types/#the-id-type
#
# this could/should be added for all existing ID-able objects (Container etc)
cc @dusk scaffold @sullen wyvern
I basically want to find a consistent pattern we can use to make any module object ID-able, and eventually adjust our own API to match. So far we've skirted around this issue by either having a valid 'zero value' for the major types (Directory = empty directory, Container = scratch container) or making it so you can only construct by FooID! or through other APIs (File). When the 'zero value' is valid you can just add an optional id param to the constructor, but it doesn't seem right to force every object to support a partial/empty state.
So this approach implies two constructors for every object: getFoo(id: FooID!): Foo! (ID always required), and foo(...): Foo! (which takes whatever required/optional args you want). The getFoo field would be more of a plumbing method available to SDKs or folks doing fun meta-things (Daggerverse?). For this thread, it would solve the issue by having a predictable way of going from an ID returned in a list of objects to a query selecting from that single object.
welcome - I only went with "getFoo" because dgraph (which I've never used) went with it. Could also consider "loadFoo" or "_foo" or something.
I would probably rank your suggestion at the very end as my favorite, though I'd advocate going even more verbose/explicit with it:
type Query {
loadFunctionArgFromID(id: FunctionArgID!): FunctionArg!
functionArg(name: String!, typeDef: TypeDefID!): FunctionArg!
}
load*FromID is just very specific to what it's doing relative to get*, which is so generic that it in theory could make sense for either method. If I was autocompleting in my IDE, typed functionArg and saw getFunctionArg and functionArg available as the two closest matches, I'd have to look at the signatures+docs to know which one I should use.
Whereas if my IDE showed me loadFunctionArgFromID and functionArg, my brain would probably rule out loadFunctionArgFromID more quickly.
This is very much splitting already split hairs though ๐ Realistically get* is totally fine too.
Makes sense! I agree, we don't want it looking super prominent, and that fixes the tab-completion issue too. I like the 'FromXXX' pattern in general too, it felt like a shame to drop it
(IMO Container.import would be better as Container.fromOCITarball or something, for example)
Any progress? ๐ Is there any way to get the type of a function arg in the mean time? I need it to support Directory and Secret arguments in the CLI, however not for today.
Working on it! I think I've landed on a decent pattern for ID-able things with a bit less boilerplate now. As a work-around I suppose you could drop down to raw GraphQL?
I ended up doing the same thing here, for similar reasons: https://github.com/dagger/dagger/blob/703d227ce400ec431a033268b164d212dfe85b14/sdk/go/cmd/bootstrap/main.go#L203-L212 (this one uses the module's stitched schema directly, but you could also query functions/args/etc in your case)
I'd need to dynamically build a query string which is what the querybuilder already does but it's internal. Another option is the ast way like erik's done before.
Your raw query is easier if you have a known set of arguments.
It's going to be needed for the chaining in a next iteration anyway.
make querybuilder public or ast? ๐
ast doesn't look too bad, but I remember @dusk scaffold complaining about it.
It's one of those things that's a one-time headache, but once it's written out it's fine
I was probably just whining ๐
lol - FWIW I'm OK with just making querybuilder public; we already expose (*Client).Do, we might as well expose the thing that makes it easier to use, and it's not like it's a moving target with regard to API stability
Agree, no need for it to be secret/internal. If that makes it easier we should do it
some day we'll probably reinvent our whole GraphQL stack, and at that point it can just become part of that package ๐
tbh back in the cloak days when we were looking around that's what we saw quite a few projects doing; Wundergraph has their own thing IIRC
At least for Go
Yeah there's a bit of a sprawl. Wonder if anyone has something worth extracting/collaborating on instead...
(another topic for another day)
Yeah I'm gonna dodge this nerd-discussion snipe ๐
So I guess it would be dagger.io/dagger/querybuilder?
What's the best way to get the graphql.Client instance from dagger.Client? Ok to expose a method for it or something else?
We already expose Do, which is just a light wrapper around the only method on the graphql.Client interface (MakeRequest): https://github.com/sipsma/dagger/blob/efeb21ffdf0ed9349dd79bf387632a217e436c06/sdk/go/client.go#L109-L109
Do you need to be able to call MakeRequest directly? Or does Do work?
The querybuilder's Execute calls on internal functions for s.build() and s.unpack().
Execute is doing what Do does, but with those internal functions.
Oh yeah I guess exposing it through a method on dagger.Client that returns the graphql.Client would be fine I think?